From 7d72463b1107cc6f264fcbdc06e3c4df0d9ed668 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 28 Jun 2018 10:26:21 -0400 Subject: Avoid capturing ExecutionContext into CancellationTokenSource's Timer (#18670) * Avoid capturing ExecutionContext into CancellationTokenSource's Timer It's not needed, and it can keep unrelated state alive unnecessarily * Address PR feedback --- .../System/Threading/CancellationTokenSource.cs | 4 ++-- .../src/System/Threading/Tasks/Task.cs | 2 +- .../src/System/Threading/Timer.cs | 25 ++++++++++++++++------ 3 files changed, 22 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index 7f41d250b6..d38ffc966e 100644 --- a/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -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); } /// Communicates a request for cancellation. @@ -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. diff --git a/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index eb0227014f..a8926bbf7b 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -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 diff --git a/src/System.Private.CoreLib/src/System/Threading/Timer.cs b/src/System.Private.CoreLib/src/System/Threading/Timer.cs index 4d5c1c7fc1..d9aa01e505 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Timer.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Timer.cs @@ -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) -- cgit v1.2.3