11using System . ComponentModel ;
22using System . Diagnostics ;
33using System . Diagnostics . CodeAnalysis ;
4+ using System . Numerics ;
45using System . Runtime . CompilerServices ;
56using System . Runtime . InteropServices ;
67using System . Threading . Tasks . Sources ;
@@ -92,7 +93,7 @@ private CancellationState ResetCore(out short token)
9293 /// Resets the state of the source.
9394 /// </summary>
9495 /// <remarks>
95- /// This methods acts as a barrier for completion.
96+ /// This method acts as a barrier for completion.
9697 /// It means that calling of this method guarantees that the task
9798 /// cannot be completed by the previously linked timeout or cancellation token.
9899 /// </remarks>
@@ -293,10 +294,16 @@ public void OnCompleted(Action<object?> continuation, object? state, short token
293294 {
294295 Timeout . Validate ( timeout ) ;
295296
296- // The task can be created for the completed (but not yet consumed) source.
297- // This workaround is needed for AsyncBridge methods
297+ // The source can be completed before the activation. Moreover, someone could try
298+ // to complete the task during the activation concurrently. To avoid lock contention,
299+ // use TryEnterLock(). If we can't acquire the lock, there is concurrent completion.
300+ // In that case, do not activate the source and skip fast.
301+ return TryEnterLock ( ) ? ActivateSlow ( timeout , token ) : versionAndStatus . Version ;
302+ }
303+
304+ private short ? ActivateSlow ( TimeSpan timeout , CancellationToken token )
305+ {
298306 short ? result ;
299- EnterLock ( ) ;
300307 try
301308 {
302309 switch ( versionAndStatus . Status )
@@ -305,11 +312,9 @@ public void OnCompleted(Action<object?> continuation, object? state, short token
305312 CompleteAsTimedOut ( ) ;
306313 goto case ManualResetCompletionSourceStatus . WaitForConsumption ;
307314 case ManualResetCompletionSourceStatus . WaitForActivation :
308- state . Initialize ( ref versionAndStatus , cancellationCallback , timeout , token ) ;
309-
310- if ( token . IsCancellationRequested )
315+ if ( ! state . Initialize ( ref versionAndStatus , cancellationCallback , timeout , token ) )
311316 CompleteAsCanceled ( token ) ;
312-
317+
313318 goto case ManualResetCompletionSourceStatus . WaitForConsumption ;
314319 case ManualResetCompletionSourceStatus . WaitForConsumption :
315320 result = versionAndStatus . Version ;
@@ -570,10 +575,10 @@ private struct CancellationState : IDisposable
570575 private CancellationTokenRegistration tokenTracker ;
571576 private CancellationTokenSource ? timeoutSource ;
572577
573- internal void Initialize ( ref VersionAndStatus vs , Action < object ? , CancellationToken > callback , TimeSpan timeout , CancellationToken token )
578+ internal bool Initialize ( ref VersionAndStatus vs , Action < object ? , CancellationToken > callback , TimeSpan timeout , CancellationToken token )
574579 {
575580 // box current token once and only if needed
576- var cachedVersion = default ( IEquatable < short > ) ;
581+ IBinaryInteger < short > ? cachedVersion = null ;
577582
578583 if ( token . CanBeCanceled )
579584 {
@@ -586,7 +591,10 @@ internal void Initialize(ref VersionAndStatus vs, Action<object?, CancellationTo
586591 // To avoid that, change the status later and check the token after calling this method
587592 vs . Status = ManualResetCompletionSourceStatus . Activated ;
588593
589- if ( timeout > default ( TimeSpan ) && ! token . IsCancellationRequested )
594+ if ( token . IsCancellationRequested )
595+ return false ;
596+
597+ if ( timeout > default ( TimeSpan ) )
590598 {
591599 timeoutSource ??= new ( ) ;
592600
@@ -595,6 +603,8 @@ internal void Initialize(ref VersionAndStatus vs, Action<object?, CancellationTo
595603 timeoutSource . Token . UnsafeRegister ( callback , cachedVersion ?? vs . Version ) ;
596604 timeoutSource . CancelAfter ( timeout ) ;
597605 }
606+
607+ return true ;
598608 }
599609
600610 internal readonly bool IsTimeoutToken ( CancellationToken token )
0 commit comments