Avoid capturing ExecutionContext into CancellationTokenSource's Timer (dotnet/coreclr...
authorStephen Toub <stoub@microsoft.com>
Thu, 28 Jun 2018 14:26:21 +0000 (10:26 -0400)
committerGitHub <noreply@github.com>
Thu, 28 Jun 2018 14:26:21 +0000 (10:26 -0400)
* Avoid capturing ExecutionContext into CancellationTokenSource's Timer

It's not needed, and it can keep unrelated state alive unnecessarily

* Address PR feedback

Commit migrated from https://github.com/dotnet/coreclr/commit/7d72463b1107cc6f264fcbdc06e3c4df0d9ed668

src/coreclr/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs
src/coreclr/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
src/coreclr/src/System.Private.CoreLib/src/System/Threading/Timer.cs

index 7f41d25..d38ffc9 100644 (file)
@@ -205,7 +205,7 @@ namespace System.Threading
         private void InitializeWithTimer(int millisecondsDelay)
         {
             _state = NotCanceledState;
-            _timer = new Timer(s_timerCallback, this, millisecondsDelay, -1);
+            _timer = new Timer(s_timerCallback, this, millisecondsDelay, -1, flowExecutionContext: false);
         }
 
         /// <summary>Communicates a request for cancellation.</summary>
@@ -345,7 +345,7 @@ namespace System.Threading
                 // Initially set to "never go off" because we don't want to take a
                 // chance on a timer "losing" the initialization and then
                 // cancelling the token before it (the timer) can be disposed.
-                Timer newTimer = new Timer(s_timerCallback, this, -1, -1);
+                Timer newTimer = new Timer(s_timerCallback, this, -1, -1, flowExecutionContext: false);
                 if (Interlocked.CompareExchange(ref _timer, newTimer, null) != null)
                 {
                     // We did not initialize the timer.  Dispose the new timer.
index eb02270..a8926bb 100644 (file)
@@ -5419,7 +5419,7 @@ namespace System.Threading.Tasks
             // ... and create our timer and make sure that it stays rooted.
             if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout
             {
-                promise.Timer = new TimerQueueTimer(state => ((DelayPromise)state).Complete(), promise, (uint)millisecondsDelay, Timeout.UnsignedInfinite);
+                promise.Timer = new TimerQueueTimer(state => ((DelayPromise)state).Complete(), promise, (uint)millisecondsDelay, Timeout.UnsignedInfinite, flowExecutionContext: false);
             }
 
             // Return the timer proxy task
index 4d5c1c7..d9aa01e 100644 (file)
@@ -440,13 +440,16 @@ namespace System.Threading
         private volatile WaitHandle m_notifyWhenNoCallbacksRunning;
 
 
-        internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period)
+        internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period, bool flowExecutionContext)
         {
             m_timerCallback = timerCallback;
             m_state = state;
             m_dueTime = Timeout.UnsignedInfinite;
             m_period = Timeout.UnsignedInfinite;
-            m_executionContext = ExecutionContext.Capture();
+            if (flowExecutionContext)
+            {
+                m_executionContext = ExecutionContext.Capture();
+            }
             m_associatedTimerQueue = TimerQueue.Instances[RuntimeThread.GetCurrentProcessorId() % TimerQueue.Instances.Length];
 
             //
@@ -677,14 +680,23 @@ namespace System.Threading
         public Timer(TimerCallback callback,
                      object state,
                      int dueTime,
-                     int period)
+                     int period) :
+                     this(callback, state, dueTime, period, flowExecutionContext: true)
+        {
+        }
+
+        internal Timer(TimerCallback callback,
+                       object state,
+                       int dueTime,
+                       int period,
+                       bool flowExecutionContext)
         {
             if (dueTime < -1)
                 throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
             if (period < -1)
                 throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
 
-            TimerSetup(callback, state, (uint)dueTime, (uint)period);
+            TimerSetup(callback, state, (uint)dueTime, (uint)period, flowExecutionContext);
         }
 
         public Timer(TimerCallback callback,
@@ -745,12 +757,13 @@ namespace System.Threading
         private void TimerSetup(TimerCallback callback,
                                 object state,
                                 uint dueTime,
-                                uint period)
+                                uint period,
+                                bool flowExecutionContext = true)
         {
             if (callback == null)
                 throw new ArgumentNullException(nameof(TimerCallback));
 
-            m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period));
+            m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period, flowExecutionContext));
         }
 
         public bool Change(int dueTime, int period)