Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 7d72463

Browse files
authored
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
1 parent 83bdd21 commit 7d72463

File tree

3 files changed

+22
-9
lines changed

3 files changed

+22
-9
lines changed

src/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public CancellationTokenSource(int millisecondsDelay)
205205
private void InitializeWithTimer(int millisecondsDelay)
206206
{
207207
_state = NotCanceledState;
208-
_timer = new Timer(s_timerCallback, this, millisecondsDelay, -1);
208+
_timer = new Timer(s_timerCallback, this, millisecondsDelay, -1, flowExecutionContext: false);
209209
}
210210

211211
/// <summary>Communicates a request for cancellation.</summary>
@@ -345,7 +345,7 @@ public void CancelAfter(int millisecondsDelay)
345345
// Initially set to "never go off" because we don't want to take a
346346
// chance on a timer "losing" the initialization and then
347347
// cancelling the token before it (the timer) can be disposed.
348-
Timer newTimer = new Timer(s_timerCallback, this, -1, -1);
348+
Timer newTimer = new Timer(s_timerCallback, this, -1, -1, flowExecutionContext: false);
349349
if (Interlocked.CompareExchange(ref _timer, newTimer, null) != null)
350350
{
351351
// We did not initialize the timer. Dispose the new timer.

src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5419,7 +5419,7 @@ public static Task Delay(int millisecondsDelay, CancellationToken cancellationTo
54195419
// ... and create our timer and make sure that it stays rooted.
54205420
if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout
54215421
{
5422-
promise.Timer = new TimerQueueTimer(state => ((DelayPromise)state).Complete(), promise, (uint)millisecondsDelay, Timeout.UnsignedInfinite);
5422+
promise.Timer = new TimerQueueTimer(state => ((DelayPromise)state).Complete(), promise, (uint)millisecondsDelay, Timeout.UnsignedInfinite, flowExecutionContext: false);
54235423
}
54245424

54255425
// Return the timer proxy task

src/System.Private.CoreLib/src/System/Threading/Timer.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,13 +440,16 @@ internal sealed class TimerQueueTimer : IThreadPoolWorkItem
440440
private volatile WaitHandle m_notifyWhenNoCallbacksRunning;
441441

442442

443-
internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period)
443+
internal TimerQueueTimer(TimerCallback timerCallback, object state, uint dueTime, uint period, bool flowExecutionContext)
444444
{
445445
m_timerCallback = timerCallback;
446446
m_state = state;
447447
m_dueTime = Timeout.UnsignedInfinite;
448448
m_period = Timeout.UnsignedInfinite;
449-
m_executionContext = ExecutionContext.Capture();
449+
if (flowExecutionContext)
450+
{
451+
m_executionContext = ExecutionContext.Capture();
452+
}
450453
m_associatedTimerQueue = TimerQueue.Instances[RuntimeThread.GetCurrentProcessorId() % TimerQueue.Instances.Length];
451454

452455
//
@@ -677,14 +680,23 @@ public sealed class Timer : MarshalByRefObject, IDisposable
677680
public Timer(TimerCallback callback,
678681
object state,
679682
int dueTime,
680-
int period)
683+
int period) :
684+
this(callback, state, dueTime, period, flowExecutionContext: true)
685+
{
686+
}
687+
688+
internal Timer(TimerCallback callback,
689+
object state,
690+
int dueTime,
691+
int period,
692+
bool flowExecutionContext)
681693
{
682694
if (dueTime < -1)
683695
throw new ArgumentOutOfRangeException(nameof(dueTime), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
684696
if (period < -1)
685697
throw new ArgumentOutOfRangeException(nameof(period), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
686698

687-
TimerSetup(callback, state, (uint)dueTime, (uint)period);
699+
TimerSetup(callback, state, (uint)dueTime, (uint)period, flowExecutionContext);
688700
}
689701

690702
public Timer(TimerCallback callback,
@@ -745,12 +757,13 @@ public Timer(TimerCallback callback)
745757
private void TimerSetup(TimerCallback callback,
746758
object state,
747759
uint dueTime,
748-
uint period)
760+
uint period,
761+
bool flowExecutionContext = true)
749762
{
750763
if (callback == null)
751764
throw new ArgumentNullException(nameof(TimerCallback));
752765

753-
m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period));
766+
m_timer = new TimerHolder(new TimerQueueTimer(callback, state, dueTime, period, flowExecutionContext));
754767
}
755768

756769
public bool Change(int dueTime, int period)

0 commit comments

Comments
 (0)