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

Commit

Permalink
Better handle absolute time
Browse files Browse the repository at this point in the history
  • Loading branch information
stephentoub committed Oct 9, 2018
1 parent a214e68 commit b2bc3db
Showing 1 changed file with 56 additions and 5 deletions.
61 changes: 56 additions & 5 deletions src/System.Private.CoreLib/src/System/Threading/Timer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,11 @@ internal static void AppDomainTimerCallback(int id)

#region Firing timers

// The two lists of timers that are part of this TimerQueue. The conform to a single guarantee:
// The two lists of timers that are part of this TimerQueue. They conform to a single guarantee:
// for any timer A in m_shortTimers, every other timer B in the TimerQueue where
// B.m_dueTime <= A.m_dueTime must also be in m_shortTimers.
// (A.m_startTime + A.m_dueTime) - (B.m_startTime + B.m_dueTime) >= 0
// must also be in m_shortTimers. In other words, if a timer is in m_shortTimers, every timer
// that's going to fire at the same time or earlier must also be in m_shortTimers.
private TimerQueueTimer m_shortTimers;
private TimerQueueTimer m_longTimers;

Expand Down Expand Up @@ -232,6 +234,14 @@ internal static void AppDomainTimerCallback(int id)
// the long-lived timers.
private const int ShortTimersThresholdMilliseconds = 333;

// The lowest absolute firing time of any timer in the long list. This may be updated
// whenever a new timer is added, when an existing timer is updated, or when
// firing timers. It's possible when a long timer is moved to the short list that
// we could recompute this in order to raise it (in case the moved timer was the shorted),
// but doing so is expensive, and the next time the long list is walked, this will be
// updated anyway, so we're ok with having
private int m_minLongListAbsoluteDueTime = int.MaxValue;

private volatile int m_pauseTicks = 0; // Time when Pause was called

// Fire any timers that have expired, and update the native timer to schedule the rest of them.
Expand Down Expand Up @@ -308,6 +318,19 @@ private void FireNextTimers()
{
MoveTimerToCorrectList(timer, shouldNowTargetShortList);
}

// Ensure we track the nearest long list timer. We want to make sure that any
// potential short list timers that have a due time within the threshold but an
// absolute time that's after something in the long list is also marshaled
// to the long list rather than to the short one.
if (!timer.m_short)
{
int absoluteDueTime = (int)(nowTicks + timer.m_dueTime);
if (m_minLongListAbsoluteDueTime - absoluteDueTime > 0)
{
m_minLongListAbsoluteDueTime = absoluteDueTime;
}
}
}
else
{
Expand Down Expand Up @@ -337,10 +360,19 @@ private void FireNextTimers()
nextAppDomainTimerDuration = (uint)remaining;
}

if (remaining < ShortTimersThresholdMilliseconds && !timer.m_short)
if (!timer.m_short && remaining < ShortTimersThresholdMilliseconds)
{
MoveTimerToCorrectList(timer, shortList: true);
}

if (!timer.m_short)
{
int absoluteDueTime = (int)(nowTicks + timer.m_dueTime);
if (m_minLongListAbsoluteDueTime - absoluteDueTime > 0)
{
m_minLongListAbsoluteDueTime = absoluteDueTime;
}
}
}

timer = next;
Expand All @@ -355,6 +387,8 @@ private void FireNextTimers()
{
break;
}

m_minLongListAbsoluteDueTime = int.MaxValue;
timer = m_longTimers;
}
}
Expand All @@ -377,8 +411,20 @@ private void FireNextTimers()

public bool UpdateTimer(TimerQueueTimer timer, uint dueTime, uint period)
{
int nowTicks = TickCount;

// The timer can be put onto the short list if:
// - it's going to fire within the next threshold, AND
// - there's no timer in the long queue that's going to fire at the same time or before it.
// If we put it into the short list and either of those conditions didn't hold,
// we risk having a timer in the long list that'll be missed, when FireNextTimers
// runs and doesn't look at the long list because of the presence of this timer.
bool wasShort = timer.m_short;
timer.m_short = dueTime <= ShortTimersThresholdMilliseconds;
int absoluteDueTime = (int)(nowTicks + dueTime);
bool shorterThanCurrentLongMin = (m_minLongListAbsoluteDueTime - absoluteDueTime) > 0;
timer.m_short =
shorterThanCurrentLongMin &&
dueTime <= ShortTimersThresholdMilliseconds;

if (timer.m_dueTime == Timeout.UnsignedInfinite)
{
Expand All @@ -393,9 +439,14 @@ public bool UpdateTimer(TimerQueueTimer timer, uint dueTime, uint period)
LinkTimer(timer);
}

if (!timer.m_short && shorterThanCurrentLongMin)
{
m_minLongListAbsoluteDueTime = absoluteDueTime;
}

timer.m_dueTime = dueTime;
timer.m_period = (period == 0) ? Timeout.UnsignedInfinite : period;
timer.m_startTicks = TickCount;
timer.m_startTicks = nowTicks;
return EnsureAppDomainTimerFiresBy(dueTime);
}

Expand Down

0 comments on commit b2bc3db

Please sign in to comment.