using System; using System.Threading; using System.Threading.Tasks; namespace SafeMobileLib.WebsocketClient.Threading { /// /// 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: /// /// using(await _asyncLock.LockAsync()) /// { /// // do your synchronized work /// } /// /// public class WebsocketAsyncLock { private readonly Task _releaserTask; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private readonly IDisposable _releaser; /// /// Class that wraps SemaphoreSlim and enables to use locking inside 'using' blocks easily /// Don't need to bother with releasing and handling SemaphoreSlim correctly /// public WebsocketAsyncLock() { _releaser = new Releaser(_semaphore); _releaserTask = Task.FromResult(_releaser); } /// /// Use inside 'using' block /// public IDisposable Lock() { _semaphore.Wait(); return _releaser; } /// /// Use inside 'using' block with await /// public Task 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(); } } } }