@@ -41,8 +41,16 @@ protected QueuedSynchronizer()
41
41
/// </summary>
42
42
/// <param name="context">The context associated with the suspended caller or supplied externally.</param>
43
43
/// <returns><see langword="true"/> if acquisition is allowed; otherwise, <see langword="false"/>.</returns>
44
+ /// <exception cref="Exception">A custom exception is thrown.</exception>
44
45
protected abstract bool CanAcquire ( TContext context ) ;
45
46
47
+ /// <summary>
48
+ /// Returns an optional exception if <see cref="CanAcquire"/> returns <see langword="false"/>.
49
+ /// </summary>
50
+ /// <param name="context">The acquisition context.</param>
51
+ /// <returns>The exception; or <see langword="null"/>.</returns>
52
+ protected virtual Exception ? GetAcquisitionException ( TContext context ) => null ;
53
+
46
54
/// <summary>
47
55
/// Modifies the internal state according to acquisition semantics.
48
56
/// </summary>
@@ -69,11 +77,19 @@ private protected sealed override void DrainWaitQueue(ref WaitQueueVisitor waitQ
69
77
{
70
78
for ( ; ! waitQueueVisitor . IsEndOfQueue < WaitNode , TContext > ( out var context ) ; waitQueueVisitor . Advance ( ) )
71
79
{
72
- if ( ! CanAcquire ( context ) )
73
- break ;
74
-
75
- if ( waitQueueVisitor . SignalCurrent ( ) )
76
- AcquireCore ( context ) ;
80
+ switch ( CanAcquire ( context ) )
81
+ {
82
+ case false when GetAcquisitionException ( context ) is { } e :
83
+ waitQueueVisitor . SignalCurrent ( e ) ;
84
+ goto default ;
85
+ case false :
86
+ return ;
87
+ case true when waitQueueVisitor . SignalCurrent ( ) :
88
+ AcquireCore ( context ) ;
89
+ goto default ;
90
+ default :
91
+ continue ;
92
+ }
77
93
}
78
94
}
79
95
@@ -194,6 +210,34 @@ protected ValueTask AcquireAsync(TContext context, CancellationToken token)
194
210
return AcquireAsync < ValueTask , CancellationTokenOnly > ( context , ref builder ) ;
195
211
}
196
212
213
+ private T AcquireAsync < T , TBuilder > ( TContext context , ref TBuilder builder )
214
+ where T : struct , IEquatable < T >
215
+ where TBuilder : struct , ITaskBuilder < T >
216
+ {
217
+ T result ;
218
+ if ( ! builder . IsCompleted )
219
+ {
220
+ var acquired = TryAcquireCore ( context ) ;
221
+ if ( ! acquired && GetAcquisitionException ( context ) is { } e )
222
+ {
223
+ result = TBuilder . FromException ( e ) ;
224
+ goto exit ;
225
+ }
226
+
227
+ if ( Acquire < T , TBuilder , WaitNode > ( ref builder , acquired ) is { } node )
228
+ {
229
+ node . DrainOnReturn = true ;
230
+ node . Context = context ;
231
+ }
232
+ }
233
+
234
+ result = builder . Invoke ( ) ;
235
+
236
+ exit :
237
+ builder . Dispose ( ) ;
238
+ return result ;
239
+ }
240
+
197
241
private bool TryAcquireCore ( TContext context )
198
242
{
199
243
Debug . Assert ( Monitor . IsEntered ( SyncRoot ) ) ;
@@ -206,24 +250,4 @@ private bool TryAcquireCore(TContext context)
206
250
207
251
return false ;
208
252
}
209
-
210
- private T AcquireAsync < T , TBuilder > ( TContext context , ref TBuilder builder )
211
- where T : struct , IEquatable < T >
212
- where TBuilder : struct , ITaskBuilder < T >
213
- {
214
- switch ( builder . IsCompleted )
215
- {
216
- case true :
217
- goto default ;
218
- case false when Acquire < T , TBuilder , WaitNode > ( ref builder , TryAcquireCore ( context ) ) is { } node :
219
- node . DrainOnReturn = true ;
220
- node . Context = context ;
221
- goto default ;
222
- default :
223
- builder . Dispose ( ) ;
224
- break ;
225
- }
226
-
227
- return builder . Invoke ( ) ;
228
- }
229
253
}
0 commit comments