Skip to content

Commit 05ceecf

Browse files
committed
[runloop] Ensure timing arithmetic is done in ns.
Fixes SR-14288.
1 parent ca0f193 commit 05ceecf

File tree

1 file changed

+19
-10
lines changed

1 file changed

+19
-10
lines changed

CoreFoundation/RunLoop.subproj/CFRunLoop.c

+19-10
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ extern void objc_terminate(void);
4848
#define USE_DISPATCH_SOURCE_FOR_TIMERS 0
4949
#endif
5050

51+
static inline uint64_t __CFNanosecondsToTSR(uint64_t ns) {
52+
#if TARGET_OS_MAC || TARGET_OS_LINUX
53+
return ns;
54+
#else
55+
CFTimeInterval ti = ns / 1.0E9;
56+
return __CFTimeIntervalToTSR(ti);
57+
#endif
58+
}
59+
5160
#if USE_DISPATCH_SOURCE_FOR_TIMERS
5261
#if !TARGET_OS_MAC
5362
typedef uint32_t mach_port_t;
@@ -2399,10 +2408,10 @@ static void __CFArmNextTimerInMode(CFRunLoopModeRef rlm, CFRunLoopRef rl) {
23992408

24002409
if (nextSoftDeadline < UINT64_MAX && (nextHardDeadline != rlm->_timerHardDeadline || nextSoftDeadline != rlm->_timerSoftDeadline)) {
24012410
if (CFRUNLOOP_NEXT_TIMER_ARMED_ENABLED()) {
2402-
CFRUNLOOP_NEXT_TIMER_ARMED((unsigned long)(nextSoftDeadline - mach_absolute_time()));
2411+
CFRUNLOOP_NEXT_TIMER_ARMED((unsigned long)(nextSoftDeadline - __CFNanosecondsToTSR(mach_absolute_time())));
24032412
}
24042413

2405-
cf_trace(KDEBUG_EVENT_CFRL_NEXT_TIMER_ARMED, rl, rlm, (nextSoftDeadline - mach_absolute_time()), 0);
2414+
cf_trace(KDEBUG_EVENT_CFRL_NEXT_TIMER_ARMED, rl, rlm, (nextSoftDeadline - __CFNanosecondsToTSR(mach_absolute_time())), 0);
24062415
#if USE_DISPATCH_SOURCE_FOR_TIMERS
24072416
// We're going to hand off the range of allowable timer fire date to dispatch and let it fire when appropriate for the system.
24082417
uint64_t leeway = __CFTSRToNanoseconds(nextHardDeadline - nextSoftDeadline);
@@ -2499,7 +2508,7 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo
24992508
CFRetain(rlt);
25002509
__CFRunLoopTimerLock(rlt);
25012510

2502-
if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
2511+
if (__CFIsValid(rlt) && rlt->_fireTSR <= __CFNanosecondsToTSR(mach_absolute_time()) && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
25032512
void *context_info = NULL;
25042513
void (*context_release)(const void *) = NULL;
25052514
if (rlt->_context.retain) {
@@ -2576,7 +2585,7 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo
25762585
CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat");
25772586
HALT;
25782587
}
2579-
uint64_t currentTSR = mach_absolute_time();
2588+
uint64_t currentTSR = __CFNanosecondsToTSR(mach_absolute_time());
25802589
nextFireTSR = oldFireTSR;
25812590
while (nextFireTSR <= currentTSR) {
25822591
nextFireTSR += intervalTSR;
@@ -2920,7 +2929,7 @@ static Boolean __CFRunLoopWaitForMultipleObjects(__CFPortSet portSet, HANDLE *on
29202929

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

29252934
if (__CFRunLoopIsStopped(rl)) {
29262935
__CFRunLoopUnsetStopped(rl);
@@ -3177,7 +3186,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
31773186
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
31783187
CFRUNLOOP_WAKEUP_FOR_TIMER();
31793188
cf_trace(KDEBUG_EVENT_CFRL_DID_WAKEUP_FOR_TIMER, rl, rlm, livePort, 0);
3180-
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
3189+
if (!__CFRunLoopDoTimers(rl, rlm, __CFNanosecondsToTSR(mach_absolute_time()))) {
31813190
// Re-arm the next timer, because we apparently fired early
31823191
__CFArmNextTimerInMode(rlm, rl);
31833192
}
@@ -3187,7 +3196,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
31873196
CFRUNLOOP_WAKEUP_FOR_TIMER();
31883197
// 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.
31893198
// 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
3190-
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
3199+
if (!__CFRunLoopDoTimers(rl, rlm, __CFNanosecondsToTSR(mach_absolute_time()))) {
31913200
// Re-arm the next timer
31923201
// Since we'll be resetting the same timer as before
31933202
// with the same deadlines, we need to reset these
@@ -3261,7 +3270,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
32613270

32623271
if (sourceHandledThisLoop && stopAfterHandle) {
32633272
retVal = kCFRunLoopRunHandledSource;
3264-
} else if (termTSR < mach_absolute_time()) {
3273+
} else if (termTSR < __CFNanosecondsToTSR(mach_absolute_time())) {
32653274
retVal = kCFRunLoopRunTimedOut;
32663275
} else if (__CFRunLoopIsStopped(rl)) {
32673276
__CFRunLoopUnsetStopped(rl);
@@ -4518,7 +4527,7 @@ CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime
45184527
memory->_tolerance = 0.0;
45194528
if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT;
45204529
memory->_nextFireDate = fireDate;
4521-
uint64_t now2 = mach_absolute_time();
4530+
uint64_t now2 = __CFNanosecondsToTSR(mach_absolute_time());
45224531
CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent();
45234532
if (fireDate < now1) {
45244533
memory->_fireTSR = now2;
@@ -4578,7 +4587,7 @@ void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDat
45784587
if (!__CFIsValid(rlt)) return;
45794588
if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT;
45804589
uint64_t nextFireTSR = 0ULL;
4581-
uint64_t now2 = mach_absolute_time();
4590+
uint64_t now2 = __CFNanosecondsToTSR(mach_absolute_time());
45824591
CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent();
45834592
if (fireDate < now1) {
45844593
nextFireTSR = now2;

0 commit comments

Comments
 (0)