Skip to content

Commit 3895f53

Browse files
committed
Restore context in finally
1 parent 78102c6 commit 3895f53

File tree

1 file changed

+52
-43
lines changed

1 file changed

+52
-43
lines changed

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs

Lines changed: 52 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,26 @@ private void SignalCompletion()
224224
{
225225
_continuation(_continuationState);
226226
}
227-
228-
return;
229227
}
230-
231-
InvokeSchedulerContinuation();
232-
return;
228+
else
229+
{
230+
InvokeSchedulerContinuation();
231+
}
232+
}
233+
else
234+
{
235+
InvokeContinuationWithContext();
233236
}
237+
}
238+
239+
private void InvokeContinuationWithContext()
240+
{
241+
// This is in a helper as the error handling causes the generated asm
242+
// for the surrounding code to become less efficent (stack spills etc)
243+
// and it is an uncommon path.
244+
245+
Debug.Assert(_continuation != null);
246+
Debug.Assert(_executionContext != null);
234247

235248
ExecutionContext? currentContext = ExecutionContext.Capture();
236249
// Restore the captured ExecutionContext before executing anything.
@@ -240,61 +253,57 @@ private void SignalCompletion()
240253
{
241254
if (RunContinuationsAsynchronously)
242255
{
243-
ThreadPool.QueueUserWorkItem(_continuation, _continuationState, preferLocal: true);
244-
// Restore the current ExecutionContext.
245-
ExecutionContext.Restore(currentContext);
256+
try
257+
{
258+
ThreadPool.QueueUserWorkItem(_continuation, _continuationState, preferLocal: true);
259+
}
260+
finally
261+
{
262+
// Restore the current ExecutionContext.
263+
ExecutionContext.Restore(currentContext);
264+
}
246265
}
247266
else
248267
{
249268
// Running inline may throw; capture the edi if it does as we changed the ExecutionContext,
250269
// so need to restore it back before propagating the throw.
251-
ExceptionDispatchInfo? edi = InvokeInlineContinuation();
252-
// Restore the current ExecutionContext.
253-
ExecutionContext.Restore(currentContext);
270+
ExceptionDispatchInfo? edi = null;
271+
SynchronizationContext? syncContext = SynchronizationContext.Current;
272+
try
273+
{
274+
_continuation(_continuationState);
275+
}
276+
catch (Exception ex)
277+
{
278+
// Note: we have a "catch" rather than a "finally" because we want
279+
// to stop the first pass of EH here. That way we can restore the previous
280+
// context before any of our callers' EH filters run.
281+
edi = ExceptionDispatchInfo.Capture(ex);
282+
}
283+
finally
284+
{
285+
// Set sync context back to what it was prior to coming in
286+
SynchronizationContext.SetSynchronizationContext(syncContext);
287+
// Restore the current ExecutionContext.
288+
ExecutionContext.Restore(currentContext);
289+
}
290+
254291
// Now rethrow the exception; if there is one.
255292
edi?.Throw();
256293
}
257294

258295
return;
259296
}
260297

261-
InvokeSchedulerContinuation();
262-
// Restore the current ExecutionContext.
263-
ExecutionContext.Restore(currentContext);
264-
}
265-
266-
/// <summary>
267-
/// Invokes the continuation inline and captures any exception thrown.
268-
/// This assumes that if <see cref="_continuation"/> is not null we're already
269-
/// running within that <see cref="ExecutionContext"/>.
270-
/// </summary>
271-
private ExceptionDispatchInfo? InvokeInlineContinuation()
272-
{
273-
// This is in a helper as the error handling causes the generated asm
274-
// for the surrounding code to become less efficent (stack spills etc)
275-
// and it is an uncommon path.
276-
277-
Debug.Assert(_continuation != null);
278-
Debug.Assert(_capturedContext == null);
279-
Debug.Assert(!RunContinuationsAsynchronously);
280-
281-
ExceptionDispatchInfo? edi = null;
282-
SynchronizationContext? syncContext = SynchronizationContext.Current;
283298
try
284299
{
285-
_continuation(_continuationState);
300+
InvokeSchedulerContinuation();
286301
}
287-
catch (Exception ex)
302+
finally
288303
{
289-
// Note: we have a "catch" rather than a "finally" because we want
290-
// to stop the first pass of EH here. That way we can restore the previous
291-
// context before any of our callers' EH filters run.
292-
edi = ExceptionDispatchInfo.Capture(ex);
304+
// Restore the current ExecutionContext.
305+
ExecutionContext.Restore(currentContext);
293306
}
294-
295-
// Set sync context back to what it was prior to coming in
296-
SynchronizationContext.SetSynchronizationContext(syncContext);
297-
return edi;
298307
}
299308

300309
/// <summary>

0 commit comments

Comments
 (0)