59 lines
1.6 KiB
C#
59 lines
1.6 KiB
C#
namespace WxAgent.Core;
|
|
|
|
/// <summary>Serializes commands locally and across processes; the OS releases the file handle after a crash.</summary>
|
|
public sealed class InterprocessCommandGate : IDisposable
|
|
{
|
|
private readonly SemaphoreSlim _local = new(1, 1);
|
|
private readonly string _path;
|
|
private FileStream? _lease;
|
|
|
|
public InterprocessCommandGate(string path)
|
|
{
|
|
_path = Path.GetFullPath(path);
|
|
Directory.CreateDirectory(Path.GetDirectoryName(_path)!);
|
|
}
|
|
|
|
public async Task WaitAsync(CancellationToken cancellationToken)
|
|
{
|
|
await _local.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
try
|
|
{
|
|
_lease = new FileStream(_path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
|
|
return;
|
|
}
|
|
catch (IOException exception) when ((exception.HResult & 0xffff) is 11 or 32 or 33)
|
|
{
|
|
// EAGAIN on Unix; sharing/lock violation on Windows. Other I/O errors must surface.
|
|
await Task.Delay(50, cancellationToken).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
_local.Release();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public void Release()
|
|
{
|
|
try { _lease?.Dispose(); }
|
|
finally
|
|
{
|
|
_lease = null;
|
|
_local.Release();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_lease?.Dispose();
|
|
_local.Dispose();
|
|
}
|
|
}
|