-
-
Notifications
You must be signed in to change notification settings - Fork 357
/
AsyncCountdownEvent.cs
179 lines (159 loc) · 5.56 KB
/
AsyncCountdownEvent.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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
// Original idea by Stephen Toub: http://blogs.msdn.com/b/pfxteam/archive/2012/02/11/10266930.aspx
namespace Nito.AsyncEx
{
/// <summary>
/// An async-compatible countdown event.
/// </summary>
[DebuggerDisplay("Id = {Id}, CurrentCount = {_count}")]
[DebuggerTypeProxy(typeof(DebugView))]
public sealed class AsyncCountdownEvent
{
/// <summary>
/// The underlying manual-reset event.
/// </summary>
private readonly AsyncManualResetEvent _mre;
/// <summary>
/// The remaining count on this event.
/// </summary>
private long _count;
/// <summary>
/// Creates an async-compatible countdown event.
/// </summary>
/// <param name="count">The number of signals this event will need before it becomes set.</param>
public AsyncCountdownEvent(long count)
{
_mre = new AsyncManualResetEvent(count == 0);
_count = count;
}
/// <summary>
/// Gets a semi-unique identifier for this asynchronous countdown event.
/// </summary>
public int Id
{
get { return _mre.Id; }
}
/// <summary>
/// Gets the current number of remaining signals before this event becomes set. This member is seldom used; code using this member has a high possibility of race conditions.
/// </summary>
public long CurrentCount
{
get
{
lock (_mre)
return _count;
}
}
/// <summary>
/// Asynchronously waits for the count to reach zero.
/// </summary>
public Task WaitAsync()
{
return _mre.WaitAsync();
}
/// <summary>
/// Synchronously waits for the count to reach zero. This method may block the calling thread.
/// </summary>
/// <param name="cancellationToken">The cancellation token used to cancel the wait. If this token is already canceled, this method will first check whether the event is set.</param>
public Task WaitAsync(CancellationToken cancellationToken)
{
return _mre.WaitAsync(cancellationToken);
}
/// <summary>
/// Synchronously waits for the count to reach zero. This method may block the calling thread.
/// </summary>
public void Wait()
{
_mre.Wait();
}
/// <summary>
/// Synchronously waits for the count to reach zero. This method may block the calling thread.
/// </summary>
/// <param name="cancellationToken">The cancellation token used to cancel the wait. If this token is already canceled, this method will first check whether the event is set.</param>
public void Wait(CancellationToken cancellationToken)
{
_mre.Wait(cancellationToken);
}
/// <summary>
/// Attempts to modify the current count by the specified amount.
/// </summary>
/// <param name="difference">The amount to change the current count.</param>
/// <param name="add"><c>true</c> to add to the current count; <c>false</c> to subtract.</param>
private void ModifyCount(long difference, bool add)
{
if (difference == 0)
return;
lock (_mre)
{
var oldCount = _count;
checked
{
if (add)
_count += difference;
else
_count -= difference;
}
if (oldCount == 0)
{
_mre.Reset();
}
else if (_count == 0)
{
_mre.Set();
}
else if ((oldCount < 0 && _count > 0) || (oldCount > 0 && _count < 0))
{
_mre.Set();
_mre.Reset();
}
}
}
/// <summary>
/// Adds the specified value to the current count.
/// </summary>
/// <param name="addCount">The amount to change the current count.</param>
public void AddCount(long addCount)
{
ModifyCount(addCount, true);
}
/// <summary>
/// Adds one to the current count.
/// </summary>
public void AddCount()
{
AddCount(1);
}
/// <summary>
/// Subtracts the specified value from the current count.
/// </summary>
/// <param name="signalCount">The amount to change the current count.</param>
public void Signal(long signalCount)
{
ModifyCount(signalCount, false);
}
/// <summary>
/// Subtracts one from the current count.
/// </summary>
public void Signal()
{
Signal(1);
}
// ReSharper disable UnusedMember.Local
[DebuggerNonUserCode]
private sealed class DebugView
{
private readonly AsyncCountdownEvent _ce;
public DebugView(AsyncCountdownEvent ce)
{
_ce = ce;
}
public int Id { get { return _ce.Id; } }
public long CurrentCount { get { return _ce.CurrentCount; } }
public AsyncManualResetEvent AsyncManualResetEvent { get { return _ce._mre; } }
}
// ReSharper restore UnusedMember.Local
}
}