@@ -739,6 +739,12 @@ public void ReleaseLock(string instanceId)
739739
740740 public Task < OrchestrationState > WaitForInstanceAsync ( string instanceId , CancellationToken cancellationToken )
741741 {
742+ // First, add the waiter before checking completion to avoid a race condition.
743+ // This ensures we don't miss a completion notification that happens between
744+ // checking the status and adding the waiter.
745+ var tcs = this . waiters . GetOrAdd ( instanceId , _ => new TaskCompletionSource < OrchestrationState > ( ) ) ;
746+
747+ // Now check if already completed - if so, complete the waiter immediately
742748 if ( this . store . TryGetValue ( instanceId , out SerializedInstanceState ? state ) )
743749 {
744750 lock ( state )
@@ -750,16 +756,18 @@ public Task<OrchestrationState> WaitForInstanceAsync(string instanceId, Cancella
750756 statusRecord . OrchestrationStatus == OrchestrationStatus . Failed ||
751757 statusRecord . OrchestrationStatus == OrchestrationStatus . Terminated )
752758 {
753- // orchestration has already completed
754- return Task . FromResult ( statusRecord ) ;
759+ // Orchestration has already completed - complete the waiter and clean it up
760+ if ( tcs . TrySetResult ( statusRecord ) )
761+ {
762+ this . waiters . TryRemove ( instanceId , out _ ) ;
763+ }
755764 }
756765 }
757766 }
758767 }
759768
760769 // Caller will be notified when the instance completes.
761770 // The ContinueWith is just to enable cancellation: https://stackoverflow.com/a/25652873/2069
762- var tcs = this . waiters . GetOrAdd ( instanceId , _ => new TaskCompletionSource < OrchestrationState > ( ) ) ;
763771 return tcs . Task . ContinueWith ( t => t . GetAwaiter ( ) . GetResult ( ) , cancellationToken ) ;
764772 }
765773
0 commit comments