From 9f4167504baa1ec693b14932868c6a8473d23180 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 4 Apr 2018 12:14:33 -0700 Subject: [PATCH] Fix ExecutionContext capture in Task (#17407) When Task was refactored in core to remove the defunct StackCrawlMark support, the code that captured ExecutionContext was moved from a helper into TaskConstructorCore. In doing so, though, it was put at the beginning of TaskConstructorCore, even though in its previous location it would have come after the call to TaskConstructorCore. That change of place is causing an assert to fire, validating that m_stateFlags is still 0, which is failing because if ExecutionContext.Capture() returns null due to flow being suppressed, the code that stores the context sets a bit into m_stateFlags (to be able to differentiate null meaning no state flowed and null meaning default). The assert is correct, though, and a small bug did result from this: Task hasn't been entirely respecting ExecutionContext.SuppressFlow (which was added back in .NET Core 2.0), in that it won't flow the current context, but because it would overwrite that bit in m_stateFlags, it would treat a null ExecutionContext as default instead of as flow suppressed, and thus would use ExecutionContext.Default to invoke the Task's delegate. That in turn means that any EC changes made by the Task's delegate wouldn't be visible to the caller, as EC.Run would be used. The fix is simply to move the code to the end of TaskConstructorCore instead of having it at the beginning. --- src/mscorlib/src/System/Threading/Tasks/Task.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs index 6d4ddf8473c3..5fc1d22779f9 100644 --- a/src/mscorlib/src/System/Threading/Tasks/Task.cs +++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs @@ -542,10 +542,6 @@ internal void TaskConstructorCore(Delegate action, object state, CancellationTok m_stateObject = state; m_taskScheduler = scheduler; - Debug.Assert(m_contingentProperties == null || m_contingentProperties.m_capturedContext == null, - "Captured an ExecutionContext when one was already captured."); - CapturedContext = ExecutionContext.Capture(); - // Check for validity of options if ((creationOptions & ~(TaskCreationOptions.AttachedToParent | @@ -602,6 +598,10 @@ internal void TaskConstructorCore(Delegate action, object state, CancellationTok AssignCancellationToken(cancellationToken, null, null); } + + Debug.Assert(m_contingentProperties == null || m_contingentProperties.m_capturedContext == null, + "Captured an ExecutionContext when one was already captured."); + CapturedContext = ExecutionContext.Capture(); } ///