edge-util/src/Microsoft.Azure.Devices.Edge.Util/concurrency/AsyncLock.cs (50 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. namespace Microsoft.Azure.Devices.Edge.Util.Concurrency { // Code ported from http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx using System; using System.Threading; using System.Threading.Tasks; public sealed class AsyncLock : IDisposable { readonly SemaphoreSlim semaphore; public AsyncLock() : this(1) { } public AsyncLock(int maximumConcurrency) { this.semaphore = new SemaphoreSlim(maximumConcurrency, maximumConcurrency); } public Task<IDisposable> LockAsync() => this.LockAsync(CancellationToken.None); public Task<IDisposable> LockAsync(CancellationToken token) { Task wait = this.semaphore.WaitAsync(token); return wait.Status == TaskStatus.RanToCompletion ? Task.FromResult<IDisposable>(new Releaser(this)) : wait.ContinueWith<IDisposable>( (_, state) => new Releaser((AsyncLock)state), this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); } /// <inheritdoc /> public void Dispose() => this.semaphore.Dispose(); private class Releaser : IDisposable { readonly AsyncLock toRelease; int disposed; public Releaser(AsyncLock toRelease) { Preconditions.CheckNotNull(toRelease); this.toRelease = toRelease; this.disposed = 0; } public void Dispose() { if (Interlocked.Exchange(ref this.disposed, 1) == 0) { this.toRelease.semaphore.Release(); } } } } }