@@ -29,6 +29,28 @@ public enum SemaphoreWaitResult
29
29
Entered
30
30
}
31
31
32
+ private readonly struct CancellationContext : IDisposable
33
+ {
34
+ public bool IsSignaled { get ; }
35
+ public CancellationTokenSource LinkedCancellationTokenSource { get ; }
36
+ public CancellationTokenSource SignalCancellationTokenSource { get ; }
37
+
38
+ public static CancellationContext Signaled { get ; } = new CancellationContext ( true , default , default ) ;
39
+
40
+ public CancellationContext (
41
+ bool isSignaled ,
42
+ CancellationTokenSource linkedCancellationTokenSource ,
43
+ CancellationTokenSource signaledCancellationTokenSource )
44
+ {
45
+ IsSignaled = isSignaled ;
46
+ LinkedCancellationTokenSource = linkedCancellationTokenSource ;
47
+ SignalCancellationTokenSource = signaledCancellationTokenSource ;
48
+ }
49
+
50
+ public CancellationToken CancellationToken => LinkedCancellationTokenSource . Token ;
51
+ public void Dispose ( ) => LinkedCancellationTokenSource ? . Dispose ( ) ;
52
+ }
53
+
32
54
public sealed class SemaphoreSlimSignalableAwaiter : IDisposable
33
55
{
34
56
private readonly SemaphoreSlimSignalable _semaphoreSlimSignalable ;
@@ -91,6 +113,7 @@ public void Reset()
91
113
{
92
114
if ( _signalCancellationTokenSource . IsCancellationRequested )
93
115
{
116
+ _signalCancellationTokenSource . Dispose ( ) ;
94
117
_signalCancellationTokenSource = new CancellationTokenSource ( ) ;
95
118
}
96
119
}
@@ -111,21 +134,21 @@ public async Task<SemaphoreWaitResult> WaitAsync(TimeSpan timeout, CancellationT
111
134
112
135
public SemaphoreWaitResult WaitSignaled ( TimeSpan timeout , CancellationToken cancellationToken )
113
136
{
114
- var ( tokenSourceLinked , signalTokenSource , signaled ) = GetLinkedTokenAndCheckForSignaled ( cancellationToken ) ;
137
+ using var cancellationContext = GetCancellationTokenContext ( cancellationToken ) ;
115
138
116
- if ( signaled )
139
+ if ( cancellationContext . IsSignaled )
117
140
{
118
141
return SemaphoreWaitResult . Signaled ;
119
142
}
120
143
121
144
try
122
145
{
123
- var entered = _semaphore . Wait ( timeout , tokenSourceLinked . Token ) ;
146
+ var entered = _semaphore . Wait ( timeout , cancellationContext . CancellationToken ) ;
124
147
return entered ? SemaphoreWaitResult . Entered : SemaphoreWaitResult . TimedOut ;
125
148
}
126
149
catch ( OperationCanceledException )
127
150
{
128
- if ( IsSignaled ( signalTokenSource . Token , cancellationToken ) )
151
+ if ( IsSignaled ( cancellationContext . SignalCancellationTokenSource , cancellationToken ) )
129
152
{
130
153
return SemaphoreWaitResult . Signaled ;
131
154
}
@@ -136,22 +159,22 @@ public SemaphoreWaitResult WaitSignaled(TimeSpan timeout, CancellationToken canc
136
159
137
160
public async Task < SemaphoreWaitResult > WaitSignaledAsync ( TimeSpan timeout , CancellationToken cancellationToken )
138
161
{
139
- var ( tokenSourceLinked , signalTokenSource , signaled ) = GetLinkedTokenAndCheckForSignaled ( cancellationToken ) ;
162
+ using var cancellationContext = GetCancellationTokenContext ( cancellationToken ) ;
140
163
141
- if ( signaled )
164
+ if ( cancellationContext . IsSignaled )
142
165
{
143
166
return SemaphoreWaitResult . Signaled ;
144
167
}
145
168
146
169
try
147
170
{
148
- var entered = await _semaphore . WaitAsync ( timeout , tokenSourceLinked . Token ) . ConfigureAwait ( false ) ;
171
+ var entered = await _semaphore . WaitAsync ( timeout , cancellationContext . CancellationToken ) . ConfigureAwait ( false ) ;
149
172
150
173
return entered ? SemaphoreWaitResult . Entered : SemaphoreWaitResult . TimedOut ;
151
174
}
152
175
catch ( OperationCanceledException )
153
176
{
154
- if ( IsSignaled ( signalTokenSource . Token , cancellationToken ) )
177
+ if ( IsSignaled ( cancellationContext . SignalCancellationTokenSource , cancellationToken ) )
155
178
{
156
179
// Request task rescheduling, to avoid resuming execution on Signal thread
157
180
await TaskExtensions . YieldNoContext ( ) ;
@@ -174,31 +197,46 @@ public void Release()
174
197
public void Dispose ( )
175
198
{
176
199
_semaphore . Dispose ( ) ;
200
+ _signalCancellationTokenSource . Dispose ( ) ;
177
201
}
178
202
179
- private ( CancellationTokenSource TokenSourceLinked , CancellationTokenSource SignalTokenSource , bool Signaled ) GetLinkedTokenAndCheckForSignaled ( CancellationToken cancellationToken )
203
+ private CancellationContext GetCancellationTokenContext ( CancellationToken cancellationToken )
180
204
{
181
205
var signalTokenSource = _signalCancellationTokenSource ;
182
206
183
- if ( IsSignaled ( signalTokenSource . Token , cancellationToken ) )
207
+ if ( IsSignaled ( signalTokenSource , cancellationToken ) )
184
208
{
185
- return ( default , default , true ) ;
209
+ return CancellationContext . Signaled ;
186
210
}
187
211
188
- var tokenSourceLinked = CancellationTokenSource . CreateLinkedTokenSource (
189
- signalTokenSource . Token ,
190
- cancellationToken ) ;
212
+ try
213
+ {
214
+ var cancellationLinkedTokenSource = CancellationTokenSource . CreateLinkedTokenSource (
215
+ signalTokenSource . Token ,
216
+ cancellationToken ) ;
191
217
192
- return ( tokenSourceLinked , signalTokenSource , false ) ;
218
+ return new CancellationContext ( false , cancellationLinkedTokenSource , signalTokenSource ) ;
219
+ }
220
+ catch ( ObjectDisposedException )
221
+ {
222
+ // signalTokenSource was disposed, it will happen only when cancellation was requested for signalTokenSource or on Dispose
223
+ return CancellationContext . Signaled ;
224
+ }
193
225
}
194
226
195
- #pragma warning disable CA1068 // CancellationToken parameters must come last
196
- private bool IsSignaled ( CancellationToken signalToken , CancellationToken cancellationToken )
197
- #pragma warning restore CA1068 // CancellationToken parameters must come last
227
+ private bool IsSignaled ( CancellationTokenSource signalTokenSource , CancellationToken cancellationToken )
198
228
{
199
229
cancellationToken . ThrowIfCancellationRequested ( ) ;
200
230
201
- return signalToken . IsCancellationRequested ;
231
+ try
232
+ {
233
+ return signalTokenSource . Token . IsCancellationRequested ;
234
+ }
235
+ catch ( ObjectDisposedException )
236
+ {
237
+ // signalTokenSource was disposed, it will happen only when cancellation was requested for signalTokenSource or on Dispose
238
+ return true ;
239
+ }
202
240
}
203
241
}
204
242
}
0 commit comments