Skip to content

Commit 84c7d00

Browse files
committed
[runloop] Ensure timing arithmetic is done in ns.
Fixes SR-14288.
1 parent 0721f0e commit 84c7d00

File tree

1 file changed

+19
-10
lines changed

1 file changed

+19
-10
lines changed

CoreFoundation/RunLoop.subproj/CFRunLoop.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ extern void objc_terminate(void);
4545
#define USE_DISPATCH_SOURCE_FOR_TIMERS 0
4646
#endif
4747

48+
static inline uint64_t __CFNanosecondsToTSR(uint64_t ns) {
49+
#if TARGET_OS_MAC || TARGET_OS_LINUX
50+
return ns;
51+
#else
52+
CFTimeInterval ti = ns / 1.0E9;
53+
return __CFTimeIntervalToTSR(ti);
54+
#endif
55+
}
56+
4857
#if USE_DISPATCH_SOURCE_FOR_TIMERS
4958
#if !TARGET_OS_MAC
5059
typedef uint32_t mach_port_t;
@@ -2396,10 +2405,10 @@ static void __CFArmNextTimerInMode(CFRunLoopModeRef rlm, CFRunLoopRef rl) {
23962405

23972406
if (nextSoftDeadline < UINT64_MAX && (nextHardDeadline != rlm->_timerHardDeadline || nextSoftDeadline != rlm->_timerSoftDeadline)) {
23982407
if (CFRUNLOOP_NEXT_TIMER_ARMED_ENABLED()) {
2399-
CFRUNLOOP_NEXT_TIMER_ARMED((unsigned long)(nextSoftDeadline - mach_absolute_time()));
2408+
CFRUNLOOP_NEXT_TIMER_ARMED((unsigned long)(nextSoftDeadline - __CFNanosecondsToTSR(mach_absolute_time())));
24002409
}
24012410

2402-
cf_trace(KDEBUG_EVENT_CFRL_NEXT_TIMER_ARMED, rl, rlm, (nextSoftDeadline - mach_absolute_time()), 0);
2411+
cf_trace(KDEBUG_EVENT_CFRL_NEXT_TIMER_ARMED, rl, rlm, (nextSoftDeadline - __CFNanosecondsToTSR(mach_absolute_time())), 0);
24032412
#if USE_DISPATCH_SOURCE_FOR_TIMERS
24042413
// We're going to hand off the range of allowable timer fire date to dispatch and let it fire when appropriate for the system.
24052414
uint64_t leeway = __CFTSRToNanoseconds(nextHardDeadline - nextSoftDeadline);
@@ -2496,7 +2505,7 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo
24962505
CFRetain(rlt);
24972506
__CFRunLoopTimerLock(rlt);
24982507

2499-
if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
2508+
if (__CFIsValid(rlt) && rlt->_fireTSR <= __CFNanosecondsToTSR(mach_absolute_time()) && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
25002509
void *context_info = NULL;
25012510
void (*context_release)(const void *) = NULL;
25022511
if (rlt->_context.retain) {
@@ -2573,7 +2582,7 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo
25732582
CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat");
25742583
HALT;
25752584
}
2576-
uint64_t currentTSR = mach_absolute_time();
2585+
uint64_t currentTSR = __CFNanosecondsToTSR(mach_absolute_time());
25772586
nextFireTSR = oldFireTSR;
25782587
while (nextFireTSR <= currentTSR) {
25792588
nextFireTSR += intervalTSR;
@@ -2917,7 +2926,7 @@ static Boolean __CFRunLoopWaitForMultipleObjects(__CFPortSet portSet, HANDLE *on
29172926

29182927
/* rl, rlm are locked on entrance and exit */
29192928
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
2920-
uint64_t startTSR = mach_absolute_time();
2929+
uint64_t startTSR = __CFNanosecondsToTSR(mach_absolute_time());
29212930

29222931
if (__CFRunLoopIsStopped(rl)) {
29232932
__CFRunLoopUnsetStopped(rl);
@@ -3174,7 +3183,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
31743183
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
31753184
CFRUNLOOP_WAKEUP_FOR_TIMER();
31763185
cf_trace(KDEBUG_EVENT_CFRL_DID_WAKEUP_FOR_TIMER, rl, rlm, livePort, 0);
3177-
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
3186+
if (!__CFRunLoopDoTimers(rl, rlm, __CFNanosecondsToTSR(mach_absolute_time()))) {
31783187
// Re-arm the next timer, because we apparently fired early
31793188
__CFArmNextTimerInMode(rlm, rl);
31803189
}
@@ -3184,7 +3193,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
31843193
CFRUNLOOP_WAKEUP_FOR_TIMER();
31853194
// On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled.
31863195
// In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
3187-
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
3196+
if (!__CFRunLoopDoTimers(rl, rlm, __CFNanosecondsToTSR(mach_absolute_time()))) {
31883197
// Re-arm the next timer
31893198
// Since we'll be resetting the same timer as before
31903199
// with the same deadlines, we need to reset these
@@ -3258,7 +3267,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
32583267

32593268
if (sourceHandledThisLoop && stopAfterHandle) {
32603269
retVal = kCFRunLoopRunHandledSource;
3261-
} else if (termTSR < mach_absolute_time()) {
3270+
} else if (termTSR < __CFNanosecondsToTSR(mach_absolute_time())) {
32623271
retVal = kCFRunLoopRunTimedOut;
32633272
} else if (__CFRunLoopIsStopped(rl)) {
32643273
__CFRunLoopUnsetStopped(rl);
@@ -4515,7 +4524,7 @@ CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime
45154524
memory->_tolerance = 0.0;
45164525
if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT;
45174526
memory->_nextFireDate = fireDate;
4518-
uint64_t now2 = mach_absolute_time();
4527+
uint64_t now2 = __CFNanosecondsToTSR(mach_absolute_time());
45194528
CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent();
45204529
if (fireDate < now1) {
45214530
memory->_fireTSR = now2;
@@ -4575,7 +4584,7 @@ void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDat
45754584
if (!__CFIsValid(rlt)) return;
45764585
if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT;
45774586
uint64_t nextFireTSR = 0ULL;
4578-
uint64_t now2 = mach_absolute_time();
4587+
uint64_t now2 = __CFNanosecondsToTSR(mach_absolute_time());
45794588
CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent();
45804589
if (fireDate < now1) {
45814590
nextFireTSR = now2;

0 commit comments

Comments
 (0)