-
Notifications
You must be signed in to change notification settings - Fork 2
Potential promise context lost fix #4583 #4584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -450,7 +450,19 @@ void ICriticalNotifyCompletion.UnsafeOnCompleted(Action continuation) | |
|
|
||
| void INotifyCompletion.OnCompleted(Action continuation) | ||
| { | ||
| ((ICriticalNotifyCompletion)this).UnsafeOnCompleted(continuation); | ||
| // Mirror TaskAwaiter.OnCompleted: flow ExecutionContext so AsyncLocal | ||
| // values set by the caller survive the await and are restored on the | ||
| // thread that resolves the promise. | ||
| var capturedContext = System.Threading.ExecutionContext.Capture(); | ||
| if (capturedContext == null) | ||
| { | ||
| ((ICriticalNotifyCompletion)this).UnsafeOnCompleted(continuation); | ||
| return; | ||
| } | ||
| ((ICriticalNotifyCompletion)this).UnsafeOnCompleted(() => | ||
| { | ||
| System.Threading.ExecutionContext.Run(capturedContext, s => ((Action)s)(), continuation); | ||
| }); | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -1253,6 +1265,18 @@ public void SetStateMachine(IAsyncStateMachine machine) | |
| _stateMachine = machine; | ||
| } | ||
|
|
||
| private void MoveNextWithCapturedContext(System.Threading.ExecutionContext capturedContext) | ||
| { | ||
| if (capturedContext == null) | ||
| { | ||
| _stateMachine.MoveNext(); | ||
| } | ||
| else | ||
| { | ||
| System.Threading.ExecutionContext.Run(capturedContext, s => ((IAsyncStateMachine)s).MoveNext(), _stateMachine); | ||
| } | ||
|
Comment on lines
+1268
to
+1277
|
||
| } | ||
|
|
||
| public void AwaitOnCompleted<TAwaiter, TStateMachine>( | ||
| ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||
| where TAwaiter : INotifyCompletion | ||
|
|
@@ -1264,18 +1288,26 @@ public void AwaitOnCompleted<TAwaiter, TStateMachine>( | |
| _stateMachine.SetStateMachine(stateMachine); | ||
| } | ||
|
|
||
| awaiter.OnCompleted(() => | ||
| { | ||
| _stateMachine.MoveNext(); | ||
| }); | ||
| // Mirror AsyncTaskMethodBuilder: capture ExecutionContext at the await | ||
| // point and restore it before resuming the state machine so AsyncLocal | ||
| // values flow across the await. | ||
| var capturedContext = System.Threading.ExecutionContext.Capture(); | ||
| awaiter.OnCompleted(() => MoveNextWithCapturedContext(capturedContext)); | ||
|
Comment on lines
+1291
to
+1295
|
||
| } | ||
|
|
||
| public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( | ||
| ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||
| where TAwaiter : ICriticalNotifyCompletion | ||
| where TStateMachine : IAsyncStateMachine | ||
| { | ||
| AwaitOnCompleted(ref awaiter, ref stateMachine); | ||
| if (_stateMachine == null) | ||
| { | ||
| _stateMachine = stateMachine; | ||
| _stateMachine.SetStateMachine(stateMachine); | ||
| } | ||
|
|
||
| var capturedContext = System.Threading.ExecutionContext.Capture(); | ||
| awaiter.UnsafeOnCompleted(() => MoveNextWithCapturedContext(capturedContext)); | ||
|
Comment on lines
+1303
to
+1310
|
||
| } | ||
|
|
||
| public void Start<TStateMachine>(ref TStateMachine stateMachine) | ||
|
|
@@ -1313,6 +1345,18 @@ public void SetStateMachine(IAsyncStateMachine machine) | |
| _stateMachine = machine; | ||
| } | ||
|
|
||
| private void MoveNextWithCapturedContext(System.Threading.ExecutionContext capturedContext) | ||
| { | ||
| if (capturedContext == null) | ||
| { | ||
| _stateMachine.MoveNext(); | ||
| } | ||
| else | ||
| { | ||
| System.Threading.ExecutionContext.Run(capturedContext, s => ((IAsyncStateMachine)s).MoveNext(), _stateMachine); | ||
| } | ||
| } | ||
|
|
||
| public void AwaitOnCompleted<TAwaiter, TStateMachine>( | ||
| ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||
| where TAwaiter : INotifyCompletion | ||
|
|
@@ -1324,18 +1368,23 @@ public void AwaitOnCompleted<TAwaiter, TStateMachine>( | |
| _stateMachine.SetStateMachine(stateMachine); | ||
| } | ||
|
|
||
| awaiter.OnCompleted(() => | ||
| { | ||
| _stateMachine.MoveNext(); | ||
| }); | ||
| var capturedContext = System.Threading.ExecutionContext.Capture(); | ||
| awaiter.OnCompleted(() => MoveNextWithCapturedContext(capturedContext)); | ||
| } | ||
|
|
||
| public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>( | ||
| ref TAwaiter awaiter, ref TStateMachine stateMachine) | ||
| where TAwaiter : ICriticalNotifyCompletion | ||
| where TStateMachine : IAsyncStateMachine | ||
| { | ||
| AwaitOnCompleted(ref awaiter, ref stateMachine); | ||
| if (_stateMachine == null) | ||
| { | ||
| _stateMachine = stateMachine; | ||
| _stateMachine.SetStateMachine(stateMachine); | ||
| } | ||
|
|
||
| var capturedContext = System.Threading.ExecutionContext.Capture(); | ||
| awaiter.UnsafeOnCompleted(() => MoveNextWithCapturedContext(capturedContext)); | ||
|
Comment on lines
+1371
to
+1387
|
||
| } | ||
|
|
||
| public void Start<TStateMachine>(ref TStateMachine stateMachine) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
System.Threading.ExecutionContextis referenced here, but this file definesDISABLE_THREADINGforUNITY_WEBGLand elsewhere conditionally compiles outSystem.Threadingusage. As-is, this new code path can break WebGL builds (or any build withDISABLE_THREADING) becauseExecutionContextlives inSystem.Threading. Consider wrapping the ExecutionContext capture/run logic in#if !DISABLE_THREADINGand falling back to the previousUnsafeOnCompleted(continuation)behavior when threading is disabled.