-
Notifications
You must be signed in to change notification settings - Fork 152
/
AsyncLock.cs
103 lines (85 loc) · 2.86 KB
/
AsyncLock.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//
// 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.Utility
{
/// <summary>
/// Provides a simple wrapper over a SemaphoreSlim to allow
/// synchronization locking inside of async calls. Cannot be
/// used recursively.
/// </summary>
public class AsyncLock
{
#region Fields
private Task<IDisposable> lockReleaseTask;
private SemaphoreSlim lockSemaphore = new SemaphoreSlim(1, 1);
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the AsyncLock class.
/// </summary>
public AsyncLock()
{
this.lockReleaseTask =
Task.FromResult(
(IDisposable)new LockReleaser(this));
}
#endregion
#region Public Methods
/// <summary>
/// Locks
/// </summary>
/// <returns>A task which has an IDisposable</returns>
public Task<IDisposable> LockAsync()
{
return this.LockAsync(CancellationToken.None);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="cancellationToken">
/// A CancellationToken which can be used to cancel the lock.
/// </param>
/// <returns></returns>
public Task<IDisposable> 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
/// <summary>
/// Provides an IDisposable wrapper around an AsyncLock so
/// that it can easily be used inside of a 'using' block.
/// </summary>
private class LockReleaser : IDisposable
{
private AsyncLock lockToRelease;
internal LockReleaser(AsyncLock lockToRelease)
{
this.lockToRelease = lockToRelease;
}
public void Dispose()
{
this.lockToRelease.lockSemaphore.Release();
}
}
#endregion
}
}