V8 / 34b774036cd1
AtomicFile.cs
Lilith/Core/Extensions/AtomicFile.cs
Unchanged from the preceding versionCS
namespace Lilith.Core.Extensions;
public static class AtomicFile
{
private static readonly object[] Gates = [.. Enumerable.Range(0, 64).Select(_ => new object())];
public static void Write(string path, Action<string> write)
{
path = Path.GetFullPath(path);
lock (Gates[(uint)StringComparer.OrdinalIgnoreCase.GetHashCode(path) % Gates.Length])
{
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
string temporary = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
try
{
write(temporary);
// Windows readers/other processes may briefly hold the destination without delete sharing.
for (int attempt = 0; ; attempt++)
{
try { File.Move(temporary, path, overwrite: true); break; }
catch (Exception ex) when (attempt < 10 && ex is IOException or UnauthorizedAccessException)
{ Thread.Sleep(20); }
}
}
finally { if (File.Exists(temporary)) File.Delete(temporary); }
}
}
}