@@ -207,51 +207,104 @@ private void SignalCompletion()
207207 }
208208 _completed = true ;
209209
210- if ( _continuation != null || Interlocked . CompareExchange ( ref _continuation , ManualResetValueTaskSourceCoreShared . s_sentinel , null ) != null )
210+ if ( _continuation is null && Interlocked . CompareExchange ( ref _continuation , ManualResetValueTaskSourceCoreShared . s_sentinel , null ) is null )
211211 {
212- if ( _executionContext != null )
212+ return ;
213+ }
214+
215+ if ( _executionContext is null )
216+ {
217+ if ( _capturedContext is null )
218+ {
219+ if ( RunContinuationsAsynchronously )
220+ {
221+ ThreadPool . UnsafeQueueUserWorkItem ( _continuation , _continuationState , preferLocal : true ) ;
222+ }
223+ else
224+ {
225+ _continuation ( _continuationState ) ;
226+ }
227+
228+ return ;
229+ }
230+
231+ InvokeSchedulerContinuation ( ) ;
232+ return ;
233+ }
234+
235+ ExecutionContext ? currentContext = ExecutionContext . Capture ( ) ;
236+ // Restore the captured ExecutionContext before executing anything.
237+ ExecutionContext . Restore ( _executionContext ) ;
238+
239+ if ( _capturedContext is null )
240+ {
241+ if ( RunContinuationsAsynchronously )
213242 {
214- ExecutionContext . RunInternal (
215- _executionContext ,
216- ( ref ManualResetValueTaskSourceCore < TResult > s ) => s . InvokeContinuation ( ) ,
217- ref this ) ;
243+ ThreadPool . QueueUserWorkItem ( _continuation , _continuationState , preferLocal : true ) ;
244+ // Restore the current ExecutionContext.
245+ ExecutionContext . Restore ( currentContext ) ;
218246 }
219247 else
220248 {
221- InvokeContinuation ( ) ;
249+ // Running inline may throw; capture the edi if it does as we changed the ExecutionContext,
250+ // so need to restore it back before propagating the throw.
251+ ExceptionDispatchInfo ? edi = InvokeInlineContinuation ( ) ;
252+ // Restore the current ExecutionContext.
253+ ExecutionContext . Restore ( currentContext ) ;
254+ // Now rethrow the exception; if there is one.
255+ edi ? . Throw ( ) ;
222256 }
257+
258+ return ;
223259 }
260+
261+ InvokeSchedulerContinuation ( ) ;
262+ // Restore the current ExecutionContext.
263+ ExecutionContext . Restore ( currentContext ) ;
224264 }
225265
226266 /// <summary>
227- /// Invokes the continuation with the appropriate captured context / scheduler .
228- /// This assumes that if <see cref="_executionContext "/> is not null we're already
267+ /// Invokes the continuation inline and captures any exception thrown .
268+ /// This assumes that if <see cref="_continuation "/> is not null we're already
229269 /// running within that <see cref="ExecutionContext"/>.
230270 /// </summary>
231- private void InvokeContinuation ( )
271+ private ExceptionDispatchInfo ? InvokeInlineContinuation ( )
232272 {
233273 Debug . Assert ( _continuation != null ) ;
274+ Debug . Assert ( _capturedContext == null ) ;
275+ Debug . Assert ( ! RunContinuationsAsynchronously ) ;
234276
235- switch ( _capturedContext )
277+ ExceptionDispatchInfo ? edi = null ;
278+ SynchronizationContext ? syncContext = SynchronizationContext . Current ;
279+ try
236280 {
237- case null :
238- if ( RunContinuationsAsynchronously )
239- {
240- if ( _executionContext != null )
241- {
242- ThreadPool . QueueUserWorkItem ( _continuation , _continuationState , preferLocal : true ) ;
243- }
244- else
245- {
246- ThreadPool . UnsafeQueueUserWorkItem ( _continuation , _continuationState , preferLocal : true ) ;
247- }
248- }
249- else
250- {
251- _continuation ( _continuationState ) ;
252- }
253- break ;
281+ _continuation ( _continuationState ) ;
282+ }
283+ catch ( Exception ex )
284+ {
285+ // Note: we have a "catch" rather than a "finally" because we want
286+ // to stop the first pass of EH here. That way we can restore the previous
287+ // context before any of our callers' EH filters run.
288+ edi = ExceptionDispatchInfo . Capture ( ex ) ;
289+ }
290+
291+ // Set sync context back to what it was prior to coming in
292+ SynchronizationContext . SetSynchronizationContext ( syncContext ) ;
293+ return edi ;
294+ }
295+
296+ /// <summary>
297+ /// Invokes the continuation with the appropriate scheduler.
298+ /// This assumes that if <see cref="_continuation"/> is not null we're already
299+ /// running within that <see cref="ExecutionContext"/>.
300+ /// </summary>
301+ private void InvokeSchedulerContinuation ( )
302+ {
303+ Debug . Assert ( _capturedContext != null ) ;
304+ Debug . Assert ( _continuation != null ) ;
254305
306+ switch ( _capturedContext )
307+ {
255308 case SynchronizationContext sc :
256309 sc . Post ( s =>
257310 {
0 commit comments