// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Threading; using System.Threading.Tasks; namespace Microsoft.SqlTools.EditorServices.Utility { /// /// Provides a simple wrapper over a SemaphoreSlim to allow /// synchronization locking inside of async calls. Cannot be /// used recursively. /// public class AsyncLock { #region Fields private Task lockReleaseTask; private SemaphoreSlim lockSemaphore = new SemaphoreSlim(1, 1); #endregion #region Constructors /// /// Initializes a new instance of the AsyncLock class. /// public AsyncLock() { this.lockReleaseTask = Task.FromResult( (IDisposable)new LockReleaser(this)); } #endregion #region Public Methods /// /// Locks /// /// A task which has an IDisposable public Task LockAsync() { return this.LockAsync(CancellationToken.None); } /// /// Obtains or waits for a lock which can be used to synchronize /// access to a resource. The wait may be cancelled with the /// given CancellationToken. /// /// /// A CancellationToken which can be used to cancel the lock. /// /// public Task LockAsync(CancellationToken cancellationToken) { Task waitTask = lockSemaphore.WaitAsync(cancellationToken); return waitTask.IsCompleted ? this.lockReleaseTask : waitTask.ContinueWith( (t, releaser) => { return (IDisposable)releaser; }, this.lockReleaseTask.Result, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } #endregion #region Private Classes /// /// Provides an IDisposable wrapper around an AsyncLock so /// that it can easily be used inside of a 'using' block. /// private class LockReleaser : IDisposable { private AsyncLock lockToRelease; internal LockReleaser(AsyncLock lockToRelease) { this.lockToRelease = lockToRelease; } public void Dispose() { this.lockToRelease.lockSemaphore.Release(); } } #endregion } }