@@ -97,18 +97,44 @@ public bool TryTakeSync()
9797 }
9898
9999 public ValueTask < bool > TryTakeAsync ( CancellationToken cancellationToken )
100+ {
101+ bool lockTaken = false ;
102+ try
103+ {
104+ // try to acquire uncontested lock - that way we can avoid allocating the pending caller
105+ Monitor . TryEnter ( _queue , 0 , ref lockTaken ) ;
106+ if ( lockTaken )
107+ {
108+ if ( _isDisposed ) return DisposedAsync ( ) ;
109+ if ( cancellationToken . IsCancellationRequested )
110+ {
111+ return CanceledAsync ( cancellationToken ) ;
112+ }
113+
114+ if ( TryTakeInsideLockCore ( ) ) return new ValueTask < bool > ( true ) ;
115+ }
116+ }
117+ finally
118+ {
119+ if ( lockTaken ) Monitor . Exit ( _queue ) ;
120+ }
121+
122+ return TryTakeAsyncSlow ( cancellationToken ) ;
123+ }
124+
125+ private ValueTask < bool > TryTakeAsyncSlow ( CancellationToken cancellationToken )
100126 {
101127 lock ( _queue )
102128 {
129+ if ( _isDisposed ) return DisposedAsync ( ) ;
103130 if ( cancellationToken . IsCancellationRequested )
104131 {
105- ThrowIfDisposed ( ) ;
106- return AsyncPendingCaller . Canceled ( ) ;
132+ return CanceledAsync ( cancellationToken ) ;
107133 }
108134
109- if ( TryTakeInsideLock ( ) ) return new ValueTask < bool > ( true ) ;
135+ if ( TryTakeInsideLockCore ( ) ) return new ValueTask < bool > ( true ) ;
110136 if ( TimeoutMilliseconds == 0 ) return new ValueTask < bool > ( false ) ;
111- if ( cancellationToken . IsCancellationRequested ) return AsyncPendingCaller . Canceled ( ) ;
137+ if ( cancellationToken . IsCancellationRequested ) return CanceledAsync ( cancellationToken ) ;
112138
113139 var pending = new AsyncPendingCaller ( TimeoutMilliseconds , cancellationToken ) ;
114140 _queue . Enqueue ( pending ) ;
@@ -137,6 +163,11 @@ public void Release()
137163 private bool TryTakeInsideLock ( )
138164 {
139165 ThrowIfDisposed ( ) ;
166+ return TryTakeInsideLockCore ( ) ;
167+ }
168+
169+ private bool TryTakeInsideLockCore ( )
170+ {
140171 if ( _isHeld || _queue . Count != 0 ) return false ;
141172 _isHeld = true ;
142173 return true ;
@@ -191,6 +222,12 @@ private void ThrowIfDisposed()
191222
192223 private static void ThrowDisposed ( ) => throw new ObjectDisposedException ( nameof ( AwaitableMutex ) ) ;
193224
225+ private static ValueTask < bool > DisposedAsync ( )
226+ => new ( Task . FromException < bool > ( new ObjectDisposedException ( nameof ( AwaitableMutex ) ) ) ) ;
227+
228+ private static ValueTask < bool > CanceledAsync ( CancellationToken cancellationToken )
229+ => new ( Task . FromCanceled < bool > ( cancellationToken ) ) ;
230+
194231 private static uint GetTime ( ) => ( uint ) Environment . TickCount ;
195232
196233 private static int GetRemainingTimeout ( uint startTime , int originalTimeoutMilliseconds )
@@ -280,15 +317,16 @@ private sealed class AsyncPendingCaller : TaskCompletionSource<bool>, IPendingCa
280317 {
281318 private static readonly TimerCallback s_onTimeout = state => ( ( AsyncPendingCaller ) state ! ) . TryComplete ( CompletionState . TimedOut ) ;
282319 private static readonly Action < object ? > s_onCanceled = state => ( ( AsyncPendingCaller ) state ! ) . TryComplete ( CompletionState . Canceled ) ;
283- private static readonly Task < bool > s_canceledTask = CreateCanceledTask ( ) ;
284320
321+ private readonly CancellationToken _cancellationToken ;
285322 private readonly CancellationTokenRegistration _cancellation ;
286323 private readonly Timer ? _timeout ;
287324 private int _completionState ;
288325
289326 public AsyncPendingCaller ( int timeoutMilliseconds , CancellationToken cancellationToken )
290327 : base ( TaskCreationOptions . RunContinuationsAsynchronously )
291328 {
329+ _cancellationToken = cancellationToken ;
292330 if ( timeoutMilliseconds != Timeout . Infinite )
293331 {
294332 _timeout = new Timer ( s_onTimeout , this , timeoutMilliseconds , Timeout . Infinite ) ;
@@ -300,8 +338,6 @@ public AsyncPendingCaller(int timeoutMilliseconds, CancellationToken cancellatio
300338 }
301339 }
302340
303- public static ValueTask < bool > Canceled ( ) => new ( s_canceledTask ) ;
304-
305341 public bool TryGrant ( ) => TryComplete ( CompletionState . Granted ) ;
306342
307343 public void Abort ( ) => TryComplete ( CompletionState . Disposed ) ;
@@ -331,21 +367,14 @@ private void Complete(CompletionState completionState)
331367 TrySetResult ( false ) ;
332368 break ;
333369 case CompletionState . Canceled :
334- TrySetCanceled ( ) ;
370+ TrySetCanceled ( _cancellationToken ) ;
335371 break ;
336372 case CompletionState . Disposed :
337373 TrySetException ( new ObjectDisposedException ( nameof ( AwaitableMutex ) ) ) ;
338374 break ;
339375 }
340376 }
341377
342- private static Task < bool > CreateCanceledTask ( )
343- {
344- var source = new TaskCompletionSource < bool > ( ) ;
345- source . SetCanceled ( ) ;
346- return source . Task ;
347- }
348-
349378 private enum CompletionState
350379 {
351380 Pending = 0 ,
0 commit comments