From 8201c100be8adf990b4fc53b4e80bff862456ec0 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Thu, 25 Mar 2021 17:23:57 -0400 Subject: [PATCH 1/5] [runloop] Set up some symbols for generic code. CFRunLoop has a recurring abstraction for platform-specific code, but it is a little leaky. Plug these leaks: ensure `MACH_PORT_NULL` in the generic, non-platform context is rewritten as `CFPORT_NULL`, and ensure that `kern_return_t` and `KERN_SUCCESS` are defined properly. --- CoreFoundation/RunLoop.subproj/CFRunLoop.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/CoreFoundation/RunLoop.subproj/CFRunLoop.c b/CoreFoundation/RunLoop.subproj/CFRunLoop.c index 34242a9155..a2db020bc5 100644 --- a/CoreFoundation/RunLoop.subproj/CFRunLoop.c +++ b/CoreFoundation/RunLoop.subproj/CFRunLoop.c @@ -161,6 +161,8 @@ typedef int kern_return_t; #else static _CFThreadRef const kNilPthreadT = (_CFThreadRef)0; +typedef int kern_return_t; +#define KERN_SUCCESS 0 #define pthreadPointer(a) a #define lockCount(a) a #endif @@ -654,7 +656,7 @@ static void __CFRunLoopModeDeallocate(CFTypeRef cf) { dispatch_release(rlm->_queue); } #endif - if (MACH_PORT_NULL != rlm->_timerPort) mk_timer_destroy(rlm->_timerPort); + if (CFPORT_NULL != rlm->_timerPort) mk_timer_destroy(rlm->_timerPort); _CFRecursiveMutexDestroy(&rlm->_lock); memset((char *)cf + sizeof(CFRuntimeBase), 0x7C, sizeof(struct __CFRunLoopMode) - sizeof(CFRuntimeBase)); } @@ -842,8 +844,8 @@ static CFRunLoopModeRef __CFRunLoopCopyMode(CFRunLoopRef rl, CFStringRef modeNam #if USE_DISPATCH_SOURCE_FOR_TIMERS rlm->_timerFired = false; rlm->_queue = _dispatch_runloop_root_queue_create_4CF("Run Loop Mode Queue", 0); - mach_port_t queuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue); - if (queuePort == MACH_PORT_NULL) CRASH("*** Unable to create run loop mode queue port. (%d) ***", -1); + __CFPort queuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue); + if (queuePort == CFPORT_NULL) CRASH("*** Unable to create run loop mode queue port. (%d) ***", -1); rlm->_timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, rlm->_queue); __block Boolean *timerFiredPointer = &(rlm->_timerFired); @@ -860,7 +862,7 @@ static CFRunLoopModeRef __CFRunLoopCopyMode(CFRunLoopRef rl, CFStringRef modeNam #endif rlm->_timerPort = mk_timer_create(); - if (rlm->_timerPort == MACH_PORT_NULL) { + if (rlm->_timerPort == CFPORT_NULL) { CRASH("*** Unable to create timer Port (%d) ***", rlm->_timerPort); } ret = __CFPortSetInsert(rlm->_timerPort, rlm->_portSet); @@ -2737,7 +2739,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter Boolean poll = sourceHandledThisLoop || (0ULL == termTSR); #if __HAS_DISPATCH__ - if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { + if (CFPORT_NULL != dispatchPort && !didDispatchPortLastTime) { #if TARGET_OS_MAC msg = (mach_msg_header_t *)msg_buffer; if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL, rl, rlm)) { @@ -2867,7 +2869,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter #endif - if (MACH_PORT_NULL == livePort) { + if (CFPORT_NULL == livePort) { CFRUNLOOP_WAKEUP_FOR_NOTHING(); cf_trace(KDEBUG_EVENT_CFRL_DID_WAKEUP_FOR_NOTHING, rl, rlm, livePort, 0); // handle nothing @@ -2890,7 +2892,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter } } #endif - else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { + else if (rlm->_timerPort != CFPORT_NULL && livePort == rlm->_timerPort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); // 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. // 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 From 386e107cc02f8dd863aedda38595f62adeaebf6a Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Thu, 25 Mar 2021 17:43:04 -0400 Subject: [PATCH 2/5] [runloop] Remind porter these stubs are required. New platforms must define these types and functions; it is incorrect to not have #else cases here. --- CoreFoundation/RunLoop.subproj/CFRunLoop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CoreFoundation/RunLoop.subproj/CFRunLoop.c b/CoreFoundation/RunLoop.subproj/CFRunLoop.c index a2db020bc5..b6d3a53b9c 100644 --- a/CoreFoundation/RunLoop.subproj/CFRunLoop.c +++ b/CoreFoundation/RunLoop.subproj/CFRunLoop.c @@ -446,6 +446,8 @@ CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { CF_INLINE void __CFPortSetFree(__CFPortSet portSet) { close(portSet); } +#else +#error "CFPort* stubs for this platform must be implemented #endif #if !defined(__MACTYPES__) && !defined(_OS_OSTYPES_H) @@ -555,6 +557,8 @@ static kern_return_t mk_timer_cancel(HANDLE name, AbsoluteTime *result_time) { } return (int)res; } +#else +#error "mk_timer_* stubs for this platform must be implemented" #endif From ca0f193c0a77b175ce39b2b626a95c15e7df6eb1 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Thu, 25 Mar 2021 19:16:22 -0400 Subject: [PATCH 3/5] [runloop] Implement runloop abstraction for BSD. --- CoreFoundation/RunLoop.subproj/CFRunLoop.c | 297 ++++++++++++++++++++- Sources/Foundation/RunLoop.swift | 4 +- 2 files changed, 296 insertions(+), 5 deletions(-) diff --git a/CoreFoundation/RunLoop.subproj/CFRunLoop.c b/CoreFoundation/RunLoop.subproj/CFRunLoop.c index b6d3a53b9c..6ca5284580 100644 --- a/CoreFoundation/RunLoop.subproj/CFRunLoop.c +++ b/CoreFoundation/RunLoop.subproj/CFRunLoop.c @@ -72,6 +72,8 @@ typedef mach_port_t dispatch_runloop_handle_t; typedef int dispatch_runloop_handle_t; #elif TARGET_OS_WIN32 typedef HANDLE dispatch_runloop_handle_t; +#else +typedef uint64_t dispatch_runloop_handle_t; #endif #if TARGET_OS_MAC @@ -113,9 +115,12 @@ DISPATCH_EXPORT void _dispatch_main_queue_callback_4CF(void * _Null_unspecified) dispatch_runloop_handle_t _dispatch_get_main_queue_port_4CF(void); extern void _dispatch_main_queue_callback_4CF(void *_Null_unspecified msg); +#else +dispatch_runloop_handle_t _dispatch_get_main_queue_port_4CF(void); +extern void _dispatch_main_queue_callback_4CF(void *_Null_unspecified msg); #endif -#if TARGET_OS_WIN32 || TARGET_OS_LINUX +#if TARGET_OS_WIN32 || TARGET_OS_LINUX || TARGET_OS_BSD CF_EXPORT _CFThreadRef _CF_pthread_main_thread_np(void); #define pthread_main_thread_np() _CF_pthread_main_thread_np() #endif @@ -446,6 +451,267 @@ CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { CF_INLINE void __CFPortSetFree(__CFPortSet portSet) { close(portSet); } +#elif TARGET_OS_BSD + +#include +#include +#include +#include + +typedef uint64_t __CFPort; +#define CFPORT_NULL ((__CFPort)-1) + +// _dispatch_get_main_queue_port_4CF is a uint64_t, i.e., a __CFPort. +// That is, we can't use one type for the queue handle in Dispatch and a +// different type for __CFPort in CF. +#define __CFPORT_PACK(rfd, wfd) (((uint64_t)(rfd) << 32) | ((uint32_t)(wfd))) +#define __CFPORT_UNPACK_W(port) ((uint32_t)((port) & 0xffffffff)) +#define __CFPORT_UNPACK_R(port) ((uint32_t)((port) >> 32)) + +typedef struct ___CFPortSet { + int kq; +} *__CFPortSet; +#define CFPORTSET_NULL NULL + +#define TIMEOUT_INFINITY UINT64_MAX + +// Timers are not pipes; they are kevents on a parent kqueue. +// We must flag these to differentiate them from pipes, but we have +// to pack the (kqueue, timer ident) pair like a __CFPort. +#define __CFPORT_TIMER_PACK(ident, kq) \ + ((1ULL << 63) | ((uint64_t)(ident) << 32) | ((uint32_t)(kq))) +#define __CFPORT_IS_TIMER(port) ((port) & (1ULL << 63)) + +static __CFPort __CFPortAllocate(__unused uintptr_t guard) { + __CFPort port; + int fds[2]; + int r = pipe2(fds, O_CLOEXEC | O_NONBLOCK); + if (r == -1) { + return CFPORT_NULL; + } + + uint32_t rfd = (uint32_t)fds[0], wfd = (uint32_t)fds[1]; + port = __CFPORT_PACK(rfd, wfd); + + if (__CFPORT_IS_TIMER(port)) { + // This port is not distinguishable from a flagged packed timer. + close((int)(__CFPORT_UNPACK_W(port))); + close((int)(__CFPORT_UNPACK_R(port))); + return CFPORT_NULL; + } + + return port; +} + +static void __CFPortTrigger(__CFPort port) { + int wfd = (int)__CFPORT_UNPACK_W(port); + ssize_t result; + do { + result = write(wfd, "x", 1); + } while (result == -1 && errno == EINTR); +} + +CF_INLINE void __CFPortFree(__CFPort port, __unused uintptr_t guard) { + close((int)(__CFPORT_UNPACK_W(port))); + close((int)(__CFPORT_UNPACK_R(port))); +} + +#define __CFPORT_TIMER_UNPACK_ID(port) (((port) >> 32) & 0x7fffffff) +#define __CFPORT_TIMER_UNPACK_KQ(port) ((port) & 0xffffffff) +#define MAX_TIMERS 16 +uintptr_t ident = 0; + +static __CFPort mk_timer_create(__CFPortSet parent) { + if (ident > MAX_TIMERS) return CFPORT_NULL; + ident++; + + int kq = parent->kq; + __CFPort port = __CFPORT_TIMER_PACK(ident, kq); + + return port; +} + +static kern_return_t mk_timer_arm(__CFPort timer, int64_t expire_tsr) { + uint64_t now = mach_absolute_time(); + uint64_t expire_time = __CFTSRToNanoseconds(expire_tsr); + int64_t duration = 0; + if (now <= expire_time) { + duration = __CFTSRToTimeInterval(expire_time - now) * 1000; + } + + int id = __CFPORT_TIMER_UNPACK_ID(timer); + struct kevent tev; + EV_SET( + &tev, + id, + EVFILT_TIMER, + EV_ADD | EV_ENABLE, + 0, + duration, + (void *)timer); + + int kq = __CFPORT_TIMER_UNPACK_KQ(timer); + int r = kevent(kq, &tev, 1, NULL, 0, NULL); + + return KERN_SUCCESS; +} + +static kern_return_t mk_timer_cancel(__CFPort timer, const void *unused) { + int id = __CFPORT_TIMER_UNPACK_ID(timer); + struct kevent tev; + EV_SET( + &tev, + id, + EVFILT_TIMER, + EV_DISABLE, + 0, + 0, + (void *)timer); + + int kq = __CFPORT_TIMER_UNPACK_KQ(timer); + int r = kevent(kq, &tev, 1, NULL, 0, NULL); + + return KERN_SUCCESS; +} + +static kern_return_t mk_timer_destroy(__CFPort timer) { + int id = __CFPORT_TIMER_UNPACK_ID(timer); + struct kevent tev; + EV_SET( + &tev, + id, + EVFILT_TIMER, + EV_DELETE, + 0, + 0, + (void *)timer); + + int kq = __CFPORT_TIMER_UNPACK_KQ(timer); + int r = kevent(kq, &tev, 1, NULL, 0, NULL); + + ident--; + return KERN_SUCCESS; +} + +CF_INLINE __CFPortSet __CFPortSetAllocate(void) { + struct ___CFPortSet *set = malloc(sizeof(struct ___CFPortSet)); + set->kq = kqueue(); + return set; +} + +CF_INLINE kern_return_t __CFPortSetInsert(__CFPort port, __CFPortSet set) { + if (__CFPORT_IS_TIMER(port)) { + return 0; + } + + struct kevent change; + EV_SET(&change, + __CFPORT_UNPACK_R(port), + EVFILT_READ, + EV_ADD | EV_ENABLE | EV_CLEAR | EV_RECEIPT, + 0, + 0, + (void *)port); + struct timespec timeout = {0, 0}; + int r = kevent(set->kq, &change, 1, NULL, 0, &timeout); + + return 0; +} + +CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet set) { + if (__CFPORT_IS_TIMER(port)) { + return 0; + } + + struct kevent change; + EV_SET(&change, + __CFPORT_UNPACK_R(port), + EVFILT_READ, + EV_DELETE | EV_RECEIPT, + 0, + 0, + (void *)port); + struct timespec timeout = {0, 0}; + int r = kevent(set->kq, &change, 1, NULL, 0, &timeout); + + return 0; +} + +CF_INLINE void __CFPortSetFree(__CFPortSet set) { + close(set->kq); + free(set); +} + +static int __CFPollFileDescriptors(struct pollfd *fds, nfds_t nfds, uint64_t timeout) { + uint64_t elapsed = 0; + uint64_t start = mach_absolute_time(); + int result = 0; + while (1) { + struct timespec ts = {0}; + struct timespec *tsPtr = &ts; + if (timeout == TIMEOUT_INFINITY) { + tsPtr = NULL; + } else if (elapsed < timeout) { + uint64_t delta = timeout - elapsed; + ts.tv_sec = delta / 1000000000UL; + ts.tv_nsec = delta % 1000000000UL; + } + + result = ppoll(fds, 1, tsPtr, NULL); + + if (result == -1 && errno == EINTR) { + uint64_t end = mach_absolute_time(); + elapsed += (end - start); + start = end; + } else { + return result; + } + } +} + +static Boolean __CFRunLoopServiceFileDescriptors(__CFPortSet set, __CFPort port, uint64_t timeout, __CFPort *livePort) { + __CFPort awokenPort = CFPORT_NULL; + + if (port != CFPORT_NULL) { + int rfd = __CFPORT_UNPACK_R(port); + struct pollfd fdInfo = { + .fd = rfd, + .events = POLLIN, + }; + + ssize_t result = __CFPollFileDescriptors(&fdInfo, 1, timeout); + if (result == 0) + return false; + + awokenPort = port; + } else { + struct kevent awake; + struct timespec timeout = {0, 0}; + + int r = kevent(set->kq, NULL, 0, &awake, 1, &timeout); + + if (r == 0) { + return false; + } + + if (awake.flags == EV_ERROR) { + return false; + } + + if (awake.filter == EVFILT_READ) { + char x; + r = read(awake.ident, &x, 1); + } + + awokenPort = (__CFPort)awake.udata; + } + + if (livePort) + *livePort = awokenPort; + + return true; +} + #else #error "CFPort* stubs for this platform must be implemented #endif @@ -557,6 +823,11 @@ static kern_return_t mk_timer_cancel(HANDLE name, AbsoluteTime *result_time) { } return (int)res; } +#elif TARGET_OS_BSD +/* + * This implementation of the mk_timer_* stubs is defined with the + * implementation of the CFPort* stubs. + */ #else #error "mk_timer_* stubs for this platform must be implemented" #endif @@ -863,9 +1134,13 @@ static CFRunLoopModeRef __CFRunLoopCopyMode(CFRunLoopRef rl, CFStringRef modeNam ret = __CFPortSetInsert(queuePort, rlm->_portSet); if (KERN_SUCCESS != ret) CRASH("*** Unable to insert timer port into port set. (%d) ***", ret); - #endif + rlm->_timerPort = CFPORT_NULL; +#if TARGET_OS_BSD + rlm->_timerPort = mk_timer_create(rlm->_portSet); +#else rlm->_timerPort = mk_timer_create(); +#endif if (rlm->_timerPort == CFPORT_NULL) { CRASH("*** Unable to create timer Port (%d) ***", rlm->_timerPort); } @@ -2720,6 +2995,8 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter Boolean windowsMessageReceived = false; #elif TARGET_OS_LINUX int livePort = -1; +#else + __CFPort livePort = CFPORT_NULL; #endif __CFPortSet waitSet = rlm->_portSet; @@ -2757,6 +3034,12 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) { goto handle_msg; } +#elif TARGET_OS_BSD + if (__CFRunLoopServiceFileDescriptors(CFPORTSET_NULL, dispatchPort, 0, &livePort)) { + goto handle_msg; + } +#else +#error "invoking the port select implementation is required" #endif } #endif @@ -2812,6 +3095,10 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived); #elif TARGET_OS_LINUX __CFRunLoopServiceFileDescriptors(waitSet, CFPORT_NULL, poll ? 0 : TIMEOUT_INFINITY, &livePort); +#elif TARGET_OS_BSD + __CFRunLoopServiceFileDescriptors(waitSet, CFPORT_NULL, poll ? 0 : TIMEOUT_INFINITY, &livePort); +#else +#error "invoking the port set select implementation is required" #endif __CFRunLoopLock(rl); @@ -2923,7 +3210,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __CFRunLoopUnlock(rl); _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL); -#if TARGET_OS_WIN32 || TARGET_OS_LINUX +#if TARGET_OS_WIN32 || TARGET_OS_LINUX || TARGET_OS_BSD void *msg = 0; #endif CFRUNLOOP_ARP_BEGIN(NULL) @@ -3119,6 +3406,10 @@ void CFRunLoopWakeUp(CFRunLoopRef rl) { CFAssert1(0 == ret, __kCFLogAssertion, "%s(): Unable to send wake message to eventfd", __PRETTY_FUNCTION__); #elif TARGET_OS_WIN32 SetEvent(rl->_wakeUpPort); +#elif TARGET_OS_BSD + __CFPortTrigger(rl->_wakeUpPort); +#else +#error "required" #endif cf_trace(KDEBUG_EVENT_CFRL_WAKEUP | DBG_FUNC_END, rl, 0, 0, 0); diff --git a/Sources/Foundation/RunLoop.swift b/Sources/Foundation/RunLoop.swift index de87902c94..66dbf209e5 100644 --- a/Sources/Foundation/RunLoop.swift +++ b/Sources/Foundation/RunLoop.swift @@ -7,7 +7,7 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +#if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(OpenBSD) import CoreFoundation #else @_implementationOnly import CoreFoundation @@ -90,7 +90,7 @@ open class RunLoop: NSObject { // On platforms where it's available, getCFRunLoop() can be overridden and we use it below. // Make sure we honor the override -- var currentCFRunLoop will do so on platforms where overrides are available. - #if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + #if os(Linux) || os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(OpenBSD) internal var currentCFRunLoop: CFRunLoop { getCFRunLoop() } @available(*, deprecated, message: "Directly accessing the run loop may cause your code to not become portable in the future.") From 05ceecf295f1086982359027bfe23d13109e330c Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Thu, 25 Mar 2021 19:57:59 -0400 Subject: [PATCH 4/5] [runloop] Ensure timing arithmetic is done in ns. Fixes SR-14288. --- CoreFoundation/RunLoop.subproj/CFRunLoop.c | 29 ++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/CoreFoundation/RunLoop.subproj/CFRunLoop.c b/CoreFoundation/RunLoop.subproj/CFRunLoop.c index 6ca5284580..d06ed6eeb9 100644 --- a/CoreFoundation/RunLoop.subproj/CFRunLoop.c +++ b/CoreFoundation/RunLoop.subproj/CFRunLoop.c @@ -48,6 +48,15 @@ extern void objc_terminate(void); #define USE_DISPATCH_SOURCE_FOR_TIMERS 0 #endif +static inline uint64_t __CFNanosecondsToTSR(uint64_t ns) { +#if TARGET_OS_MAC || TARGET_OS_LINUX + return ns; +#else + CFTimeInterval ti = ns / 1.0E9; + return __CFTimeIntervalToTSR(ti); +#endif +} + #if USE_DISPATCH_SOURCE_FOR_TIMERS #if !TARGET_OS_MAC typedef uint32_t mach_port_t; @@ -2399,10 +2408,10 @@ static void __CFArmNextTimerInMode(CFRunLoopModeRef rlm, CFRunLoopRef rl) { if (nextSoftDeadline < UINT64_MAX && (nextHardDeadline != rlm->_timerHardDeadline || nextSoftDeadline != rlm->_timerSoftDeadline)) { if (CFRUNLOOP_NEXT_TIMER_ARMED_ENABLED()) { - CFRUNLOOP_NEXT_TIMER_ARMED((unsigned long)(nextSoftDeadline - mach_absolute_time())); + CFRUNLOOP_NEXT_TIMER_ARMED((unsigned long)(nextSoftDeadline - __CFNanosecondsToTSR(mach_absolute_time()))); } - cf_trace(KDEBUG_EVENT_CFRL_NEXT_TIMER_ARMED, rl, rlm, (nextSoftDeadline - mach_absolute_time()), 0); + cf_trace(KDEBUG_EVENT_CFRL_NEXT_TIMER_ARMED, rl, rlm, (nextSoftDeadline - __CFNanosecondsToTSR(mach_absolute_time())), 0); #if USE_DISPATCH_SOURCE_FOR_TIMERS // We're going to hand off the range of allowable timer fire date to dispatch and let it fire when appropriate for the system. uint64_t leeway = __CFTSRToNanoseconds(nextHardDeadline - nextSoftDeadline); @@ -2499,7 +2508,7 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo CFRetain(rlt); __CFRunLoopTimerLock(rlt); - if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) { + if (__CFIsValid(rlt) && rlt->_fireTSR <= __CFNanosecondsToTSR(mach_absolute_time()) && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) { void *context_info = NULL; void (*context_release)(const void *) = NULL; if (rlt->_context.retain) { @@ -2576,7 +2585,7 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat"); HALT; } - uint64_t currentTSR = mach_absolute_time(); + uint64_t currentTSR = __CFNanosecondsToTSR(mach_absolute_time()); nextFireTSR = oldFireTSR; while (nextFireTSR <= currentTSR) { nextFireTSR += intervalTSR; @@ -2920,7 +2929,7 @@ static Boolean __CFRunLoopWaitForMultipleObjects(__CFPortSet portSet, HANDLE *on /* rl, rlm are locked on entrance and exit */ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { - uint64_t startTSR = mach_absolute_time(); + uint64_t startTSR = __CFNanosecondsToTSR(mach_absolute_time()); if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); @@ -3177,7 +3186,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); cf_trace(KDEBUG_EVENT_CFRL_DID_WAKEUP_FOR_TIMER, rl, rlm, livePort, 0); - if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { + if (!__CFRunLoopDoTimers(rl, rlm, __CFNanosecondsToTSR(mach_absolute_time()))) { // Re-arm the next timer, because we apparently fired early __CFArmNextTimerInMode(rlm, rl); } @@ -3187,7 +3196,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter CFRUNLOOP_WAKEUP_FOR_TIMER(); // 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. // 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 - if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { + if (!__CFRunLoopDoTimers(rl, rlm, __CFNanosecondsToTSR(mach_absolute_time()))) { // Re-arm the next timer // Since we'll be resetting the same timer as before // with the same deadlines, we need to reset these @@ -3261,7 +3270,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter if (sourceHandledThisLoop && stopAfterHandle) { retVal = kCFRunLoopRunHandledSource; - } else if (termTSR < mach_absolute_time()) { + } else if (termTSR < __CFNanosecondsToTSR(mach_absolute_time())) { retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); @@ -4518,7 +4527,7 @@ CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime memory->_tolerance = 0.0; if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT; memory->_nextFireDate = fireDate; - uint64_t now2 = mach_absolute_time(); + uint64_t now2 = __CFNanosecondsToTSR(mach_absolute_time()); CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); if (fireDate < now1) { memory->_fireTSR = now2; @@ -4578,7 +4587,7 @@ void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDat if (!__CFIsValid(rlt)) return; if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT; uint64_t nextFireTSR = 0ULL; - uint64_t now2 = mach_absolute_time(); + uint64_t now2 = __CFNanosecondsToTSR(mach_absolute_time()); CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); if (fireDate < now1) { nextFireTSR = now2; From 13bc94f270830d0b0a93074ddf9a86380abe723d Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Thu, 25 Mar 2021 20:14:14 -0400 Subject: [PATCH 5/5] [runloop] Avoid pthread_main_np on OpenBSD. During runloop testing, log messages suggest that the runloop thinks the main thread has exited and certain runloop operations don't actually proceed. The flag for denoting when the main thread has exited is set in the thread-specific data destructor `__CFTSDFinalize`. This function detects whether the thread in question is the main thread via `pthread_main_np`, and has been observed in debugging this problem to return 1 on apparently non-main threads on OpenBSD. This obviously will cause the main thread exited flag to be erroneously set when non-main threads complete their work and dispose of thread-specific data. Instead, use the existing `_CFMainPThread` symbol set in `__CFInitialize` to determine the main thread. I am not sure whether the platform `pthread_main_np` is at fault here, but since this workaround makes the tests pass, let's use it. --- CoreFoundation/Base.subproj/CFPlatform.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CoreFoundation/Base.subproj/CFPlatform.c b/CoreFoundation/Base.subproj/CFPlatform.c index c2ce76e800..c90c54bf05 100644 --- a/CoreFoundation/Base.subproj/CFPlatform.c +++ b/CoreFoundation/Base.subproj/CFPlatform.c @@ -249,7 +249,11 @@ const char *_CFProcessPath(void) { #if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_BSD CF_CROSS_PLATFORM_EXPORT Boolean _CFIsMainThread(void) { +#if defined(__OpenBSD__) + return pthread_equal(pthread_self(), _CFMainPThread) != 0; +#else return pthread_main_np() == 1; +#endif } #endif @@ -774,7 +778,7 @@ static void __CFTSDFinalize(void *arg) { #if TARGET_OS_WASI __CFMainThreadHasExited = true; #else - if (pthread_main_np() == 1) { + if (_CFIsMainThread()) { // Important: we need to be sure that the only time we set this flag to true is when we actually can guarentee we ARE the main thread. __CFMainThreadHasExited = true; }