mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-20 01:21:29 -07:00
98 lines
3.4 KiB
C#
98 lines
3.4 KiB
C#
namespace Bloxstrap.Utility
|
|
{
|
|
// https://gist.github.com/dfederm/35c729f6218834b764fa04c219181e4e
|
|
|
|
public sealed class AsyncMutex : IAsyncDisposable
|
|
{
|
|
private readonly bool _initiallyOwned;
|
|
private readonly string _name;
|
|
private Task? _mutexTask;
|
|
private ManualResetEventSlim? _releaseEvent;
|
|
private CancellationTokenSource? _cancellationTokenSource;
|
|
|
|
public AsyncMutex(bool initiallyOwned, string name)
|
|
{
|
|
_initiallyOwned = initiallyOwned;
|
|
_name = name;
|
|
}
|
|
|
|
public Task AcquireAsync(CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
TaskCompletionSource taskCompletionSource = new();
|
|
|
|
_releaseEvent = new ManualResetEventSlim();
|
|
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
|
|
// Putting all mutex manipulation in its own task as it doesn't work in async contexts
|
|
// Note: this task should not throw.
|
|
_mutexTask = Task.Factory.StartNew(
|
|
state =>
|
|
{
|
|
try
|
|
{
|
|
CancellationToken cancellationToken = _cancellationTokenSource.Token;
|
|
using var mutex = new Mutex(_initiallyOwned, _name);
|
|
try
|
|
{
|
|
// Wait for either the mutex to be acquired, or cancellation
|
|
if (WaitHandle.WaitAny(new[] { mutex, cancellationToken.WaitHandle }) != 0)
|
|
{
|
|
taskCompletionSource.SetCanceled(cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
catch (AbandonedMutexException)
|
|
{
|
|
// Abandoned by another process, we acquired it.
|
|
}
|
|
|
|
taskCompletionSource.SetResult();
|
|
|
|
// Wait until the release call
|
|
_releaseEvent.Wait();
|
|
|
|
mutex.ReleaseMutex();
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
taskCompletionSource.TrySetCanceled(cancellationToken);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
taskCompletionSource.TrySetException(ex);
|
|
}
|
|
},
|
|
state: null,
|
|
cancellationToken,
|
|
TaskCreationOptions.LongRunning,
|
|
TaskScheduler.Default);
|
|
|
|
return taskCompletionSource.Task;
|
|
}
|
|
|
|
public async Task ReleaseAsync()
|
|
{
|
|
_releaseEvent?.Set();
|
|
|
|
if (_mutexTask != null)
|
|
{
|
|
await _mutexTask;
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
// Ensure the mutex task stops waiting for any acquire
|
|
_cancellationTokenSource?.Cancel();
|
|
|
|
// Ensure the mutex is released
|
|
await ReleaseAsync();
|
|
|
|
_releaseEvent?.Dispose();
|
|
_cancellationTokenSource?.Dispose();
|
|
}
|
|
}
|
|
}
|