blob: b92c2bf9b304af6d687d88075c80d7db7cc59554 [file] [log] [blame]
/* CFRunLoop.c
Copyright (c) 1998-2016, Apple Inc. and the Swift project authors
Portions Copyright (c) 2014-2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
Responsibility: Tony Parker
*/
#include <CoreFoundation/CFRunLoop.h>
#include <CoreFoundation/CFSet.h>
#include <CoreFoundation/CFBag.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFPreferences.h>
#include "CFInternal.h"
#include <math.h>
#include <stdio.h>
#include <limits.h>
#include <pthread.h>
#if __HAS_DISPATCH__
#include <dispatch/dispatch.h>
#endif
#if DEPLOYMENT_TARGET_WINDOWS
#include <typeinfo.h>
#endif
#if __has_include(<checkint.h>)
#include <checkint.h>
#else
enum {
CHECKINT_NO_ERROR = 0,
CHECKINT_OVERFLOW_ERROR = (1 << 0),
};
CF_INLINE uint64_t check_uint64_add(uint64_t x, uint64_t y, int32_t* err) {
if((ULLONG_MAX - y) < x)
*err = *err | CHECKINT_OVERFLOW_ERROR;
return x + y;
};
#endif
#if __HAS_DISPATCH__ && __has_include(<dispatch/private.h>)
#include <dispatch/private.h>
#elif __HAS_DISPATCH__
extern dispatch_queue_t _dispatch_runloop_root_queue_create_4CF(const char *_Nullable label, unsigned long flags);
extern mach_port_t _dispatch_runloop_root_queue_get_port_4CF(dispatch_queue_t queue);
extern void _dispatch_source_set_runloop_timer_4CF(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway);
#endif
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#include <sys/param.h>
#include <CoreFoundation/CFUserNotification.h>
#include <mach/mach.h>
#include <mach/clock_types.h>
#include <mach/clock.h>
#include <unistd.h>
#include <dlfcn.h>
extern pthread_t pthread_main_thread_np(void);
typedef struct voucher_s *voucher_t;
extern voucher_t _Nullable voucher_copy(void);
extern void os_release(void *object);
extern mach_port_t _dispatch_get_main_queue_port_4CF(void);
#elif DEPLOYMENT_TARGET_WINDOWS || TARGET_OS_CYGWIN
#include <process.h>
#define DISPATCH_EXPORT extern
DISPATCH_EXPORT HANDLE _dispatch_get_main_queue_handle_4CF(void);
DISPATCH_EXPORT void _dispatch_main_queue_callback_4CF(void);
#define MACH_PORT_NULL 0
#define mach_port_name_t HANDLE
#define mach_port_t HANDLE
#define _dispatch_get_main_queue_port_4CF _dispatch_get_main_queue_handle_4CF
#define _dispatch_main_queue_callback_4CF(x) _dispatch_main_queue_callback_4CF()
#define LARGE_INTEGER uint64_t
#elif DEPLOYMENT_TARGET_LINUX
#if TARGET_OS_CYGWIN
#else
#include <dlfcn.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
dispatch_runloop_handle_t _dispatch_get_main_queue_handle_4CF(void);
extern void _dispatch_main_queue_callback_4CF(void *_Null_unspecified msg);
#define _dispatch_get_main_queue_port_4CF _dispatch_get_main_queue_handle_4CF
#define _dispatch_main_queue_callback_4CF(x) _dispatch_main_queue_callback_4CF(x)
#endif
#endif
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR || DEPLOYMENT_TARGET_LINUX
CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
#define pthread_main_thread_np() _CF_pthread_main_thread_np()
#endif
#include <Block.h>
#if __has_include(<Block_private.h>)
#include <Block_private.h>
#elif __has_include("Block_private.h")
#include "Block_private.h"
#endif
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
#define USE_DISPATCH_SOURCE_FOR_TIMERS __HAS_DISPATCH__
#define USE_MK_TIMER_TOO 1
#else
#define USE_DISPATCH_SOURCE_FOR_TIMERS 0
#define USE_MK_TIMER_TOO 1
#endif
static int _LogCFRunLoop = 0;
static void _runLoopTimerWithBlockContext(CFRunLoopTimerRef timer, void *opaqueBlock);
// for conservative arithmetic safety, such that (TIMER_DATE_LIMIT + TIMER_INTERVAL_LIMIT + kCFAbsoluteTimeIntervalSince1970) * 10^9 < 2^63
#define TIMER_DATE_LIMIT 4039289856.0
#define TIMER_INTERVAL_LIMIT 504911232.0
#define HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY 0
#define CRASH(string, errcode) do { char msg[256]; snprintf(msg, 256, string, errcode); CRSetCrashLogMessage(msg); HALT; } while (0)
#if DEPLOYMENT_TARGET_WINDOWS
static pthread_t kNilPthreadT = { nil, nil };
#define pthreadPointer(a) a.p
typedef int kern_return_t;
#define KERN_SUCCESS 0
#elif DEPLOYMENT_TARGET_LINUX
static pthread_t kNilPthreadT = (pthread_t)0;
#define pthreadPointer(a) ((void*)a)
typedef int kern_return_t;
#define KERN_SUCCESS 0
#else
static pthread_t kNilPthreadT = (pthread_t)0;
#define pthreadPointer(a) a
#define lockCount(a) a
#endif
#pragma mark -
#define CF_RUN_LOOP_PROBES 0
#if CF_RUN_LOOP_PROBES
#include "CFRunLoopProbes.h"
#else
#define CFRUNLOOP_NEXT_TIMER_ARMED(arg0) do { } while (0)
#define CFRUNLOOP_NEXT_TIMER_ARMED_ENABLED() (0)
#define CFRUNLOOP_POLL() do { } while (0)
#define CFRUNLOOP_POLL_ENABLED() (0)
#define CFRUNLOOP_SLEEP() do { } while (0)
#define CFRUNLOOP_SLEEP_ENABLED() (0)
#define CFRUNLOOP_SOURCE_FIRED(arg0, arg1, arg2) do { } while (0)
#define CFRUNLOOP_SOURCE_FIRED_ENABLED() (0)
#define CFRUNLOOP_TIMER_CREATED(arg0, arg1, arg2, arg3, arg4, arg5, arg6) do { } while (0)
#define CFRUNLOOP_TIMER_CREATED_ENABLED() (0)
#define CFRUNLOOP_TIMER_FIRED(arg0, arg1, arg2, arg3, arg4) do { } while (0)
#define CFRUNLOOP_TIMER_FIRED_ENABLED() (0)
#define CFRUNLOOP_TIMER_RESCHEDULED(arg0, arg1, arg2, arg3, arg4, arg5) do { } while (0)
#define CFRUNLOOP_TIMER_RESCHEDULED_ENABLED() (0)
#define CFRUNLOOP_WAKEUP(arg0) do { } while (0)
#define CFRUNLOOP_WAKEUP_ENABLED() (0)
#define CFRUNLOOP_WAKEUP_FOR_DISPATCH() do { } while (0)
#define CFRUNLOOP_WAKEUP_FOR_DISPATCH_ENABLED() (0)
#define CFRUNLOOP_WAKEUP_FOR_NOTHING() do { } while (0)
#define CFRUNLOOP_WAKEUP_FOR_NOTHING_ENABLED() (0)
#define CFRUNLOOP_WAKEUP_FOR_SOURCE() do { } while (0)
#define CFRUNLOOP_WAKEUP_FOR_SOURCE_ENABLED() (0)
#define CFRUNLOOP_WAKEUP_FOR_TIMEOUT() do { } while (0)
#define CFRUNLOOP_WAKEUP_FOR_TIMEOUT_ENABLED() (0)
#define CFRUNLOOP_WAKEUP_FOR_TIMER() do { } while (0)
#define CFRUNLOOP_WAKEUP_FOR_TIMER_ENABLED() (0)
#define CFRUNLOOP_WAKEUP_FOR_WAKEUP() do { } while (0)
#define CFRUNLOOP_WAKEUP_FOR_WAKEUP_ENABLED() (0)
#endif
// In order to reuse most of the code across Mach and Windows v1 RunLoopSources, we define a
// simple abstraction layer spanning Mach ports and Windows HANDLES
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
CF_PRIVATE uint32_t __CFGetProcessPortCount(void) {
ipc_info_space_t info;
ipc_info_name_array_t table = 0;
mach_msg_type_number_t tableCount = 0;
ipc_info_tree_name_array_t tree = 0;
mach_msg_type_number_t treeCount = 0;
kern_return_t ret = mach_port_space_info(mach_task_self(), &info, &table, &tableCount, &tree, &treeCount);
if (ret != KERN_SUCCESS) {
return (uint32_t)0;
}
if (table != NULL) {
ret = vm_deallocate(mach_task_self(), (vm_address_t)table, tableCount * sizeof(*table));
}
if (tree != NULL) {
ret = vm_deallocate(mach_task_self(), (vm_address_t)tree, treeCount * sizeof(*tree));
}
return (uint32_t)tableCount;
}
CF_PRIVATE CFArrayRef __CFStopAllThreads(void) {
CFMutableArrayRef suspended_list = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
mach_port_t my_task = mach_task_self();
mach_port_t my_thread = mach_thread_self();
thread_act_array_t thr_list = 0;
mach_msg_type_number_t thr_cnt = 0;
// really, should loop doing the stopping until no more threads get added to the list N times in a row
kern_return_t ret = task_threads(my_task, &thr_list, &thr_cnt);
if (ret == KERN_SUCCESS) {
for (CFIndex idx = 0; idx < thr_cnt; idx++) {
thread_act_t thread = thr_list[idx];
if (thread == my_thread) continue;
if (CFArrayContainsValue(suspended_list, CFRangeMake(0, CFArrayGetCount(suspended_list)), (const void *)(uintptr_t)thread)) continue;
ret = thread_suspend(thread);
if (ret == KERN_SUCCESS) {
CFArrayAppendValue(suspended_list, (const void *)(uintptr_t)thread);
} else {
mach_port_deallocate(my_task, thread);
}
}
vm_deallocate(my_task, (vm_address_t)thr_list, sizeof(thread_t) * thr_cnt);
}
mach_port_deallocate(my_task, my_thread);
return suspended_list;
}
CF_PRIVATE void __CFRestartAllThreads(CFArrayRef threads) {
for (CFIndex idx = 0; idx < CFArrayGetCount(threads); idx++) {
thread_act_t thread = (thread_act_t)(uintptr_t)CFArrayGetValueAtIndex(threads, idx);
kern_return_t ret = thread_resume(thread);
if (ret != KERN_SUCCESS) CRASH("*** Failure from thread_resume (%x) ***", ret);
mach_port_deallocate(mach_task_self(), thread);
}
}
static uint32_t __CF_last_warned_port_count = 0;
static void foo() __attribute__((unused));
static void foo() {
uint32_t pcnt = __CFGetProcessPortCount();
if (__CF_last_warned_port_count + 1000 < pcnt) {
CFArrayRef threads = __CFStopAllThreads();
// do stuff here
CFOptionFlags responseFlags = 0;
SInt32 result = CFUserNotificationDisplayAlert(0.0, kCFUserNotificationCautionAlertLevel, NULL, NULL, NULL, CFSTR("High Mach Port Usage"), CFSTR("This application is using a lot of Mach ports."), CFSTR("Default"), CFSTR("Altern"), CFSTR("Other b"), &responseFlags);
if (0 != result) {
CFLog(3, CFSTR("ERROR"));
} else {
switch (responseFlags) {
case kCFUserNotificationDefaultResponse: CFLog(3, CFSTR("DefaultR")); break;
case kCFUserNotificationAlternateResponse: CFLog(3, CFSTR("AltR")); break;
case kCFUserNotificationOtherResponse: CFLog(3, CFSTR("OtherR")); break;
case kCFUserNotificationCancelResponse: CFLog(3, CFSTR("CancelR")); break;
}
}
__CFRestartAllThreads(threads);
CFRelease(threads);
__CF_last_warned_port_count = pcnt;
}
}
typedef mach_port_t __CFPort;
#define CFPORT_NULL MACH_PORT_NULL
typedef mach_port_t __CFPortSet;
static void __THE_SYSTEM_HAS_NO_PORTS_AVAILABLE__(kern_return_t ret) __attribute__((noinline));
static void __THE_SYSTEM_HAS_NO_PORTS_AVAILABLE__(kern_return_t ret) { HALT; };
static __CFPort __CFPortAllocate(void) {
__CFPort result = CFPORT_NULL;
kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &result);
if (KERN_SUCCESS != ret) {
char msg[256];
snprintf(msg, 256, "*** The system has no mach ports available. You may be able to diagnose which application(s) are using ports by using 'top' or Activity Monitor. (%d) ***", ret);
CRSetCrashLogMessage(msg);
__THE_SYSTEM_HAS_NO_PORTS_AVAILABLE__(ret);
return CFPORT_NULL;
}
ret = mach_port_insert_right(mach_task_self(), result, result, MACH_MSG_TYPE_MAKE_SEND);
if (KERN_SUCCESS != ret) CRASH("*** Unable to set send right on mach port. (%d) ***", ret);
mach_port_limits_t limits;
limits.mpl_qlimit = 1;
ret = mach_port_set_attributes(mach_task_self(), result, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT);
if (KERN_SUCCESS != ret) CRASH("*** Unable to set attributes on mach port. (%d) ***", ret);
return result;
}
CF_INLINE void __CFPortFree(__CFPort port) {
mach_port_destroy(mach_task_self(), port);
}
static void __NO_SPACE__(kern_return_t ret) __attribute__((noinline));
static void __NO_SPACE__(kern_return_t ret) { HALT; };
static void __RESOURCE_SHORTAGE__(kern_return_t ret) __attribute__((noinline));
static void __RESOURCE_SHORTAGE__(kern_return_t ret) { HALT; };
static void __THE_SYSTEM_HAS_NO_PORT_SETS_AVAILABLE__(kern_return_t ret) __attribute__((noinline));
static void __THE_SYSTEM_HAS_NO_PORT_SETS_AVAILABLE__(kern_return_t ret) {
if (ret == KERN_NO_SPACE) {
__NO_SPACE__(ret);
}
else if (ret == KERN_RESOURCE_SHORTAGE) {
__RESOURCE_SHORTAGE__(ret);
}
HALT;
};
CF_INLINE __CFPortSet __CFPortSetAllocate(void) {
__CFPortSet result;
kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &result);
if (KERN_SUCCESS != ret) { __THE_SYSTEM_HAS_NO_PORT_SETS_AVAILABLE__(ret); }
return (KERN_SUCCESS == ret) ? result : CFPORT_NULL;
}
CF_INLINE kern_return_t __CFPortSetInsert(__CFPort port, __CFPortSet portSet) {
if (MACH_PORT_NULL == port) {
return -1;
}
return mach_port_insert_member(mach_task_self(), port, portSet);
}
CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) {
if (MACH_PORT_NULL == port) {
return -1;
}
return mach_port_extract_member(mach_task_self(), port, portSet);
}
CF_INLINE void __CFPortSetFree(__CFPortSet portSet) {
// NOTE: we rely on the impl of mach_port destroy to extract each member, which it does
// ALSO NOTE: Per CoreOS port sets don't have ref counts, so this is equiv to mach_port_destroy, but faster/safer
const kern_return_t ret = mach_port_mod_refs(mach_task_self(), portSet, MACH_PORT_RIGHT_PORT_SET, -1);
if (ret != KERN_SUCCESS) {
CFLog(kCFLogLevelError, CFSTR("error (%d - %s) while trying to free port set: %d"), ret, mach_error_string(ret), portSet);
}
}
#elif DEPLOYMENT_TARGET_WINDOWS || TARGET_OS_CYGWIN
typedef HANDLE __CFPort;
#define CFPORT_NULL NULL
// A simple dynamic array of HANDLEs, which grows to a high-water mark
typedef struct ___CFPortSet {
uint16_t used;
uint16_t size;
HANDLE *handles;
CFLock_t lock; // insert and remove must be thread safe, like the Mach calls
} *__CFPortSet;
CF_INLINE __CFPort __CFPortAllocate(void) {
return CreateEventA(NULL, true, false, NULL);
}
CF_INLINE void __CFPortFree(__CFPort port) {
CloseHandle(port);
}
static __CFPortSet __CFPortSetAllocate(void) {
__CFPortSet result = (__CFPortSet)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct ___CFPortSet), 0);
result->used = 0;
result->size = 4;
result->handles = (HANDLE *)CFAllocatorAllocate(kCFAllocatorSystemDefault, result->size * sizeof(HANDLE), 0);
#define CF_SPINLOCK_INIT_FOR_STRUCTS(X) InitializeCriticalSection(&X)
CF_SPINLOCK_INIT_FOR_STRUCTS(result->lock);
return result;
}
static void __CFPortSetFree(__CFPortSet portSet) {
CFAllocatorDeallocate(kCFAllocatorSystemDefault, portSet->handles);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, portSet);
}
// Returns portBuf if ports fit in that space, else returns another ptr that must be freed
static __CFPort *__CFPortSetGetPorts(__CFPortSet portSet, __CFPort *portBuf, uint32_t bufSize, uint32_t *portsUsed) {
__CFLock(&(portSet->lock));
__CFPort *result = portBuf;
if (bufSize < portSet->used)
result = (__CFPort *)CFAllocatorAllocate(kCFAllocatorSystemDefault, portSet->used * sizeof(HANDLE), 0);
if (portSet->used > 1) {
// rotate the ports to vaguely simulate round-robin behaviour
uint16_t lastPort = portSet->used - 1;
HANDLE swapHandle = portSet->handles[0];
memmove(portSet->handles, &portSet->handles[1], lastPort * sizeof(HANDLE));
portSet->handles[lastPort] = swapHandle;
}
memmove(result, portSet->handles, portSet->used * sizeof(HANDLE));
*portsUsed = portSet->used;
__CFUnlock(&(portSet->lock));
return result;
}
static kern_return_t __CFPortSetInsert(__CFPort port, __CFPortSet portSet) {
if (NULL == port) {
return -1;
}
__CFLock(&(portSet->lock));
if (portSet->used >= portSet->size) {
portSet->size += 4;
portSet->handles = (HANDLE *)CFAllocatorReallocate(kCFAllocatorSystemDefault, portSet->handles, portSet->size * sizeof(HANDLE), 0);
}
if (portSet->used >= MAXIMUM_WAIT_OBJECTS) {
CFLog(kCFLogLevelWarning, CFSTR("*** More than MAXIMUM_WAIT_OBJECTS (%d) ports add to a port set. The last ones will be ignored."), MAXIMUM_WAIT_OBJECTS);
}
portSet->handles[portSet->used++] = port;
__CFUnlock(&(portSet->lock));
return KERN_SUCCESS;
}
static kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) {
int i, j;
if (NULL == port) {
return -1;
}
__CFLock(&(portSet->lock));
for (i = 0; i < portSet->used; i++) {
if (portSet->handles[i] == port) {
for (j = i+1; j < portSet->used; j++) {
portSet->handles[j-1] = portSet->handles[j];
}
portSet->used--;
__CFUnlock(&(portSet->lock));
return true;
}
}
__CFUnlock(&(portSet->lock));
return KERN_SUCCESS;
}
#elif DEPLOYMENT_TARGET_LINUX
// eventfd/timerfd descriptor
typedef int __CFPort;
#define CFPORT_NULL -1
#define MACH_PORT_NULL CFPORT_NULL
// epoll file descriptor
typedef int __CFPortSet;
#define CFPORTSET_NULL -1
static __CFPort __CFPortAllocate(void) {
return eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
}
CF_INLINE void __CFPortFree(__CFPort port) {
close(port);
}
CF_INLINE __CFPortSet __CFPortSetAllocate(void) {
return epoll_create1(EPOLL_CLOEXEC);
}
CF_INLINE kern_return_t __CFPortSetInsert(__CFPort port, __CFPortSet portSet) {
if (CFPORT_NULL == port) {
return -1;
}
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.data.fd = port;
event.events = EPOLLIN|EPOLLET;
return epoll_ctl(portSet, EPOLL_CTL_ADD, port, &event);
}
CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) {
if (CFPORT_NULL == port) {
return -1;
}
return epoll_ctl(portSet, EPOLL_CTL_DEL, port, NULL);
}
CF_INLINE void __CFPortSetFree(__CFPortSet portSet) {
close(portSet);
}
#endif
#if !defined(__MACTYPES__) && !defined(_OS_OSTYPES_H)
#if defined(__BIG_ENDIAN__)
typedef struct UnsignedWide {
UInt32 hi;
UInt32 lo;
} UnsignedWide;
#elif defined(__LITTLE_ENDIAN__)
typedef struct UnsignedWide {
UInt32 lo;
UInt32 hi;
} UnsignedWide;
#endif
typedef UnsignedWide AbsoluteTime;
#endif
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
#endif
#if USE_MK_TIMER_TOO
extern mach_port_name_t mk_timer_create(void);
extern kern_return_t mk_timer_destroy(mach_port_name_t name);
extern kern_return_t mk_timer_cancel(mach_port_name_t name, AbsoluteTime *result_time);
extern kern_return_t mk_timer_arm(mach_port_name_t name, uint64_t expire_time);
#endif
static uint32_t __CFSendTrivialMachMessage(mach_port_t port, uint32_t msg_id, CFOptionFlags options, uint32_t timeout) {
kern_return_t result;
mach_msg_header_t header;
header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
header.msgh_size = sizeof(mach_msg_header_t);
header.msgh_remote_port = port;
header.msgh_local_port = MACH_PORT_NULL;
header.msgh_id = msg_id;
result = mach_msg(&header, MACH_SEND_MSG|options, header.msgh_size, 0, MACH_PORT_NULL, timeout, MACH_PORT_NULL);
if (result == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
return result;
}
#elif DEPLOYMENT_TARGET_LINUX && !TARGET_OS_CYGWIN
static int mk_timer_create(void) {
return timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
}
static kern_return_t mk_timer_destroy(int timer) {
return close(timer);
}
static kern_return_t mk_timer_arm(int timer, int64_t expire_time) {
struct itimerspec ts;
ts.it_value.tv_sec = expire_time / 1000000000UL;
ts.it_value.tv_nsec = expire_time % 1000000000UL;
// Non-repeating timer
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;
return timerfd_settime(timer, TFD_TIMER_ABSTIME, &ts, NULL);
}
static kern_return_t mk_timer_cancel(int timer, const void *unused) {
return mk_timer_arm(timer, 0);
}
CF_INLINE int64_t __CFUInt64ToAbsoluteTime(int64_t x) {
return x;
}
#elif DEPLOYMENT_TARGET_WINDOWS || TARGET_OS_CYGWIN
static HANDLE mk_timer_create(void) {
return CreateWaitableTimer(NULL, FALSE, NULL);
}
static kern_return_t mk_timer_destroy(HANDLE name) {
BOOL res = CloseHandle(name);
if (!res) {
DWORD err = GetLastError();
CFLog(kCFLogLevelError, CFSTR("CFRunLoop: Unable to destroy timer: %d"), err);
}
return (int)res;
}
static kern_return_t mk_timer_arm(HANDLE name, LARGE_INTEGER expire_time) {
LARGE_INTEGER result;
// There is a race we know about here, (timer fire time calculated -> thread suspended -> timer armed == late timer fire), but we don't have a way to avoid it at this time, since the only way to specify an absolute value to the timer is to calculate the relative time first. Fixing that would probably require not using the TSR for timers on Windows.
uint64_t now = mach_absolute_time();
if (now > expire_time) {
result = 0;
} else {
uint64_t timeDiff = expire_time - now;
CFTimeInterval amountOfTimeToWait = __CFTSRToTimeInterval(timeDiff);
// Result is in 100 ns (10**-7 sec) units to be consistent with a FILETIME.
// CFTimeInterval is in seconds.
result = -(amountOfTimeToWait * 10000000);
}
BOOL res = SetWaitableTimer(name, &result, 0, NULL, NULL, FALSE);
if (!res) {
DWORD err = GetLastError();
CFLog(kCFLogLevelError, CFSTR("CFRunLoop: Unable to set timer: %d"), err);
}
return (int)res;
}
static kern_return_t mk_timer_cancel(HANDLE name, LARGE_INTEGER *result_time) {
BOOL res = CancelWaitableTimer(name);
if (!res) {
DWORD err = GetLastError();
CFLog(kCFLogLevelError, CFSTR("CFRunLoop: Unable to cancel timer: %d"), err);
}
return (int)res;
}
#endif
CF_EXPORT Boolean __CFMainThreadHasExited;
CF_BREAKPOINT_FUNCTION(void _CFRunLoopError_MainThreadHasExited(void));
#pragma mark -
#pragma mark Modes
/* unlock a run loop and modes before doing callouts/sleeping */
/* never try to take the run loop lock with a mode locked */
/* be very careful of common subexpression elimination and compacting code, particular across locks and unlocks! */
/* run loop mode structures should never be deallocated, even if they become empty */
static CFTypeID __kCFRunLoopModeTypeID = _kCFRuntimeNotATypeID;
static CFTypeID __kCFRunLoopTypeID = _kCFRuntimeNotATypeID;
static CFTypeID __kCFRunLoopSourceTypeID = _kCFRuntimeNotATypeID;
static CFTypeID __kCFRunLoopObserverTypeID = _kCFRuntimeNotATypeID;
static CFTypeID __kCFRunLoopTimerTypeID = _kCFRuntimeNotATypeID;
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
__CFPort _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
CF_INLINE void __CFRunLoopModeLock(CFRunLoopModeRef rlm) {
pthread_mutex_lock(&(rlm->_lock));
//CFLog(6, CFSTR("__CFRunLoopModeLock locked %p"), rlm);
}
CF_INLINE void __CFRunLoopModeUnlock(CFRunLoopModeRef rlm) {
//CFLog(6, CFSTR("__CFRunLoopModeLock unlocking %p"), rlm);
pthread_mutex_unlock(&(rlm->_lock));
}
static Boolean __CFRunLoopModeEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFRunLoopModeRef rlm1 = (CFRunLoopModeRef)cf1;
CFRunLoopModeRef rlm2 = (CFRunLoopModeRef)cf2;
return CFEqual(rlm1->_name, rlm2->_name);
}
static CFHashCode __CFRunLoopModeHash(CFTypeRef cf) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf;
return CFHash(rlm->_name);
}
static CFStringRef __CFRunLoopModeCopyDescription(CFTypeRef cf) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf;
CFMutableStringRef result;
result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
CFStringAppendFormat(result, NULL, CFSTR("<CFRunLoopMode %p [%p]>{name = %@, "), rlm, CFGetAllocator(rlm), rlm->_name);
CFStringAppendFormat(result, NULL, CFSTR("port set = 0x%x, "), rlm->_portSet);
#if USE_DISPATCH_SOURCE_FOR_TIMERS
CFStringAppendFormat(result, NULL, CFSTR("queue = %p, "), rlm->_queue);
CFStringAppendFormat(result, NULL, CFSTR("source = %p (%s), "), rlm->_timerSource, rlm->_timerFired ? "fired" : "not fired");
#endif
#if USE_MK_TIMER_TOO
CFStringAppendFormat(result, NULL, CFSTR("timer port = 0x%x, "), rlm->_timerPort);
#endif
#if DEPLOYMENT_TARGET_WINDOWS
CFStringAppendFormat(result, NULL, CFSTR("MSGQ mask = %p, "), rlm->_msgQMask);
#endif
CFStringAppendFormat(result, NULL, CFSTR("\n\tsources0 = %@,\n\tsources1 = %@,\n\tobservers = %@,\n\ttimers = %@,\n\tcurrently %0.09g (%lld) / soft deadline in: %0.09g sec (@ %lld) / hard deadline in: %0.09g sec (@ %lld)\n},\n"), rlm->_sources0, rlm->_sources1, rlm->_observers, rlm->_timers, CFAbsoluteTimeGetCurrent(), mach_absolute_time(), __CFTSRToTimeInterval(rlm->_timerSoftDeadline - mach_absolute_time()), rlm->_timerSoftDeadline, __CFTSRToTimeInterval(rlm->_timerHardDeadline - mach_absolute_time()), rlm->_timerHardDeadline);
return result;
}
static void __CFRunLoopModeDeallocate(CFTypeRef cf) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf;
if (NULL != rlm->_sources0) CFRelease(rlm->_sources0);
if (NULL != rlm->_sources1) CFRelease(rlm->_sources1);
if (NULL != rlm->_observers) CFRelease(rlm->_observers);
if (NULL != rlm->_timers) CFRelease(rlm->_timers);
if (NULL != rlm->_portToV1SourceMap) CFRelease(rlm->_portToV1SourceMap);
CFRelease(rlm->_name);
__CFPortSetFree(rlm->_portSet);
#if USE_DISPATCH_SOURCE_FOR_TIMERS
if (rlm->_timerSource) {
dispatch_source_cancel(rlm->_timerSource);
dispatch_release(rlm->_timerSource);
}
if (rlm->_queue) {
dispatch_release(rlm->_queue);
}
#endif
#if USE_MK_TIMER_TOO
if (MACH_PORT_NULL != rlm->_timerPort) mk_timer_destroy(rlm->_timerPort);
#endif
pthread_mutex_destroy(&rlm->_lock);
memset((char *)cf + sizeof(CFRuntimeBase), 0x7C, sizeof(struct __CFRunLoopMode) - sizeof(CFRuntimeBase));
}
#pragma mark -
#pragma mark Run Loops
struct _block_item {
struct _block_item *_next;
CFTypeRef _mode; // CFString or CFSet
void (^_block)(void);
};
typedef struct _per_run_data {
uint32_t a;
uint32_t b;
uint32_t stopped;
uint32_t ignoreWakeUps;
} _per_run_data;
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
/* Bit 0 of the base reserved bits is used for stopped state */
/* Bit 1 of the base reserved bits is used for sleeping state */
/* Bit 2 of the base reserved bits is used for deallocating state */
CF_INLINE volatile _per_run_data *__CFRunLoopPushPerRunData(CFRunLoopRef rl) {
volatile _per_run_data *previous = rl->_perRunData;
rl->_perRunData = (volatile _per_run_data *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(_per_run_data), 0);
rl->_perRunData->a = 0x4346524C;
rl->_perRunData->b = 0x4346524C; // 'CFRL'
rl->_perRunData->stopped = 0x00000000;
rl->_perRunData->ignoreWakeUps = 0x00000000;
return previous;
}
CF_INLINE void __CFRunLoopPopPerRunData(CFRunLoopRef rl, volatile _per_run_data *previous) {
if (rl->_perRunData) CFAllocatorDeallocate(kCFAllocatorSystemDefault, (void *)rl->_perRunData);
rl->_perRunData = previous;
}
CF_INLINE Boolean __CFRunLoopIsStopped(CFRunLoopRef rl) {
return (rl->_perRunData->stopped) ? true : false;
}
CF_INLINE void __CFRunLoopSetStopped(CFRunLoopRef rl) {
rl->_perRunData->stopped = 0x53544F50; // 'STOP'
}
CF_INLINE void __CFRunLoopUnsetStopped(CFRunLoopRef rl) {
rl->_perRunData->stopped = 0x0;
}
CF_INLINE Boolean __CFRunLoopIsIgnoringWakeUps(CFRunLoopRef rl) {
return (rl->_perRunData->ignoreWakeUps) ? true : false;
}
CF_INLINE void __CFRunLoopSetIgnoreWakeUps(CFRunLoopRef rl) {
rl->_perRunData->ignoreWakeUps = 0x57414B45; // 'WAKE'
}
CF_INLINE void __CFRunLoopUnsetIgnoreWakeUps(CFRunLoopRef rl) {
rl->_perRunData->ignoreWakeUps = 0x0;
}
CF_INLINE Boolean __CFRunLoopIsSleeping(CFRunLoopRef rl) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1);
}
CF_INLINE void __CFRunLoopSetSleeping(CFRunLoopRef rl) {
__CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
}
CF_INLINE void __CFRunLoopUnsetSleeping(CFRunLoopRef rl) {
__CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 0);
}
CF_INLINE Boolean __CFRunLoopIsDeallocating(CFRunLoopRef rl) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 2, 2);
}
CF_INLINE void __CFRunLoopSetDeallocating(CFRunLoopRef rl) {
__CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 2, 2, 1);
}
CF_INLINE void __CFRunLoopLock(CFRunLoopRef rl) {
pthread_mutex_lock(&(((CFRunLoopRef)rl)->_lock));
// CFLog(6, CFSTR("__CFRunLoopLock locked %p"), rl);
}
CF_INLINE void __CFRunLoopUnlock(CFRunLoopRef rl) {
// CFLog(6, CFSTR("__CFRunLoopLock unlocking %p"), rl);
pthread_mutex_unlock(&(((CFRunLoopRef)rl)->_lock));
}
static CFStringRef __CFRunLoopCopyDescription(CFTypeRef cf) {
CFRunLoopRef rl = (CFRunLoopRef)cf;
CFMutableStringRef result;
result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
#if DEPLOYMENT_TARGET_WINDOWS
CFStringAppendFormat(result, NULL, CFSTR("<CFRunLoop %p [%p]>{wakeup port = 0x%x, stopped = %s, ignoreWakeUps = %s, \ncurrent mode = %@,\n"), cf, CFGetAllocator(cf), rl->_wakeUpPort, __CFRunLoopIsStopped(rl) ? "true" : "false", __CFRunLoopIsIgnoringWakeUps(rl) ? "true" : "false", rl->_currentMode ? rl->_currentMode->_name : CFSTR("(none)"));
#else
CFStringAppendFormat(result, NULL, CFSTR("<CFRunLoop %p [%p]>{wakeup port = 0x%x, stopped = %s, ignoreWakeUps = %s, \ncurrent mode = %@,\n"), cf, CFGetAllocator(cf), rl->_wakeUpPort, __CFRunLoopIsStopped(rl) ? "true" : "false", __CFRunLoopIsIgnoringWakeUps(rl) ? "true" : "false", rl->_currentMode ? rl->_currentMode->_name : CFSTR("(none)"));
#endif
CFStringAppendFormat(result, NULL, CFSTR("common modes = %@,\ncommon mode items = %@,\nmodes = %@}\n"), rl->_commonModes, rl->_commonModeItems, rl->_modes);
return result;
}
CF_PRIVATE void __CFRunLoopDump() { // __private_extern__ to keep the compiler from discarding it
CFStringRef desc = CFCopyDescription(CFRunLoopGetCurrent());
CFShow(desc);
CFRelease(desc);
}
CF_INLINE void __CFRunLoopLockInit(pthread_mutex_t *lock) {
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
int32_t mret = pthread_mutex_init(lock, &mattr);
pthread_mutexattr_destroy(&mattr);
if (0 != mret) {
}
}
/* call with rl locked, returns mode locked */
static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeName, Boolean create) {
CHECK_FOR_FORK();
CFRunLoopModeRef rlm;
struct __CFRunLoopMode srlm;
memset(&srlm, 0, sizeof(srlm));
_CFRuntimeSetInstanceTypeIDAndIsa(&srlm, __kCFRunLoopModeTypeID);
srlm._name = modeName;
rlm = (CFRunLoopModeRef)CFSetGetValue(rl->_modes, &srlm);
if (NULL != rlm) {
__CFRunLoopModeLock(rlm);
return rlm;
}
if (!create) {
return NULL;
}
rlm = (CFRunLoopModeRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopModeTypeID, sizeof(struct __CFRunLoopMode) - sizeof(CFRuntimeBase), NULL);
if (NULL == rlm) {
return NULL;
}
__CFRunLoopLockInit(&rlm->_lock);
rlm->_name = CFStringCreateCopy(kCFAllocatorSystemDefault, modeName);
rlm->_stopped = false;
rlm->_portToV1SourceMap = NULL;
rlm->_sources0 = NULL;
rlm->_sources1 = NULL;
rlm->_observers = NULL;
rlm->_timers = NULL;
rlm->_observerMask = 0;
rlm->_portSet = __CFPortSetAllocate();
rlm->_timerSoftDeadline = UINT64_MAX;
rlm->_timerHardDeadline = UINT64_MAX;
kern_return_t ret = KERN_SUCCESS;
#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);
rlm->_timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, rlm->_queue);
__block Boolean *timerFiredPointer = &(rlm->_timerFired);
dispatch_source_set_event_handler(rlm->_timerSource, ^{
*timerFiredPointer = true;
});
// Set timer to far out there. The unique leeway makes this timer easy to spot in debug output.
_dispatch_source_set_runloop_timer_4CF(rlm->_timerSource, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 321);
dispatch_resume(rlm->_timerSource);
ret = __CFPortSetInsert(queuePort, rlm->_portSet);
if (KERN_SUCCESS != ret) CRASH("*** Unable to insert timer port into port set. (%d) ***", ret);
#endif
#if USE_MK_TIMER_TOO
rlm->_timerPort = mk_timer_create();
if (rlm->_timerPort == MACH_PORT_NULL) {
CRASH("*** Unable to create timer Port (%d) ***", rlm->_timerPort);
}
ret = __CFPortSetInsert(rlm->_timerPort, rlm->_portSet);
if (KERN_SUCCESS != ret) CRASH("*** Unable to insert timer port into port set. (%d) ***", ret);
#endif
ret = __CFPortSetInsert(rl->_wakeUpPort, rlm->_portSet);
if (KERN_SUCCESS != ret) CRASH("*** Unable to insert wake up port into port set. (%d) ***", ret);
#if DEPLOYMENT_TARGET_WINDOWS
rlm->_msgQMask = 0;
rlm->_msgPump = NULL;
#endif
CFSetAddValue(rl->_modes, rlm);
CFRelease(rlm);
__CFRunLoopModeLock(rlm); /* return mode locked */
return rlm;
}
// expects rl and rlm locked
static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopModeRef previousMode) {
CHECK_FOR_FORK();
if (NULL == rlm) return true;
#if DEPLOYMENT_TARGET_WINDOWS
if (0 != rlm->_msgQMask) return false;
#endif
#if __HAS_DISPATCH__
Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) return false; // represents the libdispatch main queue
#endif
if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)) return false;
if (NULL != rlm->_sources1 && 0 < CFSetGetCount(rlm->_sources1)) return false;
if (NULL != rlm->_timers && 0 < CFArrayGetCount(rlm->_timers)) return false;
struct _block_item *item = rl->_blocks_head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
doit = CFEqual(curr->_mode, rlm->_name) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(rl->_commonModes, rlm->_name));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, rlm->_name) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(rl->_commonModes, rlm->_name));
}
if (doit) return false;
}
return true;
}
#if DEPLOYMENT_TARGET_WINDOWS
uint32_t _CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef modeName) {
if (modeName == kCFRunLoopCommonModes) {
CFLog(kCFLogLevelError, CFSTR("_CFRunLoopGetWindowsMessageQueueMask: kCFRunLoopCommonModes unsupported"));
HALT;
}
DWORD result = 0;
__CFRunLoopLock(rl);
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false);
if (rlm) {
result = rlm->_msgQMask;
__CFRunLoopModeUnlock(rlm);
}
__CFRunLoopUnlock(rl);
return (uint32_t)result;
}
void _CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, uint32_t mask, CFStringRef modeName) {
if (modeName == kCFRunLoopCommonModes) {
CFLog(kCFLogLevelError, CFSTR("_CFRunLoopSetWindowsMessageQueueMask: kCFRunLoopCommonModes unsupported"));
HALT;
}
__CFRunLoopLock(rl);
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
rlm->_msgQMask = (DWORD)mask;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
}
uint32_t _CFRunLoopGetWindowsThreadID(CFRunLoopRef rl) {
return rl->_winthread;
}
CFWindowsMessageQueueHandler _CFRunLoopGetWindowsMessageQueueHandler(CFRunLoopRef rl, CFStringRef modeName) {
if (modeName == kCFRunLoopCommonModes) {
CFLog(kCFLogLevelError, CFSTR("_CFRunLoopGetWindowsMessageQueueMask: kCFRunLoopCommonModes unsupported"));
HALT;
}
if (!_CFRunLoopIsCurrent(rl)) {
CFLog(kCFLogLevelError, CFSTR("_CFRunLoopGetWindowsMessageQueueHandler: run loop parameter must be the current run loop"));
HALT;
}
void (*result)(void) = NULL;
__CFRunLoopLock(rl);
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, false);
if (rlm) {
result = rlm->_msgPump;
__CFRunLoopModeUnlock(rlm);
}
__CFRunLoopUnlock(rl);
return result;
}
void _CFRunLoopSetWindowsMessageQueueHandler(CFRunLoopRef rl, CFStringRef modeName, CFWindowsMessageQueueHandler func) {
if (modeName == kCFRunLoopCommonModes) {
CFLog(kCFLogLevelError, CFSTR("_CFRunLoopGetWindowsMessageQueueMask: kCFRunLoopCommonModes unsupported"));
HALT;
}
if (!_CFRunLoopIsCurrent(rl)) {
CFLog(kCFLogLevelError, CFSTR("_CFRunLoopGetWindowsMessageQueueHandler: run loop parameter must be the current run loop"));
HALT;
}
__CFRunLoopLock(rl);
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
rlm->_msgPump = func;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
}
#endif
#pragma mark -
#pragma mark Sources
/* Bit 3 in the base reserved bits is used for invalid state in run loop objects */
CF_INLINE Boolean __CFIsValid(const void *cf) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3);
}
CF_INLINE void __CFSetValid(void *cf) {
__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
}
CF_INLINE void __CFUnsetValid(void *cf) {
__CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 0);
}
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order; /* immutable */
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; /* immutable, except invalidation */
CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
} _context;
};
/* Bit 1 of the base reserved bits is used for signalled state */
CF_INLINE Boolean __CFRunLoopSourceIsSignaled(CFRunLoopSourceRef rls) {
return (Boolean)__CFBitfieldGetValue(rls->_bits, 1, 1);
}
CF_INLINE void __CFRunLoopSourceSetSignaled(CFRunLoopSourceRef rls) {
__CFBitfieldSetValue(rls->_bits, 1, 1, 1);
}
CF_INLINE void __CFRunLoopSourceUnsetSignaled(CFRunLoopSourceRef rls) {
__CFBitfieldSetValue(rls->_bits, 1, 1, 0);
}
CF_INLINE void __CFRunLoopSourceLock(CFRunLoopSourceRef rls) {
pthread_mutex_lock(&(rls->_lock));
// CFLog(6, CFSTR("__CFRunLoopSourceLock locked %p"), rls);
}
CF_INLINE void __CFRunLoopSourceUnlock(CFRunLoopSourceRef rls) {
// CFLog(6, CFSTR("__CFRunLoopSourceLock unlocking %p"), rls);
pthread_mutex_unlock(&(rls->_lock));
}
#pragma mark Observers
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities; /* immutable */
CFIndex _order; /* immutable */
CFRunLoopObserverCallBack _callout; /* immutable */
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};
/* Bit 0 of the base reserved bits is used for firing state */
/* Bit 1 of the base reserved bits is used for repeats state */
CF_INLINE Boolean __CFRunLoopObserverIsFiring(CFRunLoopObserverRef rlo) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0);
}
CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 1);
}
CF_INLINE void __CFRunLoopObserverUnsetFiring(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 0);
}
CF_INLINE Boolean __CFRunLoopObserverRepeats(CFRunLoopObserverRef rlo) {
return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1);
}
CF_INLINE void __CFRunLoopObserverSetRepeats(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
}
CF_INLINE void __CFRunLoopObserverUnsetRepeats(CFRunLoopObserverRef rlo) {
__CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 1, 1, 0);
}
CF_INLINE void __CFRunLoopObserverLock(CFRunLoopObserverRef rlo) {
pthread_mutex_lock(&(rlo->_lock));
// CFLog(6, CFSTR("__CFRunLoopObserverLock locked %p"), rlo);
}
CF_INLINE void __CFRunLoopObserverUnlock(CFRunLoopObserverRef rlo) {
// CFLog(6, CFSTR("__CFRunLoopObserverLock unlocking %p"), rlo);
pthread_mutex_unlock(&(rlo->_lock));
}
static void __CFRunLoopObserverSchedule(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) {
__CFRunLoopObserverLock(rlo);
if (0 == rlo->_rlCount) {
rlo->_runLoop = rl;
}
rlo->_rlCount++;
__CFRunLoopObserverUnlock(rlo);
}
static void __CFRunLoopObserverCancel(CFRunLoopObserverRef rlo, CFRunLoopRef rl, CFRunLoopModeRef rlm) {
__CFRunLoopObserverLock(rlo);
rlo->_rlCount--;
if (0 == rlo->_rlCount) {
rlo->_runLoop = NULL;
}
__CFRunLoopObserverUnlock(rlo);
}
#pragma mark Timers
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFMutableSetRef _rlModes;
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable */
CFTimeInterval _tolerance; /* mutable */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable */
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
/* Bit 0 of the base reserved bits is used for firing state */
/* Bit 1 of the base reserved bits is used for fired-during-callout state */
/* Bit 2 of the base reserved bits is used for waking state */
CF_INLINE Boolean __CFRunLoopTimerIsFiring(CFRunLoopTimerRef rlt) {
return (Boolean)__CFBitfieldGetValue(rlt->_bits, 0, 0);
}
CF_INLINE void __CFRunLoopTimerSetFiring(CFRunLoopTimerRef rlt) {
__CFBitfieldSetValue(rlt->_bits, 0, 0, 1);
}
CF_INLINE void __CFRunLoopTimerUnsetFiring(CFRunLoopTimerRef rlt) {
__CFBitfieldSetValue(rlt->_bits, 0, 0, 0);
}
CF_INLINE Boolean __CFRunLoopTimerIsDeallocating(CFRunLoopTimerRef rlt) {
return (Boolean)__CFBitfieldGetValue(rlt->_bits, 2, 2);
}
CF_INLINE void __CFRunLoopTimerSetDeallocating(CFRunLoopTimerRef rlt) {
__CFBitfieldSetValue(rlt->_bits, 2, 2, 1);
}
CF_INLINE void __CFRunLoopTimerLock(CFRunLoopTimerRef rlt) {
pthread_mutex_lock(&(rlt->_lock));
// CFLog(6, CFSTR("__CFRunLoopTimerLock locked %p"), rlt);
}
CF_INLINE void __CFRunLoopTimerUnlock(CFRunLoopTimerRef rlt) {
// CFLog(6, CFSTR("__CFRunLoopTimerLock unlocking %p"), rlt);
pthread_mutex_unlock(&(rlt->_lock));
}
static CFLock_t __CFRLTFireTSRLock = CFLockInit;
CF_INLINE void __CFRunLoopTimerFireTSRLock(void) {
__CFLock(&__CFRLTFireTSRLock);
}
CF_INLINE void __CFRunLoopTimerFireTSRUnlock(void) {
__CFUnlock(&__CFRLTFireTSRLock);
}
#pragma mark -
/* CFRunLoop */
CONST_STRING_DECL(kCFRunLoopDefaultMode, "kCFRunLoopDefaultMode")
CONST_STRING_DECL(kCFRunLoopCommonModes, "kCFRunLoopCommonModes")
// call with rl and rlm locked
static CFRunLoopSourceRef __CFRunLoopModeFindSourceForMachPort(CFRunLoopRef rl, CFRunLoopModeRef rlm, __CFPort port) { /* DOES CALLOUT */
CHECK_FOR_FORK();
CFRunLoopSourceRef found = rlm->_portToV1SourceMap ? (CFRunLoopSourceRef)CFDictionaryGetValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)port) : NULL;
return found;
}
// Remove backreferences the mode's sources have to the rl (context);
// the primary purpose of rls->_runLoops is so that Invalidation can remove
// the source from the run loops it is in, but during deallocation of a
// run loop, we already know that the sources are going to be punted
// from it, so invalidation of sources does not need to remove from a
// deallocating run loop.
static void __CFRunLoopCleanseSources(const void *value, void *context) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)context;
CFIndex idx, cnt;
const void **list, *buffer[256];
if (NULL == rlm->_sources0 && NULL == rlm->_sources1) return;
cnt = (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0) + (rlm->_sources1 ? CFSetGetCount(rlm->_sources1) : 0);
list = (const void **)((cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0));
if (rlm->_sources0) CFSetGetValues(rlm->_sources0, list);
if (rlm->_sources1) CFSetGetValues(rlm->_sources1, list + (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0));
for (idx = 0; idx < cnt; idx++) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)list[idx];
__CFRunLoopSourceLock(rls);
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
}
static void __CFRunLoopDeallocateSources(const void *value, void *context) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)context;
CFIndex idx, cnt;
const void **list, *buffer[256];
if (NULL == rlm->_sources0 && NULL == rlm->_sources1) return;
cnt = (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0) + (rlm->_sources1 ? CFSetGetCount(rlm->_sources1) : 0);
list = (const void **)((cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0));
if (rlm->_sources0) CFSetGetValues(rlm->_sources0, list);
if (rlm->_sources1) CFSetGetValues(rlm->_sources1, list + (rlm->_sources0 ? CFSetGetCount(rlm->_sources0) : 0));
for (idx = 0; idx < cnt; idx++) {
CFRetain(list[idx]);
}
if (rlm->_sources0) CFSetRemoveAllValues(rlm->_sources0);
if (rlm->_sources1) CFSetRemoveAllValues(rlm->_sources1);
for (idx = 0; idx < cnt; idx++) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)list[idx];
__CFRunLoopSourceLock(rls);
if (NULL != rls->_runLoops) {
CFBagRemoveValue(rls->_runLoops, rl);
}
__CFRunLoopSourceUnlock(rls);
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.cancel) {
rls->_context.version0.cancel(rls->_context.version0.info, rl, rlm->_name); /* CALLOUT */
}
} else if (1 == rls->_context.version0.version) {
__CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */
if (CFPORT_NULL != port) {
__CFPortSetRemove(port, rlm->_portSet);
}
}
CFRelease(rls);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
}
static void __CFRunLoopDeallocateObservers(const void *value, void *context) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)context;
CFIndex idx, cnt;
const void **list, *buffer[256];
if (NULL == rlm->_observers) return;
cnt = CFArrayGetCount(rlm->_observers);
list = (const void **)((cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0));
CFArrayGetValues(rlm->_observers, CFRangeMake(0, cnt), list);
for (idx = 0; idx < cnt; idx++) {
CFRetain(list[idx]);
}
CFArrayRemoveAllValues(rlm->_observers);
for (idx = 0; idx < cnt; idx++) {
__CFRunLoopObserverCancel((CFRunLoopObserverRef)list[idx], rl, rlm);
CFRelease(list[idx]);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
}
static void __CFRunLoopDeallocateTimers(const void *value, void *context) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)value;
if (NULL == rlm->_timers) return;
void (^deallocateTimers)(CFMutableArrayRef timers) = ^(CFMutableArrayRef timers) {
CFIndex idx, cnt;
const void **list, *buffer[256];
cnt = CFArrayGetCount(timers);
list = (const void **)((cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0));
CFArrayGetValues(timers, CFRangeMake(0, CFArrayGetCount(timers)), list);
for (idx = 0; idx < cnt; idx++) {
CFRetain(list[idx]);
}
CFArrayRemoveAllValues(timers);
for (idx = 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)list[idx];
__CFRunLoopTimerLock(rlt);
// if the run loop is deallocating, and since a timer can only be in one
// run loop, we're going to be removing the timer from all modes, so be
// a little heavy-handed and direct
CFSetRemoveAllValues(rlt->_rlModes);
rlt->_runLoop = NULL;
__CFRunLoopTimerUnlock(rlt);
CFRelease(list[idx]);
}
if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list);
};
if (rlm->_timers && CFArrayGetCount(rlm->_timers)) deallocateTimers(rlm->_timers);
}
CF_EXPORT CFRunLoopRef _CFRunLoopGet0b(pthread_t t);
static void __CFRunLoopDeallocate(CFTypeRef cf) {
CFRunLoopRef rl = (CFRunLoopRef)cf;
if (_CFRunLoopGet0b(pthread_main_thread_np()) == cf) HALT;
/* We try to keep the run loop in a valid state as long as possible,
since sources may have non-retained references to the run loop.
Another reason is that we don't want to lock the run loop for
callback reasons, if we can get away without that. We start by
eliminating the sources, since they are the most likely to call
back into the run loop during their "cancellation". Common mode
items will be removed from the mode indirectly by the following
three lines. */
__CFRunLoopSetDeallocating(rl);
if (NULL != rl->_modes) {
CFSetApplyFunction(rl->_modes, (__CFRunLoopCleanseSources), rl); // remove references to rl
CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateSources), rl);
CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateObservers), rl);
CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateTimers), rl);
}
__CFRunLoopLock(rl);
struct _block_item *item = rl->_blocks_head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
CFRelease(curr->_mode);
Block_release(curr->_block);
free(curr);
}
if (NULL != rl->_commonModeItems) {
CFRelease(rl->_commonModeItems);
}
if (NULL != rl->_commonModes) {
CFRelease(rl->_commonModes);
}
if (NULL != rl->_modes) {
CFRelease(rl->_modes);
}
__CFPortFree(rl->_wakeUpPort);
rl->_wakeUpPort = CFPORT_NULL;
__CFRunLoopPopPerRunData(rl, NULL);
__CFRunLoopUnlock(rl);
pthread_mutex_destroy(&rl->_lock);
memset((char *)cf + sizeof(CFRuntimeBase), 0x8C, sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase));
}
static const CFRuntimeClass __CFRunLoopModeClass = {
0,
"CFRunLoopMode",
NULL, // init
NULL, // copy
__CFRunLoopModeDeallocate,
__CFRunLoopModeEqual,
__CFRunLoopModeHash,
NULL, //
__CFRunLoopModeCopyDescription
};
static const CFRuntimeClass __CFRunLoopClass = {
0,
"CFRunLoop",
NULL, // init
NULL, // copy
__CFRunLoopDeallocate,
NULL,
NULL,
NULL, //
__CFRunLoopCopyDescription
};
CF_PRIVATE void __CFFinalizeRunLoop(uintptr_t data);
CFTypeID CFRunLoopGetTypeID(void) {
static dispatch_once_t initOnce;
dispatch_once(&initOnce, ^{ __kCFRunLoopTypeID = _CFRuntimeRegisterClass(&__CFRunLoopClass); __kCFRunLoopModeTypeID = _CFRuntimeRegisterClass(&__CFRunLoopModeClass); });
return __kCFRunLoopTypeID;
}
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
CFRunLoopRef loop = NULL;
CFRunLoopModeRef rlm;
uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
if (NULL == loop) {
return NULL;
}
(void)__CFRunLoopPushPerRunData(loop);
__CFRunLoopLockInit(&loop->_lock);
loop->_wakeUpPort = __CFPortAllocate();
if (CFPORT_NULL == loop->_wakeUpPort) HALT;
__CFRunLoopSetIgnoreWakeUps(loop);
loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
loop->_commonModeItems = NULL;
loop->_currentMode = NULL;
loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
loop->_blocks_head = NULL;
loop->_blocks_tail = NULL;
loop->_counterpart = NULL;
loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
loop->_winthread = GetCurrentThreadId();
#else
loop->_winthread = 0;
#endif
rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
return loop;
}
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;
CF_PRIVATE CFRunLoopRef _CFRunLoopCacheLookup(pthread_t t, const Boolean createCache) {
CFRunLoopRef loop = NULL;
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops && createCache) {
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
if (__CFRunLoops) {
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
}
__CFUnlock(&loopsLock);
return loop;
}
// should only be called by Foundation
CF_EXPORT Boolean _CFRunLoopIsCurrent(const CFRunLoopRef rl) {
Boolean result = false;
if (rl) {
CFRunLoopRef loop = (CFRunLoopRef)_CFGetTSDCreateIfNeeded(__CFTSDKeyRunLoop, false);
if (loop == NULL) {
loop = _CFRunLoopCacheLookup(pthread_self(), false);
}
result = (rl == loop);
}
return result;
}
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
// should only be called by Foundation
CFRunLoopRef _CFRunLoopGet0b(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
CFRunLoopRef loop = NULL;
if (__CFRunLoops) {
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
}
__CFUnlock(&loopsLock);
return loop;
}
static void __CFRunLoopRemoveAllSources(CFRunLoopRef rl, CFStringRef modeName);
// Called for each thread as it exits
CF_PRIVATE void __CFFinalizeRunLoop(uintptr_t data) {
CFRunLoopRef rl = NULL;
if (data <= 1) {
__CFLock(&loopsLock);
if (__CFRunLoops) {
rl = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(pthread_self()));
if (rl) CFRetain(rl);
CFDictionaryRemoveValue(__CFRunLoops, pthreadPointer(pthread_self()));
}
__CFUnlock(&loopsLock);
} else {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(data - 1), (void (*)(void *))__CFFinalizeRunLoop);
}
if (rl && CFRunLoopGetMain() != rl) { // protect against cooperative threads
if (NULL != rl->_counterpart) {
#if DEPLOYMENT_RUNTIME_SWIFT
extern void swift_release(void *);
swift_release((void *)rl->_counterpart);
#else
CFRelease(rl->_counterpart);
#endif
rl->_counterpart = NULL;
}
// purge all sources before deallocation
CFArrayRef array = CFRunLoopCopyAllModes(rl);
for (CFIndex idx = CFArrayGetCount(array); idx--;) {
CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
__CFRunLoopRemoveAllSources(rl, modeName);
}
__CFRunLoopRemoveAllSources(rl, kCFRunLoopCommonModes);
CFRelease(array);
}
if (rl) CFRelease(rl);
}
pthread_t _CFRunLoopGet1(CFRunLoopRef rl) {
return rl->_pthread;
}
// should only be called by Foundation
CF_EXPORT CFTypeRef _CFRunLoopGet2(CFRunLoopRef rl) {
CFTypeRef ret = NULL;
__CFLock(&loopsLock);
#if DEPLOYMENT_RUNTIME_SWIFT
if (rl->_counterpart == NULL) {
CFTypeRef ns = __CFSwiftBridge.NSRunLoop._new(rl); // returns retained so we will claim ownership of that return value by just assigning (the release is balanced in the destruction of the CFRunLoop
rl->_counterpart = ns;
}
#endif
ret = rl->_counterpart;
__CFUnlock(&loopsLock);
return ret;
}
// should only be called by Foundation
CF_EXPORT CFTypeRef _CFRunLoopGet2b(CFRunLoopRef rl) {
return rl->_counterpart;
}
#if DEPLOYMENT_TARGET_MACOSX
void _CFRunLoopSetCurrent(CFRunLoopRef rl) {
if (pthread_main_np()) return;
CFRunLoopRef currentLoop = CFRunLoopGetCurrent();
if (rl != currentLoop) {
CFRetain(currentLoop); // avoid a deallocation of the currentLoop inside the lock
__CFLock(&loopsLock);
if (rl) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(pthread_self()), rl);
} else {
CFDictionaryRemoveValue(__CFRunLoops, pthreadPointer(pthread_self()));
}
__CFUnlock(&loopsLock);
CFRelease(currentLoop);
_CFSetTSD(__CFTSDKeyRunLoop, NULL, NULL);
_CFSetTSD(__CFTSDKeyRunLoopCntr, 0, (void (*)(void *))__CFFinalizeRunLoop);
}
}
#endif
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl) {
CHECK_FOR_FORK();
CFStringRef result = NULL;
__CFRunLoopLock(rl);
if (NULL != rl->_currentMode) {
result = (CFStringRef)CFRetain(rl->_currentMode->_name);
}
__CFRunLoopUnlock(rl);
return result;
}
static void __CFRunLoopGetModeName(const void *value, void *context) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)value;
CFMutableArrayRef array = (CFMutableArrayRef)context;
CFArrayAppendValue(array, rlm->_name);
}
CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl) {
CHECK_FOR_FORK();
CFMutableArrayRef array;
__CFRunLoopLock(rl);
array = CFArrayCreateMutable(kCFAllocatorSystemDefault, CFSetGetCount(rl->_modes), &kCFTypeArrayCallBacks);
CFSetApplyFunction(rl->_modes, (__CFRunLoopGetModeName), array);
__CFRunLoopUnlock(rl);
return array;
}
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) {
CFTypeRef item = (CFTypeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
CFStringRef modeName = (CFStringRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
static void __CFRunLoopRemoveItemFromCommonModes(const void *value, void *ctx) {
CFStringRef modeName = (CFStringRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
CFRunLoopRemoveSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
CFRunLoopRemoveObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
CFRunLoopRemoveTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
CF_EXPORT Boolean _CFRunLoop01(CFRunLoopRef rl, CFStringRef modeName) {
__CFRunLoopLock(rl);
Boolean present = CFSetContainsValue(rl->_commonModes, modeName);
__CFRunLoopUnlock(rl);
return present;
}
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
__CFRunLoopLock(rl);
if (!CFSetContainsValue(rl->_commonModes, modeName)) {
CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL;
CFSetAddValue(rl->_commonModes, modeName);
if (NULL != set) {
CFTypeRef context[2] = {rl, modeName};
/* add all common-modes items to new mode */
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
CFRelease(set);
}
} else {
}
__CFRunLoopUnlock(rl);
}
#if __HAS_DISPATCH__
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() __attribute__((noinline));
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg) {
_dispatch_main_queue_callback_4CF(msg);
asm __volatile__(""); // thwart tail-call optimization
}
#endif
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
if (func) {
func(observer, activity, info);
}
asm __volatile__(""); // thwart tail-call optimization
}
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) {
if (func) {
func(timer, info);
}
asm __volatile__(""); // thwart tail-call optimization
}
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) {
if (block) {
block();
}
asm __volatile__(""); // thwart tail-call optimization
}
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
if (!rl->_blocks_head) return false;
if (!rlm || !rlm->_name) return false;
Boolean did = false;
struct _block_item *head = rl->_blocks_head;
struct _block_item *tail = rl->_blocks_tail;
rl->_blocks_head = NULL;
rl->_blocks_tail = NULL;
CFSetRef commonModes = rl->_commonModes;
CFStringRef curMode = rlm->_name;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
struct _block_item *prev = NULL;
struct _block_item *item = head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
}
if (!doit) prev = curr;
if (doit) {
if (prev) prev->_next = item;
if (curr == head) head = item;
if (curr == tail) tail = prev;
void (^block)(void) = curr->_block;
CFRelease(curr->_mode);
free(curr);
if (doit) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
did = true;
}
Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
if (head) {
tail->_next = rl->_blocks_head;
rl->_blocks_head = head;
if (!rl->_blocks_tail) rl->_blocks_tail = tail;
}
return did;
}
/* rl is locked, rlm is locked on entrance and exit */
static void __CFRunLoopDoObservers() __attribute__((noinline));
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */
CHECK_FOR_FORK();
CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0;
if (cnt < 1) return;
/* Fire the observers */
STACK_BUFFER_DECL(CFRunLoopObserverRef, buffer, (cnt <= 1024) ? cnt : 1);
CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef));
CFIndex obs_cnt = 0;
for (CFIndex idx = 0; idx < cnt; idx++) {
CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) {
collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);
}
}
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
for (CFIndex idx = 0; idx < obs_cnt; idx++) {
CFRunLoopObserverRef rlo = collectedObservers[idx];
__CFRunLoopObserverLock(rlo);
if (__CFIsValid(rlo)) {
Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo);
__CFRunLoopObserverSetFiring(rlo);
__CFRunLoopObserverUnlock(rlo);
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);
if (doInvalidate) {
CFRunLoopObserverInvalidate(rlo);
}
__CFRunLoopObserverUnsetFiring(rlo);
} else {
__CFRunLoopObserverUnlock(rlo);
}
CFRelease(rlo);
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
if (collectedObservers != buffer) free(collectedObservers);
}
static CFComparisonResult __CFRunLoopSourceComparator(const void *val1, const void *val2, void *context) {
CFRunLoopSourceRef o1 = (CFRunLoopSourceRef)val1;
CFRunLoopSourceRef o2 = (CFRunLoopSourceRef)val2;
if (o1->_order < o2->_order) return kCFCompareLessThan;
if (o2->_order < o1->_order) return kCFCompareGreaterThan;
return kCFCompareEqualTo;
}
static void __CFRunLoopCollectSources0(const void *value, void *context) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)value;
CFTypeRef *sources = (CFTypeRef *)context;
if (0 == rls->_context.version0.version && __CFIsValid(rls) && __CFRunLoopSourceIsSignaled(rls)) {
if (NULL == *sources) {
*sources = CFRetain(rls);
} else if (CFGetTypeID(*sources) == CFRunLoopSourceGetTypeID()) {
CFTypeRef oldrls = *sources;
*sources = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue((CFMutableArrayRef)*sources, oldrls);
CFArrayAppendValue((CFMutableArrayRef)*sources, rls);
CFRelease(oldrls);
} else {
CFArrayAppendValue((CFMutableArrayRef)*sources, rls);
}
}
}
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) {
if (perform) {
perform(info);
}
asm __volatile__(""); // thwart tail-call optimization
}
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
void *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info),
mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply,
#else
void (*perform)(void *),
#endif
void *info) {
if (perform) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
*reply = perform(msg, size, kCFAllocatorSystemDefault, info);
#else
perform(info);
#endif
}
asm __volatile__(""); // thwart tail-call optimization
}
/* rl is locked, rlm is locked on entrance and exit */
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) __attribute__((noinline));
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) { /* DOES CALLOUT */
CHECK_FOR_FORK();
CFTypeRef sources = NULL;
Boolean sourceHandled = false;
/* Fire the version 0 sources */
if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)) {
CFSetApplyFunction(rlm->_sources0, (__CFRunLoopCollectSources0), &sources);
}
if (NULL != sources) {
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
// sources is either a single (retained) CFRunLoopSourceRef or an array of (retained) CFRunLoopSourceRef
if (CFGetTypeID(sources) == CFRunLoopSourceGetTypeID()) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)sources;
__CFRunLoopSourceLock(rls);
if (__CFRunLoopSourceIsSignaled(rls)) {
__CFRunLoopSourceUnsetSignaled(rls);
if (__CFIsValid(rls)) {
__CFRunLoopSourceUnlock(rls);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
CHECK_FOR_FORK();
sourceHandled = true;
} else {
__CFRunLoopSourceUnlock(rls);
}
} else {
__CFRunLoopSourceUnlock(rls);
}
} else {
CFIndex cnt = CFArrayGetCount((CFArrayRef)sources);
CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL);
for (CFIndex idx = 0; idx < cnt; idx++) {
CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx);
__CFRunLoopSourceLock(rls);
if (__CFRunLoopSourceIsSignaled(rls)) {
__CFRunLoopSourceUnsetSignaled(rls);
if (__CFIsValid(rls)) {
__CFRunLoopSourceUnlock(rls);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
CHECK_FOR_FORK();
sourceHandled = true;
} else {
__CFRunLoopSourceUnlock(rls);
}
} else {
__CFRunLoopSourceUnlock(rls);
}
if (stopAfterHandle && sourceHandled) {
break;
}
}
}
CFRelease(sources);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
}
return sourceHandled;
}
CF_INLINE void __CFRunLoopDebugInfoForRunLoopSource(CFRunLoopSourceRef rls) {
}
// msg, size and reply are unused on Windows
static Boolean __CFRunLoopDoSource1() __attribute__((noinline));
static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
, mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply
#endif
) { /* DOES CALLOUT */
CHECK_FOR_FORK();
Boolean sourceHandled = false;
/* Fire a version 1 source */
CFRetain(rls);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
__CFRunLoopSourceLock(rls);
if (__CFIsValid(rls)) {
__CFRunLoopSourceUnsetSignaled(rls);
__CFRunLoopSourceUnlock(rls);
__CFRunLoopDebugInfoForRunLoopSource(rls);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(rls->_context.version1.perform,
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
msg, size, reply,
#endif
rls->_context.version1.info);
CHECK_FOR_FORK();
sourceHandled = true;
} else {
if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 rls %p is invalid"), CFRunLoopGetCurrent(), *_CFGetProgname(), rls); }
__CFRunLoopSourceUnlock(rls);
}
CFRelease(rls);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
return sourceHandled;
}
static CFIndex __CFRunLoopInsertionIndexInTimerArray(CFArrayRef array, CFRunLoopTimerRef rlt) __attribute__((noinline));
static CFIndex __CFRunLoopInsertionIndexInTimerArray(CFArrayRef array, CFRunLoopTimerRef rlt) {
CFIndex cnt = CFArrayGetCount(array);
if (cnt <= 0) {
return 0;
}
if (256 < cnt) {
CFRunLoopTimerRef item = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(array, cnt - 1);
if (item->_fireTSR <= rlt->_fireTSR) {
return cnt;
}
item = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(array, 0);
if (rlt->_fireTSR < item->_fireTSR) {
return 0;
}
}
CFIndex add = (1 << flsl(cnt)) * 2;
CFIndex idx = 0;
Boolean lastTestLEQ;
do {
add = add / 2;
lastTestLEQ = false;
CFIndex testIdx = idx + add;
if (testIdx < cnt) {
CFRunLoopTimerRef item = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(array, testIdx);
if (item->_fireTSR <= rlt->_fireTSR) {
idx = testIdx;
lastTestLEQ = true;
}
}
} while (0 < add);
return lastTestLEQ ? idx + 1 : idx;
}
static void __CFArmNextTimerInMode(CFRunLoopModeRef rlm, CFRunLoopRef rl) {
uint64_t nextHardDeadline = UINT64_MAX;
uint64_t nextSoftDeadline = UINT64_MAX;
if (rlm->_timers) {
// Look at the list of timers. We will calculate two TSR values; the next soft and next hard deadline.
// The next soft deadline is the first time we can fire any timer. This is the fire date of the first timer in our sorted list of timers.
// The next hard deadline is the last time at which we can fire the timer before we've moved out of the allowable tolerance of the timers in our list.
for (CFIndex idx = 0, cnt = CFArrayGetCount(rlm->_timers); idx < cnt; idx++) {
CFRunLoopTimerRef t = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers , idx);
// discount timers currently firing
if (__CFRunLoopTimerIsFiring(t)) continue;
int32_t err = CHECKINT_NO_ERROR;
uint64_t oneTimerSoftDeadline = t->_fireTSR;
uint64_t oneTimerHardDeadline = check_uint64_add(t->_fireTSR, __CFTimeIntervalToTSR(t->_tolerance), &err);
if (err != CHECKINT_NO_ERROR) oneTimerHardDeadline = UINT64_MAX;
// We can stop searching if the soft deadline for this timer exceeds the current hard deadline. Otherwise, later timers with lower tolerance could still have earlier hard deadlines.
if (oneTimerSoftDeadline > nextHardDeadline) {
break;
}
if (oneTimerSoftDeadline < nextSoftDeadline) {
nextSoftDeadline = oneTimerSoftDeadline;
}
if (oneTimerHardDeadline < nextHardDeadline) {
nextHardDeadline = oneTimerHardDeadline;
}
}
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()));
}
#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);
dispatch_time_t deadline = __CFTSRToDispatchTime(nextSoftDeadline);
#if USE_MK_TIMER_TOO
if (leeway > 0) {
// Only use the dispatch timer if we have any leeway
// <rdar://problem/14447675>
// Cancel the mk timer
if (rlm->_mkTimerArmed && rlm->_timerPort) {
AbsoluteTime dummy;
mk_timer_cancel(rlm->_timerPort, &dummy);
rlm->_mkTimerArmed = false;
}
// Arm the dispatch timer
_dispatch_source_set_runloop_timer_4CF(rlm->_timerSource, deadline, DISPATCH_TIME_FOREVER, leeway);
rlm->_dispatchTimerArmed = true;
} else {
// Cancel the dispatch timer
if (rlm->_dispatchTimerArmed) {
// Cancel the dispatch timer
_dispatch_source_set_runloop_timer_4CF(rlm->_timerSource, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 888);
rlm->_dispatchTimerArmed = false;
}
// Arm the mk timer
if (rlm->_timerPort) {
mk_timer_arm(rlm->_timerPort, nextSoftDeadline);
rlm->_mkTimerArmed = true;
}
}
#else
_dispatch_source_set_runloop_timer_4CF(rlm->_timerSource, deadline, DISPATCH_TIME_FOREVER, leeway);
#endif
#else
if (rlm->_timerPort) {
mk_timer_arm(rlm->_timerPort, nextSoftDeadline);
}
#endif
} else if (nextSoftDeadline == UINT64_MAX) {
// Disarm the timers - there is no timer scheduled
if (rlm->_mkTimerArmed && rlm->_timerPort) {
AbsoluteTime dummy;
mk_timer_cancel(rlm->_timerPort, &dummy);
rlm->_mkTimerArmed = false;
}
#if USE_DISPATCH_SOURCE_FOR_TIMERS
if (rlm->_dispatchTimerArmed) {
_dispatch_source_set_runloop_timer_4CF(rlm->_timerSource, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 333);
rlm->_dispatchTimerArmed = false;
}
#endif
}
}
rlm->_timerHardDeadline = nextHardDeadline;
rlm->_timerSoftDeadline = nextSoftDeadline;
}
// call with rlm and its run loop locked, and the TSRLock locked; rlt not locked; returns with same state
static void __CFRepositionTimerInMode(CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt, Boolean isInArray) __attribute__((noinline));
static void __CFRepositionTimerInMode(CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt, Boolean isInArray) {
if (!rlt) return;
CFMutableArrayRef timerArray = rlm->_timers;
if (!timerArray) return;
Boolean found = false;
// If we know in advance that the timer is not in the array (just being added now) then we can skip this search
if (isInArray) {
CFIndex idx = CFArrayGetFirstIndexOfValue(timerArray, CFRangeMake(0, CFArrayGetCount(timerArray)), rlt);
if (kCFNotFound != idx) {
CFRetain(rlt);
CFArrayRemoveValueAtIndex(timerArray, idx);
found = true;
}
}
if (!found && isInArray) return;
CFIndex newIdx = __CFRunLoopInsertionIndexInTimerArray(timerArray, rlt);
CFArrayInsertValueAtIndex(timerArray, newIdx, rlt);
__CFArmNextTimerInMode(rlm, rlt->_runLoop);
if (isInArray) CFRelease(rlt);
}
// mode and rl are locked on entry and exit
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */
Boolean timerHandled = false;
uint64_t oldFireTSR = 0;
/* Fire a timer */
CFRetain(rlt);
__CFRunLoopTimerLock(rlt);
if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
void *context_info = NULL;
void (*context_release)(const void *) = NULL;
if (rlt->_context.retain) {
context_info = (void *)rlt->_context.retain(rlt->_context.info);
context_release = rlt->_context.release;
} else {
context_info = rlt->_context.info;
}
Boolean doInvalidate = (0.0 == rlt->_interval);
__CFRunLoopTimerSetFiring(rlt);
// Just in case the next timer has exactly the same deadlines as this one, we reset these values so that the arm next timer code can correctly find the next timer in the list and arm the underlying timer.
rlm->_timerSoftDeadline = UINT64_MAX;
rlm->_timerHardDeadline = UINT64_MAX;
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
oldFireTSR = rlt->_fireTSR;
__CFRunLoopTimerFireTSRUnlock();
__CFArmNextTimerInMode(rlm, rl);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
CHECK_FOR_FORK();
if (doInvalidate) {
CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */
}
if (context_release) {
context_release(context_info);
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
__CFRunLoopTimerLock(rlt);
timerHandled = true;
__CFRunLoopTimerUnsetFiring(rlt);
}
if (__CFIsValid(rlt) && timerHandled) {
/* This is just a little bit tricky: we want to support calling
* CFRunLoopTimerSetNextFireDate() from within the callout and
* honor that new time here if it is a later date, otherwise
* it is completely ignored. */
if (oldFireTSR < rlt->_fireTSR) {
/* Next fire TSR was set, and set to a date after the previous
* fire date, so we honor it. */
__CFRunLoopTimerUnlock(rlt);
// The timer was adjusted and repositioned, during the
// callout, but if it was still the min timer, it was
// skipped because it was firing. Need to redo the
// min timer calculation in case rlt should now be that
// timer instead of whatever was chosen.
__CFArmNextTimerInMode(rlm, rl);
} else {
uint64_t nextFireTSR = 0LL;
uint64_t intervalTSR = 0LL;
if (rlt->_interval <= 0.0) {
} else if (TIMER_INTERVAL_LIMIT < rlt->_interval) {
intervalTSR = __CFTimeIntervalToTSR(TIMER_INTERVAL_LIMIT);
} else {
intervalTSR = __CFTimeIntervalToTSR(rlt->_interval);
}
if (LLONG_MAX - intervalTSR <= oldFireTSR) {
nextFireTSR = LLONG_MAX;
} else {
if (intervalTSR == 0) {
// 15304159: Make sure we don't accidentally loop forever here
CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat");
HALT;
}
uint64_t currentTSR = mach_absolute_time();
nextFireTSR = oldFireTSR;
while (nextFireTSR <= currentTSR) {
nextFireTSR += intervalTSR;
}
}
CFRunLoopRef rlt_rl = rlt->_runLoop;
if (rlt_rl) {
CFRetain(rlt_rl);
CFIndex cnt = CFSetGetCount(rlt->_rlModes);
STACK_BUFFER_DECL(CFTypeRef, modes, cnt);
CFSetGetValues(rlt->_rlModes, (const void **)modes);
// To avoid A->B, B->A lock ordering issues when coming up
// towards the run loop from a source, the timer has to be
// unlocked, which means we have to protect from object
// invalidation, although that's somewhat expensive.
for (CFIndex idx = 0; idx < cnt; idx++) {
CFRetain(modes[idx]);
}
__CFRunLoopTimerUnlock(rlt);
for (CFIndex idx = 0; idx < cnt; idx++) {
CFStringRef name = (CFStringRef)modes[idx];
modes[idx] = (CFTypeRef)__CFRunLoopFindMode(rlt_rl, name, false);
CFRelease(name);
}
__CFRunLoopTimerFireTSRLock();
rlt->_fireTSR = nextFireTSR;
rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
for (CFIndex idx = 0; idx < cnt; idx++) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)modes[idx];
if (rlm) {
__CFRepositionTimerInMode(rlm, rlt, true);
}
}
__CFRunLoopTimerFireTSRUnlock();
for (CFIndex idx = 0; idx < cnt; idx++) {
__CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]);
}
CFRelease(rlt_rl);
} else {
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
rlt->_fireTSR = nextFireTSR;
rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
__CFRunLoopTimerFireTSRUnlock();
}
}
} else {
__CFRunLoopTimerUnlock(rlt);
}
CFRelease(rlt);
return timerHandled;
}
// rl and rlm are locked on entry and exit
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) { /* DOES CALLOUT */
Boolean timerHandled = false;
CFMutableArrayRef timers = NULL;
for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) {
if (rlt->_fireTSR <= limitTSR) {
if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(timers, rlt);
}
}
}
for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
timerHandled = timerHandled || did;
}
if (timers) CFRelease(timers);
return timerHandled;
}
CF_EXPORT Boolean _CFRunLoopFinished(CFRunLoopRef rl, CFStringRef modeName) {
CHECK_FOR_FORK();
CFRunLoopModeRef rlm;
Boolean result = false;
__CFRunLoopLock(rl);
rlm = __CFRunLoopFindMode(rl, modeName, false);
if (NULL == rlm || __CFRunLoopModeIsEmpty(rl, rlm, NULL)) {
result = true;
}
if (rlm) __CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) __attribute__((noinline));
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#define TIMEOUT_INFINITY (~(mach_msg_timeout_t)0)
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout, voucher_mach_msg_state_t *voucherState, voucher_t *voucherCopy) {
Boolean originalBuffer = true;
kern_return_t ret = KERN_SUCCESS;
for (;;) { /* In that sleep of death what nightmares may come ... */
mach_msg_header_t *msg = (mach_msg_header_t *)*buffer;
msg->msgh_bits = 0;
msg->msgh_local_port = port;
msg->msgh_remote_port = MACH_PORT_NULL;
msg->msgh_size = buffer_size;
msg->msgh_id = 0;
if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); }
ret = mach_msg(msg, MACH_RCV_MSG|(voucherState ? MACH_RCV_VOUCHER : 0)|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, port, timeout, MACH_PORT_NULL);
// Take care of all voucher-related work right after mach_msg.
// If we don't release the previous voucher we're going to leak it.
voucher_mach_msg_revert(*voucherState);
// Someone will be responsible for calling voucher_mach_msg_revert. This call makes the received voucher the current one.
*voucherState = voucher_mach_msg_adopt(msg);
if (voucherCopy) {
*voucherCopy = NULL;
}
CFRUNLOOP_WAKEUP(ret);
if (MACH_MSG_SUCCESS == ret) {
*livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL;
return true;
}
if (MACH_RCV_TIMED_OUT == ret) {
if (!originalBuffer) free(msg);
*buffer = NULL;
*livePort = MACH_PORT_NULL;
return false;
}
if (MACH_RCV_TOO_LARGE != ret) break;
buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
if (originalBuffer) *buffer = NULL;
originalBuffer = false;
*buffer = realloc(*buffer, buffer_size);
}
HALT;
return false;
}
#elif DEPLOYMENT_TARGET_LINUX && !TARGET_OS_CYGWIN
#define TIMEOUT_INFINITY UINT64_MAX
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);
</