SafeDispatch/SafeMobileLIB_DLL/WebsocketClient/Threading/WebsocketAsyncLock.cs

75 lines
2.2 KiB
C#
Raw Permalink Normal View History

2024-02-22 16:43:59 +00:00
using System;
using System.Threading;
using System.Threading.Tasks;
namespace SafeMobileLib.WebsocketClient.Threading
{
/// <summary>
/// Class that wraps SemaphoreSlim and enables to use locking inside 'using' blocks easily
/// Don't need to bother with releasing and handling SemaphoreSlim correctly
/// Example:
/// <code>
/// using(await _asyncLock.LockAsync())
/// {
/// // do your synchronized work
/// }
/// </code>
/// </summary>
public class WebsocketAsyncLock
{
private readonly Task<IDisposable> _releaserTask;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private readonly IDisposable _releaser;
/// <summary>
/// Class that wraps SemaphoreSlim and enables to use locking inside 'using' blocks easily
/// Don't need to bother with releasing and handling SemaphoreSlim correctly
/// </summary>
public WebsocketAsyncLock()
{
_releaser = new Releaser(_semaphore);
_releaserTask = Task.FromResult(_releaser);
}
/// <summary>
/// Use inside 'using' block
/// </summary>
public IDisposable Lock()
{
_semaphore.Wait();
return _releaser;
}
/// <summary>
/// Use inside 'using' block with await
/// </summary>
public Task<IDisposable> LockAsync()
{
var waitTask = _semaphore.WaitAsync();
return waitTask.IsCompleted
? _releaserTask
: waitTask.ContinueWith(
(_, releaser) => (IDisposable)releaser,
_releaser,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
private class Releaser : IDisposable
{
private readonly SemaphoreSlim _semaphore;
public Releaser(SemaphoreSlim semaphore)
{
_semaphore = semaphore;
}
public void Dispose()
{
_semaphore.Release();
}
}
}
}