← All source files

V5 / f52bca255516

AtomicFile.cs

Lilith/Core/Extensions/AtomicFile.cs

Unchanged from the preceding version
CS
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); }
        }
    }
}