Fix Thread-related breaking changes
authorStephen Toub <stoub@microsoft.com>
Wed, 27 Feb 2019 19:51:50 +0000 (14:51 -0500)
committerStephen Toub <stoub@microsoft.com>
Wed, 27 Feb 2019 19:51:50 +0000 (14:51 -0500)
- Thread.ExecutionContext needs to return ExecutionContext.Capture() to public callers.
- Fix restoring of culture to be inside of the ExecutionContext callback where applicable

Commit migrated from https://github.com/dotnet/coreclr/commit/3042c90aa8ed4bfe56b6f6bc21c4bd93545ee239

src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs
src/libraries/System.Private.CoreLib/src/System/Threading/ThreadPool.cs

index 7efa1b2..a5e8f2a 100644 (file)
@@ -29,11 +29,14 @@ namespace System.Threading
             _executionContext = ec;
         }
 
-        internal static ContextCallback _ccb = new ContextCallback(ThreadStart_Context);
+        internal static readonly ContextCallback s_threadStartContextCallback = new ContextCallback(ThreadStart_Context);
 
         private static void ThreadStart_Context(object state)
         {
             ThreadHelper t = (ThreadHelper)state;
+
+            t.InitializeCulture();
+
             if (t._start is ThreadStart)
             {
                 ((ThreadStart)t._start)();
@@ -63,16 +66,15 @@ namespace System.Threading
         internal void ThreadStart(object obj)
         {
             _startArg = obj;
-
-            InitializeCulture();
-
+            
             ExecutionContext context = _executionContext;
             if (context != null)
             {
-                ExecutionContext.RunInternal(context, _ccb, (object)this);
+                ExecutionContext.RunInternal(context, s_threadStartContextCallback, this);
             }
             else
             {
+                InitializeCulture();
                 ((ParameterizedThreadStart)_start)(obj);
             }
         }
@@ -80,15 +82,14 @@ namespace System.Threading
         // call back helper
         internal void ThreadStart()
         {
-            InitializeCulture();
-
             ExecutionContext context = _executionContext;
             if (context != null)
             {
-                ExecutionContext.RunInternal(context, _ccb, (object)this);
+                ExecutionContext.RunInternal(context, s_threadStartContextCallback, this);
             }
             else
             {
+                InitializeCulture();
                 ((ThreadStart)_start)();
             }
         }
@@ -111,7 +112,7 @@ namespace System.Threading
         ** ThreadBaseObject to maintain alignment between the two classes.
         ** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h
         =========================================================================*/
-        private ExecutionContext m_ExecutionContext;    // this call context follows the logical thread
+        internal ExecutionContext m_ExecutionContext;    // this call context follows the logical thread
         private SynchronizationContext m_SynchronizationContext;    // On CoreCLR, this is maintained separately from ExecutionContext
 
         private string m_Name;
@@ -254,11 +255,7 @@ namespace System.Threading
             }
         }
 
-        public ExecutionContext ExecutionContext
-        {
-            get { return m_ExecutionContext; }
-            internal set { m_ExecutionContext = value; }
-        }
+        public ExecutionContext ExecutionContext => ExecutionContext.Capture();
 
         internal SynchronizationContext SynchronizationContext
         {
index 6f38bdb..b760a56 100644 (file)
@@ -1011,7 +1011,7 @@ namespace System.Runtime.CompilerServices
             // Capture references to Thread Contexts
             Thread currentThread0 = Thread.CurrentThread;
             Thread currentThread = currentThread0;
-            ExecutionContext previousExecutionCtx0 = currentThread0.ExecutionContext;
+            ExecutionContext previousExecutionCtx0 = currentThread0.m_ExecutionContext;
 
             // Store current ExecutionContext and SynchronizationContext as "previousXxx".
             // This allows us to restore them and undo any Context changes made in stateMachine.MoveNext
@@ -1036,7 +1036,7 @@ namespace System.Runtime.CompilerServices
                 }
 
                 ExecutionContext previousExecutionCtx1 = previousExecutionCtx;
-                ExecutionContext currentExecutionCtx1 = currentThread1.ExecutionContext;
+                ExecutionContext currentExecutionCtx1 = currentThread1.m_ExecutionContext;
                 if (previousExecutionCtx1 != currentExecutionCtx1)
                 {
                     ExecutionContext.RestoreChangedContextToThread(currentThread1, previousExecutionCtx1, currentExecutionCtx1);
index 2529997..f7de8fe 100644 (file)
@@ -54,7 +54,7 @@ namespace System.Threading
 
         public static ExecutionContext Capture()
         {
-            ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext;
+            ExecutionContext executionContext = Thread.CurrentThread.m_ExecutionContext;
             if (executionContext == null)
             {
                 executionContext = Default;
@@ -84,7 +84,7 @@ namespace System.Threading
         public static AsyncFlowControl SuppressFlow()
         {
             Thread currentThread = Thread.CurrentThread;
-            ExecutionContext executionContext = currentThread.ExecutionContext ?? Default;
+            ExecutionContext executionContext = currentThread.m_ExecutionContext ?? Default;
             if (executionContext.m_isFlowSuppressed)
             {
                 throw new InvalidOperationException(SR.InvalidOperation_CannotSupressFlowMultipleTimes);
@@ -92,7 +92,7 @@ namespace System.Threading
 
             executionContext = executionContext.ShallowClone(isFlowSuppressed: true);
             var asyncFlowControl = new AsyncFlowControl();
-            currentThread.ExecutionContext = executionContext;
+            currentThread.m_ExecutionContext = executionContext;
             asyncFlowControl.Initialize(currentThread);
             return asyncFlowControl;
         }
@@ -100,18 +100,18 @@ namespace System.Threading
         public static void RestoreFlow()
         {
             Thread currentThread = Thread.CurrentThread;
-            ExecutionContext executionContext = currentThread.ExecutionContext;
+            ExecutionContext executionContext = currentThread.m_ExecutionContext;
             if (executionContext == null || !executionContext.m_isFlowSuppressed)
             {
                 throw new InvalidOperationException(SR.InvalidOperation_CannotRestoreUnsupressedFlow);
             }
 
-            currentThread.ExecutionContext = executionContext.ShallowClone(isFlowSuppressed: false);
+            currentThread.m_ExecutionContext = executionContext.ShallowClone(isFlowSuppressed: false);
         }
 
         public static bool IsFlowSuppressed()
         {
-            ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext;
+            ExecutionContext executionContext = Thread.CurrentThread.m_ExecutionContext;
             return executionContext != null && executionContext.m_isFlowSuppressed;
         }
 
@@ -140,7 +140,7 @@ namespace System.Threading
             // Capture references to Thread Contexts
             Thread currentThread0 = Thread.CurrentThread;
             Thread currentThread = currentThread0;
-            ExecutionContext previousExecutionCtx0 = currentThread0.ExecutionContext;
+            ExecutionContext previousExecutionCtx0 = currentThread0.m_ExecutionContext;
             if (previousExecutionCtx0 != null && previousExecutionCtx0.m_isDefault)
             {
                 // Default is a null ExecutionContext internally
@@ -189,7 +189,7 @@ namespace System.Threading
             }
 
             ExecutionContext previousExecutionCtx1 = previousExecutionCtx;
-            ExecutionContext currentExecutionCtx1 = currentThread1.ExecutionContext;
+            ExecutionContext currentExecutionCtx1 = currentThread1.m_ExecutionContext;
             if (currentExecutionCtx1 != previousExecutionCtx1)
             {
                 RestoreChangedContextToThread(currentThread1, previousExecutionCtx1, currentExecutionCtx1);
@@ -210,7 +210,7 @@ namespace System.Threading
             // Capture references to Thread Contexts
             Thread currentThread0 = Thread.CurrentThread;
             Thread currentThread = currentThread0;
-            ExecutionContext previousExecutionCtx0 = currentThread0.ExecutionContext;
+            ExecutionContext previousExecutionCtx0 = currentThread0.m_ExecutionContext;
             if (previousExecutionCtx0 != null && previousExecutionCtx0.m_isDefault)
             {
                 // Default is a null ExecutionContext internally
@@ -259,7 +259,7 @@ namespace System.Threading
             }
 
             ExecutionContext previousExecutionCtx1 = previousExecutionCtx;
-            ExecutionContext currentExecutionCtx1 = currentThread1.ExecutionContext;
+            ExecutionContext currentExecutionCtx1 = currentThread1.m_ExecutionContext;
             if (currentExecutionCtx1 != previousExecutionCtx1)
             {
                 RestoreChangedContextToThread(currentThread1, previousExecutionCtx1, currentExecutionCtx1);
@@ -298,7 +298,7 @@ namespace System.Threading
             // Enregister threadPoolThread as it crossed EH, and use enregistered variable
             Thread currentThread = threadPoolThread;
 
-            ExecutionContext currentExecutionCtx = currentThread.ExecutionContext;
+            ExecutionContext currentExecutionCtx = currentThread.m_ExecutionContext;
 
             // Restore changed SynchronizationContext back to Default
             currentThread.SynchronizationContext = null;
@@ -322,7 +322,7 @@ namespace System.Threading
             Debug.Assert(executionContext != null && !executionContext.m_isDefault, "ExecutionContext argument is Default.");
 
             // Restore Non-Default context
-            Thread.CurrentThread.ExecutionContext = executionContext;
+            Thread.CurrentThread.m_ExecutionContext = executionContext;
             if (executionContext.HasChangeNotifications)
             {
                 OnValuesChanged(previousExecutionCtx: null, executionContext);
@@ -339,7 +339,7 @@ namespace System.Threading
             Debug.Assert(contextToRestore != currentContext);
 
             // Restore changed ExecutionContext back to previous
-            currentThread.ExecutionContext = contextToRestore;
+            currentThread.m_ExecutionContext = contextToRestore;
             if ((currentContext != null && currentContext.HasChangeNotifications) ||
                 (contextToRestore != null && contextToRestore.HasChangeNotifications))
             {
@@ -352,11 +352,11 @@ namespace System.Threading
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void ResetThreadPoolThread(Thread currentThread)
         {
-            ExecutionContext currentExecutionCtx = currentThread.ExecutionContext;
+            ExecutionContext currentExecutionCtx = currentThread.m_ExecutionContext;
 
             // Reset to defaults
             currentThread.SynchronizationContext = null;
-            currentThread.ExecutionContext = null;
+            currentThread.m_ExecutionContext = null;
 
             if (currentExecutionCtx != null && currentExecutionCtx.HasChangeNotifications)
             {
@@ -364,7 +364,7 @@ namespace System.Threading
 
                 // Reset to defaults again without change notifications in case the Change handler changed the contexts
                 currentThread.SynchronizationContext = null;
-                currentThread.ExecutionContext = null;
+                currentThread.m_ExecutionContext = null;
             }
         }
 
@@ -372,7 +372,7 @@ namespace System.Threading
         internal static void CheckThreadPoolAndContextsAreDefault()
         {
             Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
-            Debug.Assert(Thread.CurrentThread.ExecutionContext == null, "ThreadPool thread not on Default ExecutionContext.");
+            Debug.Assert(Thread.CurrentThread.m_ExecutionContext == null, "ThreadPool thread not on Default ExecutionContext.");
             Debug.Assert(Thread.CurrentThread.SynchronizationContext == null, "ThreadPool thread not on Default SynchronizationContext.");
         }
 
@@ -470,7 +470,7 @@ namespace System.Threading
 
         internal static object GetLocalValue(IAsyncLocal local)
         {
-            ExecutionContext current = Thread.CurrentThread.ExecutionContext;
+            ExecutionContext current = Thread.CurrentThread.m_ExecutionContext;
             if (current == null)
             {
                 return null;
@@ -482,7 +482,7 @@ namespace System.Threading
 
         internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications)
         {
-            ExecutionContext current = Thread.CurrentThread.ExecutionContext;
+            ExecutionContext current = Thread.CurrentThread.m_ExecutionContext;
 
             object previousValue = null;
             bool hadPreviousValue = false;
@@ -540,7 +540,7 @@ namespace System.Threading
                 }
             }
 
-            Thread.CurrentThread.ExecutionContext = 
+            Thread.CurrentThread.m_ExecutionContext = 
                 (!isFlowSuppressed && AsyncLocalValueMap.IsEmpty(newValues)) ?
                 null : // No values, return to Default context
                 new ExecutionContext(newValues, newChangeNotifications, isFlowSuppressed);
index 017a8e7..2dcb7bb 100644 (file)
@@ -558,7 +558,7 @@ namespace System.Threading
                 Thread currentThread = tl.currentThread;
 
                 // Start on clean ExecutionContext and SynchronizationContext
-                currentThread.ExecutionContext = null;
+                currentThread.m_ExecutionContext = null;
                 currentThread.SynchronizationContext = null;
 
                 //