using System; using System.Threading; using System.Threading.Tasks; namespace MQTTnet.Internal { // From Stephen Toub (https://blogs.msdn.microsoft.com/pfxteam/2012/02/12/building-async-coordination-primitives-part-6-asynclock/) public class AsyncLock : IDisposable { private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private readonly Task _releaser; public AsyncLock() { _releaser = Task.FromResult((IDisposable)new Releaser(this)); } public Task WaitAsync() { return WaitAsync(CancellationToken.None); } public Task WaitAsync(CancellationToken cancellationToken) { var task = _semaphore.WaitAsync(cancellationToken); if (task.Status == TaskStatus.RanToCompletion) { return _releaser; } return task.ContinueWith( (_, state) => (IDisposable)state, _releaser.Result, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } public void Dispose() { _semaphore?.Dispose(); } private class Releaser : IDisposable { private readonly AsyncLock _toRelease; internal Releaser(AsyncLock toRelease) { _toRelease = toRelease; } public void Dispose() { _toRelease._semaphore.Release(); } } } }