From c55f023f542e63e93a300752432de7bcc4104b3b Mon Sep 17 00:00:00 2001 From: Koundinya Veluri Date: Wed, 26 Apr 2017 16:08:53 -0700 Subject: Fix timer callback time drift (#11220) Fixes #6408: - The change to S.T.Timer is the actual fix, but fixed the timer firing on the thread pool timer thread similarly as well --- src/mscorlib/src/System/Threading/Timer.cs | 14 +++++++++++++- src/vm/win32threadpool.cpp | 25 ++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/mscorlib/src/System/Threading/Timer.cs b/src/mscorlib/src/System/Threading/Timer.cs index 960f815d64..a5c7945864 100644 --- a/src/mscorlib/src/System/Threading/Timer.cs +++ b/src/mscorlib/src/System/Threading/Timer.cs @@ -265,7 +265,19 @@ namespace System.Threading if (timer.m_period != Timeout.UnsignedInfinite) { timer.m_startTicks = nowTicks; - timer.m_dueTime = timer.m_period; + uint elapsedForNextDueTime = elapsed - timer.m_dueTime; + if (elapsedForNextDueTime < timer.m_period) + { + // Discount the extra amount of time that has elapsed since the previous firing time to + // prevent timer ticks from drifting + timer.m_dueTime = timer.m_period - elapsedForNextDueTime; + } + else + { + // Enough time has elapsed to fire the timer yet again. The timer is not able to keep up + // with the short period, have it fire 1 ms from now to avoid spinning without a delay. + timer.m_dueTime = 1; + } // // This is a repeating timer; schedule it to run again. diff --git a/src/vm/win32threadpool.cpp b/src/vm/win32threadpool.cpp index bc84762b06..a79656e745 100644 --- a/src/vm/win32threadpool.cpp +++ b/src/vm/win32threadpool.cpp @@ -4755,15 +4755,30 @@ DWORD ThreadpoolMgr::FireTimers() timerInfo, QUEUE_ONLY /* TimerInfo take care of deleting*/); - timerInfo->FiringTime = currentTime+timerInfo->Period; + if (timerInfo->Period != 0 && timerInfo->Period != (ULONG)-1) + { + ULONG nextFiringTime = timerInfo->FiringTime + timerInfo->Period; + DWORD firingInterval; + if (TimeExpired(timerInfo->FiringTime, currentTime, nextFiringTime)) + { + // Enough time has elapsed to fire the timer yet again. The timer is not able to keep up with the short + // period, have it fire 1 ms from now to avoid spinning without a delay. + timerInfo->FiringTime = currentTime + 1; + firingInterval = 1; + } + else + { + timerInfo->FiringTime = nextFiringTime; + firingInterval = TimeInterval(nextFiringTime, currentTime); + } - if ((timerInfo->Period != 0) && (timerInfo->Period != (ULONG) -1) && (nextFiringInterval > timerInfo->Period)) - nextFiringInterval = timerInfo->Period; + if (firingInterval < nextFiringInterval) + nextFiringInterval = firingInterval; + } } - else { - DWORD firingInterval = TimeInterval(timerInfo->FiringTime,currentTime); + DWORD firingInterval = TimeInterval(timerInfo->FiringTime, currentTime); if (firingInterval < nextFiringInterval) nextFiringInterval = firingInterval; } -- cgit v1.2.3