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

Avoid capturing ExecutionContext into CancellationTokenSource's Timer #18670

Merged
merged 2 commits into from
Jun 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public CancellationTokenSource(int millisecondsDelay)
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>
Expand Down Expand Up @@ -345,7 +345,7 @@ public void CancelAfter(int millisecondsDelay)
// 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5419,7 +5419,7 @@ public static Task Delay(int millisecondsDelay, CancellationToken cancellationTo
// ... 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
Expand Down
25 changes: 19 additions & 6 deletions src/System.Private.CoreLib/src/System/Threading/Timer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,13 +440,16 @@ internal sealed class TimerQueueTimer : IThreadPoolWorkItem
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];

//
Expand Down Expand Up @@ -677,14 +680,23 @@ public sealed class Timer : MarshalByRefObject, IDisposable
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,
Expand Down Expand Up @@ -745,12 +757,13 @@ public Timer(TimerCallback callback)
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)
Expand Down