Fix handling of continuations for captured context
authorStephen Toub <stoub@microsoft.com>
Fri, 6 Oct 2017 22:38:00 +0000 (18:38 -0400)
committerStephen Toub <stoub@microsoft.com>
Fri, 6 Oct 2017 22:38:00 +0000 (18:38 -0400)
My recent delegate optimization for async methods introduced a race condition that breaks posting back to a captured context.  If the awaited task completes between the time we check IsCompleted and when we try to store the continuation into the Task, we need to queue the continuation for execution, but my change incorrectly queued it to the thread pool rather than to the captured context.

src/mscorlib/src/System/Threading/Tasks/Task.cs

index e3dd01c..aed2c3b 100644 (file)
@@ -2644,10 +2644,10 @@ namespace System.Threading.Tasks
                 SynchronizationContext syncCtx = SynchronizationContext.CurrentNoFlow;
                 if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
                 {
-                    Action moveNextAction = stateMachineBox.MoveNextAction;
-                    if (!AddTaskContinuation(new SynchronizationContextAwaitTaskContinuation(syncCtx, moveNextAction, flowExecutionContext: false), addBeforeOthers: false))
+                    var tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, stateMachineBox.MoveNextAction, flowExecutionContext: false);
+                    if (!AddTaskContinuation(tc, addBeforeOthers: false))
                     {
-                        AwaitTaskContinuation.UnsafeScheduleAction(moveNextAction, this);
+                        tc.Run(this, canInlineContinuationTask: false);
                     }
                     return;
                 }
@@ -2656,10 +2656,10 @@ namespace System.Threading.Tasks
                     TaskScheduler scheduler = TaskScheduler.InternalCurrent;
                     if (scheduler != null && scheduler != TaskScheduler.Default)
                     {
-                        Action moveNextAction = stateMachineBox.MoveNextAction;
-                        if (!AddTaskContinuation(new TaskSchedulerAwaitTaskContinuation(scheduler, moveNextAction, flowExecutionContext: false), addBeforeOthers: false))
+                        var tc = new TaskSchedulerAwaitTaskContinuation(scheduler, stateMachineBox.MoveNextAction, flowExecutionContext: false);
+                        if (!AddTaskContinuation(tc, addBeforeOthers: false))
                         {
-                            AwaitTaskContinuation.UnsafeScheduleAction(moveNextAction, this);
+                            tc.Run(this, canInlineContinuationTask: false);
                         }
                         return;
                     }