| /* |
| * Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved. |
| * Copyright (c) 2009 Grant Erickson <gerickson@nuovations.com>. All rights reserved. |
| * |
| * This source code is a modified version of the CoreFoundation sources released by Apple Inc. under |
| * the terms of the APSL version 2.0 (see below). |
| * |
| * For information about changes from the original Apple source release can be found by reviewing the |
| * source control system for the project at https://sourceforge.net/svn/?group_id=246198. |
| * |
| * The original license information is as follows: |
| * |
| * Copyright (c) 2008 Apple Inc. All rights reserved. |
| * |
| * @APPLE_LICENSE_HEADER_START@ |
| * |
| * This file contains Original Code and/or Modifications of Original Code |
| * as defined in and that are subject to the Apple Public Source License |
| * Version 2.0 (the 'License'). You may not use this file except in |
| * compliance with the License. Please obtain a copy of the License at |
| * http://www.opensource.apple.com/apsl/ and read it before using this |
| * file. |
| * |
| * The Original Code and all software distributed under the License are |
| * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| * Please see the License for the specific language governing rights and |
| * limitations under the License. |
| * |
| * @APPLE_LICENSE_HEADER_END@ |
| */ |
| /* CFRunLoop.c |
| Copyright 1998-2002, Apple, Inc. All rights reserved. |
| Responsibility: Christopher Kane |
| */ |
| |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| |
| #include <CoreFoundation/CFRunLoop.h> |
| #include <CoreFoundation/CFSet.h> |
| #include <CoreFoundation/CFBag.h> |
| #include "CFInternal.h" |
| #include <math.h> |
| #include <stdio.h> |
| #include <limits.h> |
| #if DEPLOYMENT_TARGET_MACOSX |
| #include <mach/mach.h> |
| #include <mach/clock_types.h> |
| #include <mach/clock.h> |
| #include <unistd.h> |
| #include <dlfcn.h> |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| #if !defined(__MINGW32__) && !defined(__CYGWIN__) |
| // With the MS headers, turning off Standard-C gets you macros for stat vs _stat. |
| // Strictly speaking, this is supposed to control traditional vs ANSI C features. |
| #undef __STDC__ |
| #endif |
| #include <windows.h> |
| #include <process.h> |
| #if !defined(__MINGW32__) && !defined(__CYGWIN__) |
| #define __STDC__ |
| #endif |
| #elif DEPLOYMENT_TARGET_LINUX |
| #include <dlfcn.h> |
| #include <pthread.h> |
| #include <semaphore.h> |
| #endif |
| |
| static int _LogCFRunLoop = 0; |
| |
| #if 0 || 0 |
| static pthread_t kNilThreadT = { nil, nil }; |
| #define pthreadPointer(a) a.p |
| #define lockCount(a) a.LockCount |
| #define NativeThread pthread_t |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| DWORD GetInternalThreadId(HANDLE t); |
| |
| static HANDLE kNilThreadT = 0; |
| static DWORD __kCFMainThread = 0; |
| #define pthreadPointer(a) ((void*)GetInternalThreadId(a)) |
| // Note: Windows RTL_CRITICAL_SECTION LockCount is initialized to -1 |
| // indicating no lock. So add one so lock count shows a valid 'count'. |
| #define lockCount(a) (a.LockCount + 1) |
| #define NativeThread HANDLE |
| #elif DEPLOYMENT_TARGET_LINUX |
| static pthread_t kNilThreadT = (pthread_t)0; |
| static pthread_t __kCFMainThread = (pthread_t)0; |
| #define pthreadPointer(a) (void *)a |
| #define lockCount(a) a.lock |
| #define NativeThread pthread_t |
| #else |
| static pthread_t kNilThreadT = (pthread_t)0; |
| #define pthreadPointer(a) a |
| #define lockCount(a) a |
| #define NativeThread pthread_t |
| #endif |
| |
| #if DEPLOYMENT_TARGET_MACOSX |
| #include <sys/types.h> |
| #include <sys/event.h> |
| |
| typedef struct { |
| CFIndex version; |
| void * info; |
| const void *(*retain)(const void *info); |
| void (*release)(const void *info); |
| CFStringRef (*copyDescription)(const void *info); |
| Boolean (*equal)(const void *info1, const void *info2); |
| CFHashCode (*hash)(const void *info); |
| void (*perform)(const struct kevent *kev, void *info); |
| struct kevent event; |
| } CFRunLoopSourceContext2; |
| |
| // The bits in the flags field in the kevent structure are cleared except for EV_ONESHOT and EV_CLEAR. |
| // Do not use the udata field of the kevent structure -- that field is smashed by CFRunLoop. |
| // There is no way to EV_ENABLE or EV_DISABLE a kevent. |
| // The "autoinvalidation" of EV_ONESHOT is not handled properly by CFRunLoop yet. |
| // The "autoinvalidation" of EV_DELETE on the last close of a file descriptor is not handled properly by CFRunLoop yet. |
| // There is no way to reset the state in a kevent (such as clearing the EV_EOF state for fifos). |
| #endif |
| |
| extern bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey); |
| |
| // 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 |
| |
| typedef mach_port_t __CFPort; |
| #define CFPORT_NULL MACH_PORT_NULL |
| #define CFPORTSET_NULL MACH_PORT_NULL |
| typedef mach_port_t __CFPortSet; |
| |
| static __CFPort __CFPortAllocate(void) { |
| __CFPort result; |
| kern_return_t ret; |
| ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &result); |
| if (KERN_SUCCESS == ret) { |
| ret = mach_port_insert_right(mach_task_self(), result, result, MACH_MSG_TYPE_MAKE_SEND); |
| } |
| if (KERN_SUCCESS == 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); |
| } |
| return (KERN_SUCCESS == ret) ? result : CFPORT_NULL; |
| } |
| |
| CF_INLINE void __CFPortFree(__CFPort port) { |
| mach_port_destroy(mach_task_self(), port); |
| } |
| |
| CF_INLINE __CFPortSet __CFPortSetAllocate(void) { |
| __CFPortSet result; |
| kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &result); |
| return (KERN_SUCCESS == ret) ? result : CFPORT_NULL; |
| } |
| |
| CF_INLINE Boolean __CFPortSetInsert(__CFPort port, __CFPortSet portSet) { |
| kern_return_t ret = mach_port_insert_member(mach_task_self(), port, portSet); |
| return (KERN_SUCCESS == ret); |
| } |
| |
| CF_INLINE Boolean __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { |
| kern_return_t ret = mach_port_extract_member(mach_task_self(), port, portSet); |
| return (KERN_SUCCESS == ret); |
| } |
| |
| CF_INLINE void __CFPortSetFree(__CFPortSet portSet) { |
| kern_return_t ret; |
| mach_port_name_array_t array; |
| mach_msg_type_number_t idx, number; |
| |
| ret = mach_port_get_set_status(mach_task_self(), portSet, &array, &number); |
| if (KERN_SUCCESS == ret) { |
| for (idx = 0; idx < number; idx++) { |
| mach_port_extract_member(mach_task_self(), array[idx], portSet); |
| } |
| vm_deallocate(mach_task_self(), (vm_address_t)array, number * sizeof(mach_port_name_t)); |
| } |
| mach_port_destroy(mach_task_self(), portSet); |
| } |
| |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| |
| typedef HANDLE __CFPort; |
| #define CFPORT_NULL NULL |
| #define CFPORTSET_NULL NULL |
| #define MAX_PORTS MAXIMUM_WAIT_OBJECTS |
| |
| CF_INLINE __CFPort __CFPortAllocate(void) { |
| return CreateEvent(NULL, true, false, NULL); |
| } |
| |
| CF_INLINE void __CFPortFree(__CFPort port) { |
| CloseHandle(port); |
| } |
| |
| #elif DEPLOYMENT_TARGET_LINUX |
| |
| typedef sem_t * __CFPort; |
| #define CFPORT_NULL NULL |
| #define CFPORTSET_NULL NULL |
| #define MAX_PORTS 16 |
| |
| CF_INLINE __CFPort __CFPortAllocate(void) { |
| __CFPort port = CFPORT_NULL; |
| int status; |
| |
| port = (__CFPort)CFAllocatorAllocate(kCFAllocatorSystemDefault, |
| sizeof(sem_t), 0); |
| CFAssert1(port != CFPORT_NULL, __kCFLogAssertion, |
| "%s(): Could not allocate port memory.", __PRETTY_FUNCTION__); |
| if (port != CFPORT_NULL) { |
| status = sem_init(port, false, 0); |
| CFAssert1(status == 0, __kCFLogAssertion, |
| "%s(): Could not initialize port.", __PRETTY_FUNCTION__); |
| } |
| |
| return port; |
| } |
| |
| CF_INLINE void __CFPortFree(__CFPort port) { |
| int status; |
| CFAssert1(port != CFPORT_NULL, __kCFLogAssertion, |
| "%s(): Attemping to free an invalid port.", __PRETTY_FUNCTION__); |
| status = sem_destroy(port); |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, port); |
| } |
| |
| #endif /* DEPLOYMENT_TARGET_MACOSX */ |
| |
| #if !DEPLOYMENT_TARGET_MACOSX |
| |
| // A simple dynamic array of __CFPorts, which grows to a high-water mark |
| typedef struct ___CFPortSet { |
| uint16_t used; |
| uint16_t size; |
| __CFPort *ports; |
| CFSpinLock_t lock; // insert and remove must be thread safe, like the Mach calls |
| } *__CFPortSet; |
| |
| static __CFPortSet __CFPortSetAllocate(void) { |
| __CFPortSet result = (__CFPortSet)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct ___CFPortSet), 0); |
| result->used = 0; |
| result->size = 4; |
| result->ports = (__CFPort *)CFAllocatorAllocate(kCFAllocatorSystemDefault, result->size * sizeof(__CFPort), 0); |
| CF_SPINLOCK_INIT_FOR_STRUCTS(result->lock); |
| return result; |
| } |
| |
| static void __CFPortSetFree(__CFPortSet portSet) { |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, portSet->ports); |
| 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) { |
| __CFSpinLock(&(portSet->lock)); |
| __CFPort *result = portBuf; |
| if (bufSize > portSet->used) |
| result = (__CFPort *)CFAllocatorAllocate(kCFAllocatorSystemDefault, portSet->used * sizeof(__CFPort), 0); |
| memmove(result, portSet->ports, portSet->used * sizeof(__CFPort)); |
| *portsUsed = portSet->used; |
| __CFSpinUnlock(&(portSet->lock)); |
| return result; |
| } |
| |
| static Boolean __CFPortSetInsert(__CFPort port, __CFPortSet portSet) { |
| __CFSpinLock(&(portSet->lock)); |
| if (portSet->used >= portSet->size) { |
| portSet->size += 4; |
| portSet->ports = (__CFPort*)CFAllocatorReallocate(kCFAllocatorSystemDefault, portSet->ports, portSet->size * sizeof(__CFPort), 0); |
| } |
| if (portSet->used >= MAX_PORTS) |
| CFLog(kCFLogLevelWarning, CFSTR("*** More than %d ports added to a port set. The last ones will be ignored."), MAX_PORTS); |
| portSet->ports[portSet->used++] = port; |
| __CFSpinUnlock(&(portSet->lock)); |
| return true; |
| } |
| |
| static Boolean __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { |
| int i, j; |
| __CFSpinLock(&(portSet->lock)); |
| for (i = 0; i < portSet->used; i++) { |
| if (portSet->ports[i] == port) { |
| for (j = i+1; j < portSet->used; j++) { |
| portSet->ports[j-1] = portSet->ports[j]; |
| } |
| portSet->used--; |
| __CFSpinUnlock(&(portSet->lock)); |
| return true; |
| } |
| } |
| __CFSpinUnlock(&(portSet->lock)); |
| return false; |
| } |
| |
| #endif |
| |
| #if DEPLOYMENT_TARGET_MACOSX |
| 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_arm(mach_port_name_t name, AbsoluteTime expire_time); |
| extern kern_return_t mk_timer_cancel(mach_port_name_t name, AbsoluteTime *result_time); |
| |
| CF_INLINE AbsoluteTime __CFUInt64ToAbsoluteTime(int64_t x) { |
| AbsoluteTime a; |
| a.hi = x >> 32; |
| a.lo = x & (int64_t)0xFFFFFFFF; |
| return a; |
| } |
| |
| 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; |
| } |
| #endif |
| |
| /* 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; |
| CFSpinLock_t _lock; /* must have the run loop locked before locking this */ |
| CFStringRef _name; |
| Boolean _stopped; |
| char _padding[3]; |
| CFMutableSetRef _sources; |
| CFMutableSetRef _observers; |
| CFMutableSetRef _timers; |
| CFMutableArrayRef _submodes; // names of the submodes |
| __CFPortSet _portSet; |
| #if DEPLOYMENT_TARGET_MACOSX |
| int _kq; |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| DWORD _msgQMask; |
| #endif |
| }; |
| |
| static int64_t __CFRunLoopGetNextTimerFireTSR(CFRunLoopRef rl, CFRunLoopModeRef rlm); |
| |
| CF_INLINE void __CFRunLoopModeLock(CFRunLoopModeRef rlm) { |
| __CFSpinLock(&(rlm->_lock)); |
| } |
| |
| CF_INLINE void __CFRunLoopModeUnlock(CFRunLoopModeRef rlm) { |
| __CFSpinUnlock(&(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); |
| #if DEPLOYMENT_TARGET_MACOSX |
| CFStringAppendFormat(result, NULL, CFSTR("port set = %p,"), rlm->_portSet); |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| CFStringAppendFormat(result, NULL, CFSTR("MSGQ mask = %p,"), rlm->_msgQMask); |
| #endif |
| CFStringAppendFormat(result, NULL, CFSTR("\n\tsources = %@,\n\tobservers == %@,\n\ttimers = %@\n},\n"), rlm->_sources, rlm->_observers, rlm->_timers); |
| return result; |
| } |
| |
| static void __CFRunLoopModeDeallocate(CFTypeRef cf) { |
| CFRunLoopModeRef rlm = (CFRunLoopModeRef)cf; |
| if (NULL != rlm->_sources) CFRelease(rlm->_sources); |
| if (NULL != rlm->_observers) CFRelease(rlm->_observers); |
| if (NULL != rlm->_timers) CFRelease(rlm->_timers); |
| if (NULL != rlm->_submodes) CFRelease(rlm->_submodes); |
| CFRelease(rlm->_name); |
| __CFPortSetFree(rlm->_portSet); |
| #if DEPLOYMENT_TARGET_MACOSX |
| if (-1 != rlm->_kq) close(rlm->_kq); |
| #endif |
| } |
| |
| struct __CFRunLoop { |
| CFRuntimeBase _base; |
| CFSpinLock_t _lock; /* locked for accessing mode list */ |
| __CFPort _wakeUpPort; // used for CFRunLoopWakeUp |
| volatile uint32_t *_stopped; |
| CFMutableSetRef _commonModes; |
| CFMutableSetRef _commonModeItems; |
| CFRunLoopModeRef _currentMode; |
| CFMutableSetRef _modes; |
| void *_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 Boolean __CFRunLoopIsStopped(CFRunLoopRef rl) { |
| return (rl->_stopped && rl->_stopped[2]) ? true : false; |
| } |
| |
| CF_INLINE void __CFRunLoopSetStopped(CFRunLoopRef rl) { |
| if (rl->_stopped) rl->_stopped[2] = 0x53544F50; // 'STOP' |
| } |
| |
| CF_INLINE void __CFRunLoopUnsetStopped(CFRunLoopRef rl) { |
| if (rl->_stopped) rl->_stopped[2] = 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) { |
| __CFSpinLock(&(((CFRunLoopRef)rl)->_lock)); |
| } |
| |
| CF_INLINE void __CFRunLoopUnlock(CFRunLoopRef rl) { |
| __CFSpinUnlock(&(((CFRunLoopRef)rl)->_lock)); |
| } |
| |
| static CFStringRef __CFRunLoopCopyDescription(CFTypeRef cf) { |
| CFRunLoopRef rl = (CFRunLoopRef)cf; |
| CFMutableStringRef result; |
| result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); |
| CFStringAppendFormat(result, NULL, CFSTR("<CFRunLoop %p [%p]>{wait port = 0x%x, stopped = %s,\ncurrent mode = %@,\n"), cf, CFGetAllocator(cf), rl->_wakeUpPort, (rl->_stopped && (rl->_stopped[2] == 0x53544F50)) ? "true" : "false", rl->_currentMode ? rl->_currentMode->_name : CFSTR("(none)")); |
| CFStringAppendFormat(result, NULL, CFSTR("common modes = %@,\ncommon mode items = %@,\nmodes = %@}\n"), rl->_commonModes, rl->_commonModeItems, rl->_modes); |
| return result; |
| } |
| |
| /* call with rl locked */ |
| static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeName, Boolean create) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| struct __CFRunLoopMode srlm; |
| srlm._base._cfisa = __CFISAForTypeID(__kCFRunLoopModeTypeID); |
| srlm._base._cfinfo[CF_INFO_BITS] = 0; |
| _CFRuntimeSetInstanceTypeID(&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(CFGetAllocator(rl), __kCFRunLoopModeTypeID, sizeof(struct __CFRunLoopMode) - sizeof(CFRuntimeBase), NULL); |
| if (NULL == rlm) { |
| return NULL; |
| } |
| CF_SPINLOCK_INIT_FOR_STRUCTS(rlm->_lock); |
| rlm->_name = CFStringCreateCopy(CFGetAllocator(rlm), modeName); |
| rlm->_stopped = false; |
| rlm->_sources = NULL; |
| rlm->_observers = NULL; |
| rlm->_timers = NULL; |
| rlm->_submodes = NULL; |
| rlm->_portSet = __CFPortSetAllocate(); |
| if (CFPORTSET_NULL == rlm->_portSet) HALT; |
| if (!__CFPortSetInsert(rl->_wakeUpPort, rlm->_portSet)) HALT; |
| #if DEPLOYMENT_TARGET_MACOSX |
| rlm->_kq = -1; |
| #elif DELPOYMENT_TARGET_WIN32 |
| rlm->_msgQMask = 0; |
| #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) { |
| CHECK_FOR_FORK(); |
| if (NULL == rlm) return true; |
| #if DEPLOYMENT_TARGET_WINDOWS |
| if (0 != rlm->_msgQMask) return false; |
| #endif |
| if (NULL != rlm->_sources && 0 < CFSetGetCount(rlm->_sources)) return false; |
| if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) return false; |
| if (NULL != rlm->_submodes) { |
| CFIndex idx, cnt; |
| for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { |
| CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); |
| CFRunLoopModeRef subrlm; |
| Boolean subIsEmpty; |
| subrlm = __CFRunLoopFindMode(rl, modeName, false); |
| subIsEmpty = (NULL != subrlm) ? __CFRunLoopModeIsEmpty(rl, subrlm) : true; |
| if (NULL != subrlm) __CFRunLoopModeUnlock(subrlm); |
| if (!subIsEmpty) return false; |
| } |
| } |
| return true; |
| } |
| |
| #if DEPLOYMENT_TARGET_WINDOWS |
| DWORD __CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef modeName) { |
| CFRunLoopModeRef rlm; |
| DWORD result = 0; |
| __CFRunLoopLock(rl); |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| if (rlm) { |
| result = rlm->_msgQMask; |
| __CFRunLoopModeUnlock(rlm); |
| } |
| __CFRunLoopUnlock(rl); |
| return result; |
| } |
| |
| void __CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, DWORD mask, CFStringRef modeName) { |
| CFRunLoopModeRef rlm; |
| __CFRunLoopLock(rl); |
| rlm = __CFRunLoopFindMode(rl, modeName, true); |
| rlm->_msgQMask = mask; |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopUnlock(rl); |
| } |
| #endif |
| |
| /* 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; |
| CFSpinLock_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) { |
| __CFSpinLock(&(rls->_lock)); |
| } |
| |
| CF_INLINE void __CFRunLoopSourceUnlock(CFRunLoopSourceRef rls) { |
| __CFSpinUnlock(&(rls->_lock)); |
| } |
| |
| /* rlm is not locked */ |
| static void __CFRunLoopSourceSchedule(CFRunLoopSourceRef rls, CFRunLoopRef rl, CFRunLoopModeRef rlm) { /* DOES CALLOUT */ |
| __CFRunLoopSourceLock(rls); |
| if (NULL == rls->_runLoops) { |
| rls->_runLoops = CFBagCreateMutable(CFGetAllocator(rls), 0, NULL); |
| } |
| CFBagAddValue(rls->_runLoops, rl); |
| __CFRunLoopSourceUnlock(rls); // have to unlock before the callout -- cannot help clients with safety |
| if (0 == rls->_context.version0.version) { |
| if (NULL != rls->_context.version0.schedule) { |
| rls->_context.version0.schedule(rls->_context.version0.info, rl, rlm->_name); |
| } |
| } else if (1 == rls->_context.version0.version) { |
| __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ |
| if (CFPORT_NULL != port) { |
| __CFPortSetInsert(port, rlm->_portSet); |
| } |
| } |
| } |
| |
| /* rlm is not locked */ |
| static void __CFRunLoopSourceCancel(CFRunLoopSourceRef rls, CFRunLoopRef rl, CFRunLoopModeRef rlm) { /* DOES CALLOUT */ |
| 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); |
| } |
| } |
| __CFRunLoopSourceLock(rls); |
| if (NULL != rls->_runLoops) { |
| CFBagRemoveValue(rls->_runLoops, rl); |
| } |
| __CFRunLoopSourceUnlock(rls); |
| } |
| |
| struct __CFRunLoopObserver { |
| CFRuntimeBase _base; |
| CFSpinLock_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) { |
| __CFSpinLock(&(rlo->_lock)); |
| } |
| |
| CF_INLINE void __CFRunLoopObserverUnlock(CFRunLoopObserverRef rlo) { |
| __CFSpinUnlock(&(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); |
| } |
| |
| struct __CFRunLoopTimer { |
| CFRuntimeBase _base; |
| CFSpinLock_t _lock; |
| CFRunLoopRef _runLoop; |
| CFIndex _rlCount; |
| #if DEPLOYMENT_TARGET_MACOSX |
| mach_port_name_t _port; |
| #endif |
| CFIndex _order; /* immutable */ |
| int64_t _fireTSR; /* TSR units */ |
| int64_t _intervalTSR; /* immutable; 0 means non-repeating; TSR units */ |
| 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 */ |
| |
| CF_INLINE Boolean __CFRunLoopTimerIsFiring(CFRunLoopTimerRef rlt) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 0, 0); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerSetFiring(CFRunLoopTimerRef rlt) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 0, 0, 1); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerUnsetFiring(CFRunLoopTimerRef rlt) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 0, 0, 0); |
| } |
| |
| CF_INLINE Boolean __CFRunLoopTimerDidFire(CFRunLoopTimerRef rlt) { |
| return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 1, 1); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerSetDidFire(CFRunLoopTimerRef rlt) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 1, 1, 1); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerUnsetDidFire(CFRunLoopTimerRef rlt) { |
| __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_cfinfo[CF_INFO_BITS], 1, 1, 0); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerLock(CFRunLoopTimerRef rlt) { |
| __CFSpinLock(&(rlt->_lock)); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerUnlock(CFRunLoopTimerRef rlt) { |
| __CFSpinUnlock(&(rlt->_lock)); |
| } |
| |
| static CFSpinLock_t __CFRLTFireTSRLock = CFSpinLockInit; |
| |
| CF_INLINE void __CFRunLoopTimerFireTSRLock(void) { |
| __CFSpinLock(&__CFRLTFireTSRLock); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerFireTSRUnlock(void) { |
| __CFSpinUnlock(&__CFRLTFireTSRLock); |
| } |
| |
| #if DEPLOYMENT_TARGET_MACOSX |
| static CFMutableDictionaryRef __CFRLTPortMap = NULL; |
| static CFSpinLock_t __CFRLTPortMapLock = CFSpinLockInit; |
| |
| CF_INLINE void __CFRunLoopTimerPortMapLock(void) { |
| __CFSpinLock(&__CFRLTPortMapLock); |
| } |
| |
| CF_INLINE void __CFRunLoopTimerPortMapUnlock(void) { |
| __CFSpinUnlock(&__CFRLTPortMapLock); |
| } |
| #endif |
| |
| static void __CFRunLoopTimerSchedule(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFRunLoopModeRef rlm) { |
| #if DEPLOYMENT_TARGET_MACOSX |
| __CFRunLoopTimerLock(rlt); |
| if (0 == rlt->_rlCount) { |
| rlt->_runLoop = rl; |
| if (MACH_PORT_NULL == rlt->_port) { |
| rlt->_port = mk_timer_create(); |
| } |
| __CFRunLoopTimerPortMapLock(); |
| if (NULL == __CFRLTPortMap) { |
| __CFRLTPortMap = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); |
| } |
| CFDictionarySetValue(__CFRLTPortMap, (void *)(uintptr_t)rlt->_port, rlt); |
| __CFRunLoopTimerPortMapUnlock(); |
| } |
| rlt->_rlCount++; |
| mach_port_insert_member(mach_task_self(), rlt->_port, rlm->_portSet); |
| mk_timer_arm(rlt->_port, __CFUInt64ToAbsoluteTime(rlt->_fireTSR)); |
| __CFRunLoopTimerUnlock(rlt); |
| #endif |
| } |
| |
| static void __CFRunLoopTimerCancel(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFRunLoopModeRef rlm) { |
| #if DEPLOYMENT_TARGET_MACOSX |
| __CFRunLoopTimerLock(rlt); |
| __CFPortSetRemove(rlt->_port, rlm->_portSet); |
| rlt->_rlCount--; |
| if (0 == rlt->_rlCount) { |
| __CFRunLoopTimerPortMapLock(); |
| if (NULL != __CFRLTPortMap) { |
| CFDictionaryRemoveValue(__CFRLTPortMap, (void *)(uintptr_t)rlt->_port); |
| } |
| __CFRunLoopTimerPortMapUnlock(); |
| rlt->_runLoop = NULL; |
| mk_timer_cancel(rlt->_port, NULL); |
| } |
| __CFRunLoopTimerUnlock(rlt); |
| #endif |
| } |
| |
| // Caller must hold the Timer lock for safety |
| static void __CFRunLoopTimerRescheduleWithAllModes(CFRunLoopTimerRef rlt, CFRunLoopRef rl) { |
| #if DEPLOYMENT_TARGET_MACOSX |
| mk_timer_arm(rlt->_port, __CFUInt64ToAbsoluteTime(rlt->_fireTSR)); |
| #endif |
| } |
| |
| #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| typedef struct _collectTimersContext { |
| CFMutableArrayRef results; |
| int64_t cutoffTSR; |
| }; |
| |
| static void __CFRunLoopCollectTimers(const void *value, void *ctx) { |
| CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)value; |
| struct _collectTimersContext *context = (struct _collectTimersContext*)ctx; |
| if (rlt->_fireTSR <= context->cutoffTSR) { |
| if (NULL == context->results) |
| context->results = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); |
| CFArrayAppendValue(context->results, rlt); |
| } |
| } |
| |
| // RunLoop and RunLoopMode must be locked |
| static void __CFRunLoopTimersToFireRecursive(CFRunLoopRef rl, CFRunLoopModeRef rlm, struct _collectTimersContext *ctxt) { |
| if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) { |
| __CFRunLoopTimerFireTSRLock(); |
| CFSetApplyFunction(rlm->_timers, __CFRunLoopCollectTimers, ctxt); |
| __CFRunLoopTimerFireTSRUnlock(); |
| } |
| if (NULL != rlm->_submodes) { |
| CFIndex idx, cnt; |
| for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { |
| CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); |
| CFRunLoopModeRef subrlm; |
| subrlm = __CFRunLoopFindMode(rl, modeName, false); |
| if (NULL != subrlm) { |
| __CFRunLoopTimersToFireRecursive(rl, subrlm, ctxt); |
| __CFRunLoopModeUnlock(subrlm); |
| } |
| } |
| } |
| } |
| |
| // RunLoop and RunLoopMode must be locked |
| static CFArrayRef __CFRunLoopTimersToFire(CFRunLoopRef rl, CFRunLoopModeRef rlm) { |
| struct _collectTimersContext ctxt = {NULL, __CFReadTSR()}; |
| __CFRunLoopTimersToFireRecursive(rl, rlm, &ctxt); |
| return ctxt.results; |
| } |
| #endif // DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| |
| /* CFRunLoop */ |
| |
| CONST_STRING_DECL(kCFRunLoopDefaultMode, "kCFRunLoopDefaultMode") |
| CONST_STRING_DECL(kCFRunLoopCommonModes, "kCFRunLoopCommonModes") |
| |
| struct _findsource { |
| __CFPort port; |
| CFRunLoopSourceRef result; |
| }; |
| |
| static void __CFRunLoopFindSource(const void *value, void *ctx) { |
| CFRunLoopSourceRef rls = (CFRunLoopSourceRef)value; |
| struct _findsource *context = (struct _findsource *)ctx; |
| __CFPort port; |
| if (NULL != context->result) return; |
| if (1 != rls->_context.version0.version) return; |
| __CFRunLoopSourceLock(rls); |
| port = rls->_context.version1.getPort(rls->_context.version1.info); |
| if (port == context->port) { |
| context->result = rls; |
| } |
| __CFRunLoopSourceUnlock(rls); |
| } |
| |
| // call with rl and rlm locked |
| static CFRunLoopSourceRef __CFRunLoopModeFindSourceForMachPort(CFRunLoopRef rl, CFRunLoopModeRef rlm, __CFPort port) { /* DOES CALLOUT */ |
| CHECK_FOR_FORK(); |
| struct _findsource context = {port, NULL}; |
| if (NULL != rlm->_sources) { |
| CFSetApplyFunction(rlm->_sources, (__CFRunLoopFindSource), &context); |
| } |
| if (NULL == context.result && NULL != rlm->_submodes) { |
| CFIndex idx, cnt; |
| for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { |
| CFRunLoopSourceRef source = NULL; |
| CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); |
| CFRunLoopModeRef subrlm; |
| subrlm = __CFRunLoopFindMode(rl, modeName, false); |
| if (NULL != subrlm) { |
| source = __CFRunLoopModeFindSourceForMachPort(rl, subrlm, port); |
| __CFRunLoopModeUnlock(subrlm); |
| } |
| if (NULL != source) { |
| context.result = source; |
| break; |
| } |
| } |
| } |
| return context.result; |
| } |
| |
| #if DEPLOYMENT_TARGET_MACOSX |
| // call with rl and rlm locked |
| static CFRunLoopTimerRef __CFRunLoopModeFindTimerForMachPort(CFRunLoopModeRef rlm, __CFPort port) { |
| CHECK_FOR_FORK(); |
| CFRunLoopTimerRef result = NULL; |
| __CFRunLoopTimerPortMapLock(); |
| if (NULL != __CFRLTPortMap) { |
| result = (CFRunLoopTimerRef)CFDictionaryGetValue(__CFRLTPortMap, (void *)(uintptr_t)port); |
| } |
| __CFRunLoopTimerPortMapUnlock(); |
| return result; |
| } |
| #endif |
| |
| 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->_sources) return; |
| cnt = CFSetGetCount(rlm->_sources); |
| list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); |
| CFSetGetValues(rlm->_sources, list); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRetain(list[idx]); |
| } |
| CFSetRemoveAllValues(rlm->_sources); |
| for (idx = 0; idx < cnt; idx++) { |
| __CFRunLoopSourceCancel((CFRunLoopSourceRef)list[idx], rl, rlm); |
| CFRelease(list[idx]); |
| } |
| 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 = CFSetGetCount(rlm->_observers); |
| list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); |
| CFSetGetValues(rlm->_observers, list); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRetain(list[idx]); |
| } |
| CFSetRemoveAllValues(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; |
| CFRunLoopRef rl = (CFRunLoopRef)context; |
| CFIndex idx, cnt; |
| const void **list, *buffer[256]; |
| if (NULL == rlm->_timers) return; |
| cnt = CFSetGetCount(rlm->_timers); |
| list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); |
| CFSetGetValues(rlm->_timers, list); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRetain(list[idx]); |
| } |
| CFSetRemoveAllValues(rlm->_timers); |
| for (idx = 0; idx < cnt; idx++) { |
| __CFRunLoopTimerCancel((CFRunLoopTimerRef)list[idx], rl, rlm); |
| CFRelease(list[idx]); |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| } |
| |
| static void __CFRunLoopDeallocate(CFTypeRef cf) { |
| CFRunLoopRef rl = (CFRunLoopRef)cf; |
| /* 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, (__CFRunLoopDeallocateSources), rl); |
| CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateObservers), rl); |
| CFSetApplyFunction(rl->_modes, (__CFRunLoopDeallocateTimers), rl); |
| } |
| __CFRunLoopLock(rl); |
| 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; |
| __CFRunLoopUnlock(rl); |
| } |
| |
| 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_INLINE NativeThread _CFThreadSelf(void) { |
| #if DEPLOYMENT_TARGET_MACOS || DEPLOYMENT_TARGET_FREEBSD || DEPLOYMENT_TARGET_LINUX |
| return pthread_self(); |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| return GetCurrentThread(); |
| #else |
| HALT; |
| return kNilThreadT; |
| #endif |
| } |
| |
| CF_INLINE Boolean _CFThreadIsMain(void) { |
| Boolean result = false; |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_FREEBSD |
| result = pthread_main_np(); |
| #elif DEPLOYMENT_TARGET_LINUX |
| result = (__kCFMainThread == _CFThreadSelf()); |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| result = (__kCFMainThread == GetCurrentThreadId()); |
| #else |
| HALT; |
| #endif |
| return result; |
| } |
| |
| CF_INLINE Boolean _CFThreadsAreEqual(NativeThread a, NativeThread b) { |
| Boolean result = false; |
| #if DEPLOYMENT_TARGET_MACOS || DEPLOYMENT_TARGET_FREEBSD || DEPLOYMENT_TARGET_LINUX |
| result = pthread_equal(a, b); |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| result = (pthreadPointer(a) == pthreadPointer(b)); |
| #else |
| HALT; |
| #endif |
| return result; |
| } |
| |
| __private_extern__ void __CFRunLoopInitialize(void) { |
| __kCFRunLoopTypeID = _CFRuntimeRegisterClass(&__CFRunLoopClass); |
| __kCFRunLoopModeTypeID = _CFRuntimeRegisterClass(&__CFRunLoopModeClass); |
| #if DEPLOYMENT_TARGET_LINUX |
| __kCFMainThread = _CFThreadSelf(); |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| __kCFMainThread = GetInternalThreadId (_CFThreadSelf()); |
| #endif |
| } |
| |
| CFTypeID CFRunLoopGetTypeID(void) { |
| return __kCFRunLoopTypeID; |
| } |
| |
| static CFRunLoopRef __CFRunLoopCreate(void) { |
| CFRunLoopRef loop = NULL; |
| CFRunLoopModeRef rlm; |
| uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase); |
| loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopTypeID, size, NULL); |
| if (NULL == loop) { |
| return NULL; |
| } |
| loop->_stopped = NULL; |
| CF_SPINLOCK_INIT_FOR_STRUCTS(loop->_lock); |
| loop->_wakeUpPort = __CFPortAllocate(); |
| if (CFPORT_NULL == loop->_wakeUpPort) HALT; |
| loop->_commonModes = CFSetCreateMutable(CFGetAllocator(loop), 0, &kCFTypeSetCallBacks); |
| CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode); |
| loop->_commonModeItems = NULL; |
| loop->_currentMode = NULL; |
| loop->_modes = CFSetCreateMutable(CFGetAllocator(loop), 0, &kCFTypeSetCallBacks); |
| _CFSetSetCapacity(loop->_modes, 10); |
| loop->_counterpart = NULL; |
| rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true); |
| if (NULL != rlm) __CFRunLoopModeUnlock(rlm); |
| return loop; |
| } |
| |
| static CFMutableDictionaryRef runLoops = NULL; |
| static char setMainLoop = 0; |
| static CFSpinLock_t loopsLock = CFSpinLockInit; |
| |
| // If this is called on a non-main thread, and the main thread pthread_t is passed in, |
| // and this has not yet beed called on the main thread (since the last fork(), this will |
| // produce a different run loop that will probably be tossed away eventually, than the |
| // main thread run loop. There's nothing much we can do about that, without a call to |
| // fetch the main thread's pthread_t from the pthreads subsystem. |
| |
| // t==0 is a synonym for "main thread" that always works |
| __private_extern__ CFRunLoopRef _CFRunLoop0(NativeThread t) { |
| CFRunLoopRef loop = NULL; |
| __CFSpinLock(&loopsLock); |
| if (!runLoops) { |
| __CFSpinUnlock(&loopsLock); |
| CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); |
| CFRunLoopRef mainLoop = __CFRunLoopCreate(); |
| CFDictionarySetValue(dict, 0, mainLoop); |
| if (!_CFAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&runLoops)) { |
| CFRelease(dict); |
| CFRelease(mainLoop); |
| } |
| __CFSpinLock(&loopsLock); |
| } |
| if (_CFThreadIsMain() && _CFThreadsAreEqual(t, _CFThreadSelf())) { |
| t = kNilThreadT; |
| } |
| loop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(t)); |
| if (!loop) { |
| __CFSpinUnlock(&loopsLock); |
| CFRunLoopRef newLoop = __CFRunLoopCreate(); |
| __CFGetThreadSpecificData(); // just cause the thread finalizer to be called as a side effect |
| __CFSpinLock(&loopsLock); |
| loop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(t)); |
| if (loop) { |
| CFRelease(newLoop); |
| } else { |
| CFDictionarySetValue(runLoops, pthreadPointer(t), newLoop); |
| loop = newLoop; |
| } |
| } |
| if (!setMainLoop && _CFThreadIsMain()) { |
| if (_CFThreadsAreEqual(t, kNilThreadT)) { |
| CFDictionarySetValue(runLoops, pthreadPointer(_CFThreadSelf()), loop); |
| } else { |
| CFRunLoopRef mainLoop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(kNilThreadT)); |
| CFDictionarySetValue(runLoops, pthreadPointer(_CFThreadSelf()), mainLoop); |
| } |
| setMainLoop = 1; |
| } |
| __CFSpinUnlock(&loopsLock); |
| return loop; |
| } |
| |
| __private_extern__ void _CFRunLoop1(void) { |
| __CFSpinLock(&loopsLock); |
| if (runLoops) { |
| NativeThread t = _CFThreadSelf(); |
| CFRunLoopRef currentLoop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(t)); |
| CFRunLoopRef mainLoop = (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(kNilThreadT)); |
| if (currentLoop && mainLoop != currentLoop) { |
| CFDictionaryRemoveValue(runLoops, pthreadPointer(t)); |
| CFRelease(currentLoop); |
| } |
| } |
| __CFSpinUnlock(&loopsLock); |
| } |
| |
| CFRunLoopRef CFRunLoopGetMain(void) { |
| CHECK_FOR_FORK(); |
| return _CFRunLoop0(kNilThreadT); |
| } |
| |
| CFRunLoopRef CFRunLoopGetCurrent(void) { |
| CHECK_FOR_FORK(); |
| return _CFRunLoop0(_CFThreadSelf()); |
| } |
| |
| void _CFRunLoopSetCurrent(CFRunLoopRef rl) { |
| __CFSpinLock(&loopsLock); |
| CFRunLoopRef currentLoop = runLoops ? (CFRunLoopRef)CFDictionaryGetValue(runLoops, pthreadPointer(_CFThreadSelf())) : NULL; |
| if (rl != currentLoop) { |
| // intentionally leak currentLoop so we don't kill any ports in the child |
| // if (currentLoop) CFRelease(currentLoop); |
| if (rl) { |
| if (!runLoops) { |
| runLoops = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); |
| CFRunLoopRef mainLoop = __CFRunLoopCreate(); |
| CFDictionarySetValue(runLoops, pthreadPointer(kNilThreadT), mainLoop); |
| } |
| CFRetain(rl); |
| CFDictionarySetValue(runLoops, pthreadPointer(_CFThreadSelf()), rl); |
| } else { |
| CFDictionaryRemoveValue(runLoops, pthreadPointer(_CFThreadSelf())); |
| } |
| } |
| __CFSpinUnlock(&loopsLock); |
| } |
| |
| 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) == __kCFRunLoopSourceTypeID) { |
| CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName); |
| } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) { |
| CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName); |
| } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) { |
| 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) == __kCFRunLoopSourceTypeID) { |
| CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName); |
| } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) { |
| CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName); |
| } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) { |
| 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) == __kCFRunLoopSourceTypeID) { |
| CFRunLoopRemoveSource(rl, (CFRunLoopSourceRef)item, modeName); |
| } else if (CFGetTypeID(item) == __kCFRunLoopObserverTypeID) { |
| CFRunLoopRemoveObserver(rl, (CFRunLoopObserverRef)item, modeName); |
| } else if (CFGetTypeID(item) == __kCFRunLoopTimerTypeID) { |
| CFRunLoopRemoveTimer(rl, (CFRunLoopTimerRef)item, modeName); |
| } |
| } |
| |
| Boolean _CFRunLoop01(CFRunLoopRef rl, CFStringRef modeName) { |
| __CFRunLoopLock(rl); |
| Boolean present = CFSetContainsValue(rl->_commonModes, modeName); |
| __CFRunLoopUnlock(rl); |
| return present; |
| } |
| |
| void *_CFRunLoop02(CFRunLoopRef rl) { |
| return rl->_counterpart; |
| } |
| |
| void _CFRunLoop03(CFRunLoopRef rl, void *ns) { |
| rl->_counterpart = ns; |
| } |
| |
| 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); |
| __CFRunLoopUnlock(rl); |
| 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); |
| } |
| } |
| |
| static CFComparisonResult __CFRunLoopObserverQSortComparator(const void *val1, const void *val2, void *context) { |
| CFRunLoopObserverRef o1 = *((CFRunLoopObserverRef *)val1); |
| CFRunLoopObserverRef o2 = *((CFRunLoopObserverRef *)val2); |
| if (!o1) { |
| return (!o2) ? kCFCompareEqualTo : kCFCompareLessThan; |
| } |
| if (!o2) { |
| return kCFCompareGreaterThan; |
| } |
| if (o1->_order < o2->_order) return kCFCompareLessThan; |
| if (o2->_order < o1->_order) return kCFCompareGreaterThan; |
| return kCFCompareEqualTo; |
| } |
| |
| |
| /* rl is unlocked, rlm is locked on entrance and exit */ |
| /* ALERT: this should collect all the candidate observers from the top level |
| * and all submodes, recursively, THEN start calling them, in order to obey |
| * the ordering parameter. */ |
| static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */ |
| CHECK_FOR_FORK(); |
| CFIndex idx, cnt; |
| CFArrayRef submodes; |
| |
| /* Fire the observers */ |
| submodes = (NULL != rlm->_submodes && 0 < CFArrayGetCount(rlm->_submodes)) ? CFArrayCreateCopy(kCFAllocatorSystemDefault, rlm->_submodes) : NULL; |
| if (NULL != rlm->_observers) { |
| cnt = CFSetGetCount(rlm->_observers); |
| if (0 < cnt) { |
| #if __GNUC__ |
| CFRunLoopObserverRef buffer[(cnt <= 1024) ? cnt : 1]; |
| #else |
| CFRunLoopObserverRef buffer[1024]; |
| #endif |
| CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef*)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(CFRunLoopObserverRef), 0); |
| CFSetGetValues(rlm->_observers, (const void **)collectedObservers); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRunLoopObserverRef rlo = collectedObservers[idx]; |
| if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) { |
| CFRetain(rlo); |
| } else { |
| /* We're not interested in this one - set it to NULL so we don't process it later */ |
| collectedObservers[idx] = NULL; |
| } |
| } |
| __CFRunLoopModeUnlock(rlm); |
| CFQSortArray(collectedObservers, cnt, sizeof(CFRunLoopObserverRef), __CFRunLoopObserverQSortComparator, NULL); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRunLoopObserverRef rlo = collectedObservers[idx]; |
| if (rlo) { |
| __CFRunLoopObserverLock(rlo); |
| if (__CFIsValid(rlo)) { |
| __CFRunLoopObserverUnlock(rlo); |
| __CFRunLoopObserverSetFiring(rlo); |
| rlo->_callout(rlo, activity, rlo->_context.info); /* CALLOUT */ |
| __CFRunLoopObserverUnsetFiring(rlo); |
| if (!__CFRunLoopObserverRepeats(rlo)) { |
| CFRunLoopObserverInvalidate(rlo); |
| } |
| } else { |
| __CFRunLoopObserverUnlock(rlo); |
| } |
| CFRelease(rlo); |
| } |
| } |
| __CFRunLoopModeLock(rlm); |
| if (collectedObservers != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, collectedObservers); |
| } |
| } |
| if (NULL != submodes) { |
| __CFRunLoopModeUnlock(rlm); |
| for (idx = 0, cnt = CFArrayGetCount(submodes); idx < cnt; idx++) { |
| CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(submodes, idx); |
| CFRunLoopModeRef subrlm; |
| __CFRunLoopLock(rl); |
| subrlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| if (NULL != subrlm) { |
| __CFRunLoopDoObservers(rl, subrlm, activity); |
| __CFRunLoopModeUnlock(subrlm); |
| } |
| } |
| CFRelease(submodes); |
| __CFRunLoopModeLock(rlm); |
| } |
| } |
| |
| 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) == __kCFRunLoopSourceTypeID) { |
| CFTypeRef oldrls = *sources; |
| *sources = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); |
| CFArrayAppendValue((CFMutableArrayRef)*sources, oldrls); |
| CFArrayAppendValue((CFMutableArrayRef)*sources, rls); |
| CFRelease(oldrls); |
| } else { |
| CFArrayAppendValue((CFMutableArrayRef)*sources, rls); |
| } |
| } |
| } |
| |
| /* rl is unlocked, rlm is locked on entrance and exit */ |
| static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) { /* DOES CALLOUT */ |
| CHECK_FOR_FORK(); |
| CFTypeRef sources = NULL; |
| Boolean sourceHandled = false; |
| CFIndex idx, cnt; |
| |
| __CFRunLoopModeUnlock(rlm); // locks have to be taken in order |
| __CFRunLoopLock(rl); |
| __CFRunLoopModeLock(rlm); |
| /* Fire the version 0 sources */ |
| if (NULL != rlm->_sources && 0 < CFSetGetCount(rlm->_sources)) { |
| CFSetApplyFunction(rlm->_sources, (__CFRunLoopCollectSources0), &sources); |
| } |
| for (idx = 0, cnt = (NULL != rlm->_submodes) ? CFArrayGetCount(rlm->_submodes) : 0; idx < cnt; idx++) { |
| CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); |
| CFRunLoopModeRef subrlm; |
| subrlm = __CFRunLoopFindMode(rl, modeName, false); |
| if (NULL != subrlm) { |
| if (NULL != subrlm->_sources && 0 < CFSetGetCount(subrlm->_sources)) { |
| CFSetApplyFunction(subrlm->_sources, (__CFRunLoopCollectSources0), &sources); |
| } |
| __CFRunLoopModeUnlock(subrlm); |
| } |
| } |
| __CFRunLoopUnlock(rl); |
| if (NULL != sources) { |
| // sources is either a single (retained) CFRunLoopSourceRef or an array of (retained) CFRunLoopSourceRef |
| __CFRunLoopModeUnlock(rlm); |
| if (CFGetTypeID(sources) == __kCFRunLoopSourceTypeID) { |
| CFRunLoopSourceRef rls = (CFRunLoopSourceRef)sources; |
| __CFRunLoopSourceLock(rls); |
| __CFRunLoopSourceUnsetSignaled(rls); |
| if (__CFIsValid(rls)) { |
| __CFRunLoopSourceUnlock(rls); |
| if (NULL != rls->_context.version0.perform) { |
| rls->_context.version0.perform(rls->_context.version0.info); /* CALLOUT */ |
| CHECK_FOR_FORK(); |
| } |
| sourceHandled = true; |
| } else { |
| __CFRunLoopSourceUnlock(rls); |
| } |
| } else { |
| cnt = CFArrayGetCount((CFArrayRef)sources); |
| CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx); |
| __CFRunLoopSourceLock(rls); |
| __CFRunLoopSourceUnsetSignaled(rls); |
| if (__CFIsValid(rls)) { |
| __CFRunLoopSourceUnlock(rls); |
| if (NULL != rls->_context.version0.perform) { |
| rls->_context.version0.perform(rls->_context.version0.info); /* CALLOUT */ |
| CHECK_FOR_FORK(); |
| } |
| sourceHandled = true; |
| } else { |
| __CFRunLoopSourceUnlock(rls); |
| } |
| if (stopAfterHandle && sourceHandled) { |
| break; |
| } |
| } |
| } |
| CFRelease(sources); |
| __CFRunLoopModeLock(rlm); |
| } |
| return sourceHandled; |
| } |
| |
| // msg, size and reply are unused on Windows |
| static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls |
| #if DEPLOYMENT_TARGET_MACOSX |
| , 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); |
| __CFRunLoopSourceLock(rls); |
| if (__CFIsValid(rls)) { |
| __CFRunLoopSourceUnsetSignaled(rls); |
| __CFRunLoopSourceUnlock(rls); |
| if (NULL != rls->_context.version1.perform) { |
| #if DEPLOYMENT_TARGET_MACOSX |
| *reply = (mach_msg_header_t*)rls->_context.version1.perform(msg, size, kCFAllocatorSystemDefault, rls->_context.version1.info); /* CALLOUT */ |
| CHECK_FOR_FORK(); |
| #else |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 performing rls %p"), CFRunLoopGetCurrent(), *_CFGetProgname(), rls); } |
| rls->_context.version1.perform(rls->_context.version1.info); /* CALLOUT */ |
| CHECK_FOR_FORK(); |
| #endif |
| } else { |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 perform is NULL"), CFRunLoopGetCurrent(), *_CFGetProgname()); } |
| } |
| sourceHandled = true; |
| } else { |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 rls %p is invalid"), CFRunLoopGetCurrent(), *_CFGetProgname(), rls); } |
| __CFRunLoopSourceUnlock(rls); |
| } |
| CFRelease(rls); |
| __CFRunLoopModeLock(rlm); |
| return sourceHandled; |
| } |
| |
| static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */ |
| Boolean timerHandled = false; |
| int64_t oldFireTSR = 0; |
| |
| /* Fire a timer */ |
| CFRetain(rlt); |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopTimerLock(rlt); |
| if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) { |
| __CFRunLoopTimerUnsetDidFire(rlt); |
| __CFRunLoopTimerSetFiring(rlt); |
| __CFRunLoopTimerUnlock(rlt); |
| __CFRunLoopTimerFireTSRLock(); |
| oldFireTSR = rlt->_fireTSR; |
| __CFRunLoopTimerFireTSRUnlock(); |
| rlt->_callout(rlt, rlt->_context.info); /* CALLOUT */ |
| CHECK_FOR_FORK(); |
| __CFRunLoopTimerUnsetFiring(rlt); |
| timerHandled = true; |
| } else { |
| // If the timer fires while it is firing in a higher activiation, |
| // it is not allowed to fire, but we have to remember that fact. |
| // Later, if the timer's fire date is being handled manually, we |
| // need to re-arm the kernel timer, since it has possibly already |
| // fired (this firing which is being skipped, say) and the timer |
| // will permanently stop if we completely drop this firing. |
| if (__CFRunLoopTimerIsFiring(rlt)) __CFRunLoopTimerSetDidFire(rlt); |
| __CFRunLoopTimerUnlock(rlt); |
| } |
| if (__CFIsValid(rlt) && timerHandled) { |
| if (0 == rlt->_intervalTSR) { |
| CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */ |
| } else { |
| /* 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. */ |
| int64_t currentFireTSR; |
| __CFRunLoopTimerFireTSRLock(); |
| currentFireTSR = rlt->_fireTSR; |
| if (oldFireTSR < currentFireTSR) { |
| /* Next fire TSR was set, and set to a date after the previous |
| * fire date, so we honor it. */ |
| if (__CFRunLoopTimerDidFire(rlt)) { |
| __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); |
| __CFRunLoopTimerUnsetDidFire(rlt); |
| } |
| } else { |
| if ((uint64_t)LLONG_MAX <= (uint64_t)oldFireTSR + (uint64_t)rlt->_intervalTSR) { |
| currentFireTSR = LLONG_MAX; |
| } else { |
| int64_t currentTSR = (int64_t)__CFReadTSR(); |
| currentFireTSR = oldFireTSR; |
| while (currentFireTSR <= currentTSR) { |
| currentFireTSR += rlt->_intervalTSR; |
| } |
| } |
| rlt->_fireTSR = currentFireTSR; |
| __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); |
| } |
| __CFRunLoopTimerFireTSRUnlock(); |
| } |
| } |
| CFRelease(rlt); |
| __CFRunLoopModeLock(rlm); |
| 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)) { |
| result = true; |
| } |
| __CFRunLoopUnlock(rl); |
| if (rlm) __CFRunLoopModeUnlock(rlm); |
| return result; |
| } |
| |
| // rl is locked, rlm is locked on entry and exit |
| static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef rlm, __CFPortSet portSet) { |
| CFIndex idx, cnt; |
| const void **list, *buffer[256]; |
| |
| // Timers and version 1 sources go into the portSet currently |
| if (NULL != rlm->_sources) { |
| cnt = CFSetGetCount(rlm->_sources); |
| list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); |
| CFSetGetValues(rlm->_sources, list); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRunLoopSourceRef rls = (CFRunLoopSourceRef)list[idx]; |
| if (1 == rls->_context.version0.version) { |
| __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ |
| if (CFPORT_NULL != port) { |
| __CFPortSetInsert(port, portSet); |
| } |
| } |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| } |
| #if DEPLOYMENT_TARGET_MACOSX |
| if (NULL != rlm->_timers) { |
| cnt = CFSetGetCount(rlm->_timers); |
| list = (cnt <= 256) ? buffer : (const void**)CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); |
| CFSetGetValues(rlm->_timers, list); |
| for (idx = 0; idx < cnt; idx++) { |
| CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)list[idx]; |
| if (MACH_PORT_NULL != rlt->_port) { |
| mach_port_insert_member(mach_task_self(), rlt->_port, portSet); |
| } |
| } |
| if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); |
| } |
| #endif |
| // iterate over submodes |
| for (idx = 0, cnt = NULL != rlm->_submodes ? CFArrayGetCount(rlm->_submodes) : 0; idx < cnt; idx++) { |
| CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); |
| CFRunLoopModeRef subrlm; |
| subrlm = __CFRunLoopFindMode(rl, modeName, false); |
| if (NULL != subrlm) { |
| __CFRunLoopModeAddPortsToPortSet(rl, subrlm, portSet); |
| __CFRunLoopModeUnlock(subrlm); |
| } |
| } |
| } |
| |
| static __CFPortSet _LastMainWaitSet = CFPORTSET_NULL; |
| |
| // return NO if we're the main runloop and there are no messages waiting on the port set |
| int _CFRunLoopInputsReady(void) { |
| CHECK_FOR_FORK(); |
| // XXX_PCB: the following 2 lines aren't safe to call during GC, because another |
| // thread may have entered CFRunLoopGetMain(), which grabs a spink lock, and then |
| // is suspended by the GC. We can check for the main thread more directly |
| // by calling pthread_main_np(). |
| // CFRunLoopRef current = CFRunLoopGetMain() |
| // if (current != CFRunLoopGetMain()) return true; |
| if (!_CFThreadIsMain()) return true; |
| |
| // XXX_PCB: can't be any messages waiting if the wait set is NULL. |
| if (_LastMainWaitSet == CFPORTSET_NULL) return false; |
| |
| #if DEPLOYMENT_TARGET_MACOSX |
| // prepare a message header with no space for any data, nor a trailer |
| mach_msg_header_t msg; |
| msg.msgh_size = sizeof(msg); // just the header, ma'am |
| // need the waitset, actually XXX |
| msg.msgh_local_port = _LastMainWaitSet; |
| msg.msgh_remote_port = MACH_PORT_NULL; |
| msg.msgh_id = 0; |
| |
| kern_return_t ret = mach_msg(&msg, MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_LARGE, 0, msg.msgh_size, _LastMainWaitSet, 0, MACH_PORT_NULL); |
| |
| return (MACH_RCV_TOO_LARGE == ret); |
| #endif |
| return true; |
| } |
| |
| #if 0 |
| static void print_msg_scan_header(void) { |
| printf("======== ======== ======== ========\n"); |
| printf("description\tport\tport type\t\treferences\n"); |
| } |
| |
| static void print_one_port_info(const char *desc, mach_port_t port, mach_msg_type_name_t type) { |
| mach_port_urefs_t refs; |
| kern_return_t ret = mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, &refs); |
| if (ret != KERN_SUCCESS) refs = 0; |
| const char *type_name = "???"; |
| switch (type) { |
| case MACH_MSG_TYPE_MOVE_SEND: type_name = "MACH_MSG_TYPE_MOVE_SEND"; break; |
| case MACH_MSG_TYPE_MOVE_SEND_ONCE: type_name = "MACH_MSG_TYPE_MOVE_SEND_ONCE"; break; |
| case MACH_MSG_TYPE_MOVE_RECEIVE: type_name = "MACH_MSG_TYPE_MOVE_RECEIVE"; break; |
| case MACH_MSG_TYPE_MAKE_SEND: type_name = "MACH_MSG_TYPE_MAKE_SEND"; break; |
| case MACH_MSG_TYPE_MAKE_SEND_ONCE: type_name = "MACH_MSG_TYPE_MAKE_SEND_ONCE"; break; |
| } |
| printf("%s\t%p\t%-20s\t%u\n", desc, port, type_name, refs); |
| } |
| |
| static void mach_msg_scan(mach_msg_header_t *msg, int clean) { |
| Boolean printed_header = false; |
| /* |
| * The msgh_local_port field doesn't hold a port right. |
| * The receive operation consumes the destination port right. |
| */ |
| if (MACH_PORT_NULL != msg->msgh_remote_port) { |
| if (! printed_header) print_msg_scan_header(); |
| printed_header = true; |
| print_one_port_info("msg->msgh_remote_port", msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(msg->msgh_bits)); |
| } |
| if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) { |
| mach_msg_body_t *body = (mach_msg_body_t *) (msg + 1); |
| mach_msg_descriptor_t *saddr = (mach_msg_descriptor_t *) ((mach_msg_base_t *) msg + 1); |
| mach_msg_descriptor_t *eaddr = saddr + body->msgh_descriptor_count; |
| for ( ; saddr < eaddr; saddr++) { |
| switch (saddr->type.type) { |
| case MACH_MSG_PORT_DESCRIPTOR:; |
| mach_msg_port_descriptor_t *dsc = &saddr->port; |
| if (! printed_header) print_msg_scan_header(); |
| printed_header = true; |
| print_one_port_info("port in body", dsc->name, dsc->disposition); |
| // if (clean) mach_port_deallocate(mach_task_self(), dsc->name); |
| break; |
| case MACH_MSG_OOL_PORTS_DESCRIPTOR:; |
| mach_msg_ool_ports_descriptor_t *dsc2 = &saddr->ool_ports; |
| mach_port_t *ports = (mach_port_t *) dsc2->address; |
| for (mach_msg_type_number_t j = 0; j < dsc2->count; j++, ports++) { |
| if (! printed_header) print_msg_scan_header(); |
| printed_header = true; |
| print_one_port_info("port in OOL ports", *ports, dsc2->disposition); |
| } |
| break; |
| } |
| } |
| } |
| } |
| #endif |
| |
| /* rl is unlocked, rlm locked on entrance and exit */ |
| static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, Boolean waitIfEmpty) { /* DOES CALLOUT */ |
| int64_t termTSR; |
| #if DEPLOYMENT_TARGET_MACOSX |
| mach_port_name_t timeoutPort = MACH_PORT_NULL; |
| Boolean timeoutPortAdded = false; |
| #endif |
| Boolean poll = false; |
| Boolean firstPass = true; |
| |
| if (__CFRunLoopIsStopped(rl)) { |
| return kCFRunLoopRunStopped; |
| } else if (rlm->_stopped) { |
| rlm->_stopped = false; |
| return kCFRunLoopRunStopped; |
| } |
| if (seconds <= 0.0) { |
| termTSR = 0; |
| } else if (3.1556952e+9 < seconds) { |
| termTSR = LLONG_MAX; |
| } else { |
| termTSR = (int64_t)__CFReadTSR() + __CFTimeIntervalToTSR(seconds); |
| #if DEPLOYMENT_TARGET_MACOSX |
| timeoutPort = mk_timer_create(); |
| mk_timer_arm(timeoutPort, __CFUInt64ToAbsoluteTime(termTSR)); |
| #endif |
| } |
| if (seconds <= 0.0) { |
| poll = true; |
| } |
| if (rl == _CFRunLoop0(kNilThreadT)) _LastMainWaitSet = CFPORTSET_NULL; |
| for (;;) { |
| __CFPortSet waitSet = CFPORTSET_NULL; |
| waitSet = CFPORTSET_NULL; |
| Boolean destroyWaitSet = false; |
| CFRunLoopSourceRef rls; |
| #if DEPLOYMENT_TARGET_MACOSX |
| mach_msg_header_t *msg; |
| kern_return_t ret; |
| uint8_t buffer[1024 + 80] = {0}; // large enough for 1k of inline payload; must be zeroed for GC |
| #elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| CFArrayRef timersToCall = NULL; |
| #endif |
| int32_t returnValue = 0; |
| Boolean sourceHandledThisLoop = false; |
| |
| __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); |
| __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); |
| |
| sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); |
| |
| if (sourceHandledThisLoop) { |
| poll = true; |
| } |
| |
| if (!poll) { |
| __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); |
| __CFRunLoopSetSleeping(rl); |
| } |
| if (NULL != rlm->_submodes) { |
| // !!! what do we do if this doesn't succeed? |
| waitSet = __CFPortSetAllocate(); |
| if (CFPORTSET_NULL == waitSet) HALT; |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopLock(rl); |
| __CFRunLoopModeLock(rlm); |
| __CFRunLoopModeAddPortsToPortSet(rl, rlm, waitSet); |
| __CFRunLoopUnlock(rl); |
| #if DEPLOYMENT_TARGET_MACOSX |
| if (CFPORT_NULL != timeoutPort) { |
| __CFPortSetInsert(timeoutPort, waitSet); |
| } |
| #endif |
| destroyWaitSet = true; |
| } else { |
| waitSet = rlm->_portSet; |
| #if DEPLOYMENT_TARGET_MACOSX |
| if (!timeoutPortAdded && CFPORT_NULL != timeoutPort) { |
| __CFPortSetInsert(timeoutPort, waitSet); |
| timeoutPortAdded = true; |
| } |
| #endif |
| } |
| if (rl == _CFRunLoop0(kNilThreadT)) _LastMainWaitSet = waitSet; |
| __CFRunLoopModeUnlock(rlm); |
| |
| #if DEPLOYMENT_TARGET_MACOSX |
| msg = (mach_msg_header_t *)buffer; |
| msg->msgh_size = sizeof(buffer); |
| |
| /* In that sleep of death what nightmares may come ... */ |
| try_receive: |
| msg->msgh_bits = 0; |
| msg->msgh_local_port = waitSet; |
| msg->msgh_remote_port = MACH_PORT_NULL; |
| msg->msgh_id = 0; |
| ret = mach_msg(msg, MACH_RCV_MSG|MACH_RCV_LARGE|(poll ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT), 0, msg->msgh_size, waitSet, 0, MACH_PORT_NULL); |
| if (MACH_RCV_TOO_LARGE == ret) { |
| uint32_t newSize = round_msg(msg->msgh_size) + sizeof(mach_msg_audit_trailer_t); |
| if (msg == (mach_msg_header_t *)buffer) msg = NULL; |
| msg = (mach_msg_header_t*)CFAllocatorReallocate(kCFAllocatorSystemDefault, msg, newSize, 0); |
| msg->msgh_size = newSize; |
| goto try_receive; |
| } else if (MACH_RCV_TIMED_OUT == ret) { |
| // timeout, for poll |
| if (msg != (mach_msg_header_t *)buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); |
| msg = NULL; |
| } else if (MACH_MSG_SUCCESS != ret) { |
| HALT; |
| } |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| DWORD waitResult = WAIT_TIMEOUT; |
| __CFPort portBuf[MAX_PORTS]; |
| __CFPort *ports; |
| CFIndex portCount; |
| Boolean freePorts; |
| if (destroyWaitSet) { |
| // wait set is a local, no one else could modify it, no need to copy handles |
| ports = waitSet->ports; |
| portCount = waitSet->used; |
| freePorts = false; |
| } else { |
| // copy out the ports to be safe from other threads at work |
| ports = __CFPortSetGetPorts(waitSet, portBuf, MAX_PORTS, (uint32_t*)&portCount); |
| freePorts = (ports != portBuf); |
| } |
| // should msgQMask be an OR'ing of this and all submodes' masks? |
| if (0 == GetQueueStatus(rlm->_msgQMask)) { |
| DWORD timeout; |
| if (poll) |
| timeout = 0; |
| else { |
| __CFRunLoopModeLock(rlm); |
| int64_t nextStop = __CFRunLoopGetNextTimerFireTSR(rl, rlm); |
| if (nextStop <= 0) |
| nextStop = termTSR; |
| else if (nextStop > termTSR) |
| nextStop = termTSR; |
| // else the next stop is dictated by the next timer |
| int64_t timeoutTSR = nextStop - __CFReadTSR(); |
| if (timeoutTSR < 0) |
| timeout = 0; |
| else { |
| CFTimeInterval timeoutCF = __CFTSRToTimeInterval(timeoutTSR) * 1000; |
| if (timeoutCF > MAXDWORD) |
| timeout = INFINITE; |
| else |
| timeout = (DWORD)timeoutCF; |
| } |
| } |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s)- about to wait for %d objects, wakeupport is %p"), CFRunLoopGetCurrent(), *_CFGetProgname(), portCount, rl->_wakeUpPort); } |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("All RLM sources = %@"), rlm->_sources); } |
| waitResult = MsgWaitForMultipleObjects(__CFMin(portCount, MAX_PORTS), ports, false, timeout, rlm->_msgQMask); |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s)- waitResult was %d"), CFRunLoopGetCurrent(), *_CFGetProgname(), waitResult); } |
| } |
| ResetEvent(rl->_wakeUpPort); |
| #elif DEPLOYMENT_TARGET_LINUX |
| // XXX - More to fill in here. |
| #endif |
| if (destroyWaitSet) { |
| __CFPortSetFree(waitSet); |
| if (rl == _CFRunLoop0(kNilThreadT)) _LastMainWaitSet = CFPORTSET_NULL; |
| } |
| __CFRunLoopLock(rl); |
| __CFRunLoopModeLock(rlm); |
| __CFRunLoopUnlock(rl); |
| if (!poll) { |
| __CFRunLoopUnsetSleeping(rl); |
| __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); |
| } |
| poll = false; |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopLock(rl); |
| __CFRunLoopModeLock(rlm); |
| |
| __CFPort livePort = CFPORT_NULL; |
| #if DEPLOYMENT_TARGET_MACOSX |
| if (NULL != msg) { |
| livePort = msg->msgh_local_port; |
| } |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| CFAssert2(waitResult != WAIT_FAILED, __kCFLogAssertion, "%s(): error %d from MsgWaitForMultipleObjects", __PRETTY_FUNCTION__, GetLastError()); |
| if (waitResult == WAIT_TIMEOUT) { |
| // do nothing, just return to caller |
| } else if (waitResult >= WAIT_OBJECT_0 && waitResult < WAIT_OBJECT_0+portCount) { |
| // a port was signalled |
| livePort = ports[waitResult-WAIT_OBJECT_0]; |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("%p (%s)- Resetting event %p"), CFRunLoopGetCurrent(), *_CFGetProgname(), livePort); } |
| } else if (waitResult == WAIT_OBJECT_0+portCount) { |
| // windows message received - the CFWindowsMessageQueue will pick this up when |
| // the v0 RunLoopSources get their chance |
| } else if (waitResult >= WAIT_ABANDONED_0 && waitResult < WAIT_ABANDONED_0+portCount) { |
| // an "abandoned mutex object" |
| livePort = ports[waitResult-WAIT_ABANDONED_0]; |
| } else { |
| CFAssert2(waitResult == WAIT_FAILED, __kCFLogAssertion, "%s(): unexpected result from MsgWaitForMultipleObjects: %d", __PRETTY_FUNCTION__, waitResult); |
| } |
| if (freePorts) |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, ports); |
| timersToCall = __CFRunLoopTimersToFire(rl, rlm); |
| #elif DEPLOYMENT_TARGET_LINUX |
| // XXX - More to fill in here. |
| timersToCall = __CFRunLoopTimersToFire(rl, rlm); |
| #endif |
| |
| if (CFPORT_NULL == livePort) { |
| __CFRunLoopUnlock(rl); |
| } else if (livePort == rl->_wakeUpPort) { |
| // wakeup |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("wakeupPort was signalled")); } |
| __CFRunLoopUnlock(rl); |
| } |
| #if DEPLOYMENT_TARGET_MACOSX |
| else if (livePort == timeoutPort) { |
| returnValue = kCFRunLoopRunTimedOut; |
| __CFRunLoopUnlock(rl); |
| } else if (NULL != (rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort))) { |
| mach_msg_header_t *reply = NULL; |
| __CFRunLoopUnlock(rl); |
| // mach_msg_scan(msg, 0); |
| if (__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)) { |
| sourceHandledThisLoop = true; |
| } |
| // mach_msg_scan(msg, 1); |
| if (NULL != reply) { |
| ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); |
| //#warning CF: what should be done with the return value? |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); |
| } |
| } else { |
| CFRunLoopTimerRef rlt; |
| rlt = __CFRunLoopModeFindTimerForMachPort(rlm, livePort); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlt) { |
| __CFRunLoopDoTimer(rl, rlm, rlt); |
| } |
| } |
| if (msg != (mach_msg_header_t *)buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); |
| #else |
| else if (NULL != (rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort))) { |
| __CFRunLoopUnlock(rl); |
| if (_LogCFRunLoop) { CFLog(kCFLogLevelDebug, CFSTR("Source %@ was signalled"), rls); } |
| if (__CFRunLoopDoSource1(rl, rlm, rls)) { |
| sourceHandledThisLoop = true; |
| } |
| } |
| #endif |
| |
| #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| if (NULL != timersToCall) { |
| CFIndex i; |
| for (i = CFArrayGetCount(timersToCall) - 1; i >= 0; i--) { |
| CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timersToCall, i); |
| if (NULL != rlt) { |
| __CFRunLoopDoTimer(rl, rlm, rlt); |
| } |
| } |
| CFRelease(timersToCall); |
| } |
| #endif // DEPLOYMENT_TARGER_WINDOWS || DEPLOYMENT_TARGET_LINUX |
| |
| __CFRunLoopModeUnlock(rlm); // locks must be taken in order |
| __CFRunLoopLock(rl); |
| __CFRunLoopModeLock(rlm); |
| if (sourceHandledThisLoop && stopAfterHandle) { |
| returnValue = kCFRunLoopRunHandledSource; |
| // If we're about to timeout, but we just did a zero-timeout poll that only found our own |
| // internal wakeup signal on the first look at the portset, we'll go around the loop one |
| // more time, so as not to starve a v1 source that was just added along with a runloop wakeup. |
| } else if (0 != returnValue || (uint64_t)termTSR <= __CFReadTSR()) { |
| returnValue = kCFRunLoopRunTimedOut; |
| } else if (__CFRunLoopIsStopped(rl)) { |
| returnValue = kCFRunLoopRunStopped; |
| } else if (rlm->_stopped) { |
| rlm->_stopped = false; |
| returnValue = kCFRunLoopRunStopped; |
| } else if (!waitIfEmpty && __CFRunLoopModeIsEmpty(rl, rlm)) { |
| returnValue = kCFRunLoopRunFinished; |
| } |
| __CFRunLoopUnlock(rl); |
| if (0 != returnValue) { |
| #if DEPLOYMENT_TARGET_MACOSX |
| if (MACH_PORT_NULL != timeoutPort) { |
| if (!destroyWaitSet) __CFPortSetRemove(timeoutPort, waitSet); |
| mk_timer_destroy(timeoutPort); |
| } |
| #endif |
| return returnValue; |
| } |
| firstPass = false; |
| } |
| } |
| |
| SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ |
| CHECK_FOR_FORK(); |
| if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished; |
| __CFRunLoopLock(rl); |
| CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false); |
| if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode)) { |
| if (currentMode) __CFRunLoopModeUnlock(currentMode); |
| __CFRunLoopUnlock(rl); |
| return kCFRunLoopRunFinished; |
| } |
| uint32_t *previousStopped = (uint32_t *)rl->_stopped; |
| rl->_stopped = (volatile uint32_t*)CFAllocatorAllocate(kCFAllocatorSystemDefault, 4 * sizeof(uint32_t), 0); |
| rl->_stopped[0] = 0x4346524C; |
| rl->_stopped[1] = 0x4346524C; // 'CFRL' |
| rl->_stopped[2] = 0x00000000; // here the value is stored |
| rl->_stopped[3] = 0x4346524C; |
| CFRunLoopModeRef previousMode = rl->_currentMode; |
| rl->_currentMode = currentMode; |
| __CFRunLoopUnlock(rl); |
| int32_t result; |
| __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); |
| result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, false); |
| __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); |
| __CFRunLoopModeUnlock(currentMode); |
| __CFRunLoopLock(rl); |
| CFAllocatorDeallocate(kCFAllocatorSystemDefault, (uint32_t *)rl->_stopped); |
| rl->_stopped = previousStopped; |
| rl->_currentMode = previousMode; |
| __CFRunLoopUnlock(rl); |
| return result; |
| } |
| |
| void CFRunLoopRun(void) { /* DOES CALLOUT */ |
| int32_t result; |
| do { |
| result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); |
| CHECK_FOR_FORK(); |
| } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result); |
| } |
| |
| SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ |
| CHECK_FOR_FORK(); |
| return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); |
| } |
| |
| static void __CFRunLoopFindMinTimer(const void *value, void *ctx) { |
| CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)value; |
| if (__CFIsValid(rlt)) { |
| CFRunLoopTimerRef *result = (CFRunLoopTimerRef*)ctx; |
| if (NULL == *result || rlt->_fireTSR < (*result)->_fireTSR) { |
| *result = rlt; |
| } |
| } |
| } |
| |
| static int64_t __CFRunLoopGetNextTimerFireTSR(CFRunLoopRef rl, CFRunLoopModeRef rlm) { |
| CFRunLoopTimerRef result = NULL; |
| int64_t fireTime = 0; |
| if (rlm) { |
| if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) { |
| __CFRunLoopTimerFireTSRLock(); |
| CFSetApplyFunction(rlm->_timers, (__CFRunLoopFindMinTimer), &result); |
| if (result) |
| fireTime = result->_fireTSR; |
| __CFRunLoopTimerFireTSRUnlock(); |
| } |
| if (NULL != rlm->_submodes) { |
| CFIndex idx, cnt; |
| for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { |
| CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); |
| CFRunLoopModeRef subrlm; |
| subrlm = __CFRunLoopFindMode(rl, modeName, false); |
| if (NULL != subrlm) { |
| int64_t newFireTime = __CFRunLoopGetNextTimerFireTSR(rl, subrlm); |
| __CFRunLoopModeUnlock(subrlm); |
| if (fireTime == 0 || (newFireTime != 0 && newFireTime < fireTime)) |
| fireTime = newFireTime; |
| } |
| } |
| } |
| __CFRunLoopModeUnlock(rlm); |
| } |
| return fireTime; |
| } |
| |
| CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| int64_t fireTSR; |
| __CFRunLoopLock(rl); |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| fireTSR = __CFRunLoopGetNextTimerFireTSR(rl, rlm); |
| int64_t now2 = __CFReadTSR(); |
| CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); |
| return (0 == fireTSR) ? 0.0 : (now1 + __CFTSRToTimeInterval(fireTSR - now2)); |
| } |
| |
| Boolean CFRunLoopIsWaiting(CFRunLoopRef rl) { |
| CHECK_FOR_FORK(); |
| return __CFRunLoopIsSleeping(rl); |
| } |
| |
| void CFRunLoopWakeUp(CFRunLoopRef rl) { |
| CHECK_FOR_FORK(); |
| #if DEPLOYMENT_TARGET_MACOSX |
| kern_return_t ret; |
| /* We unconditionally try to send the message, since we don't want |
| * to lose a wakeup, but the send may fail if there is already a |
| * wakeup pending, since the queue length is 1. */ |
| ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0); |
| if (ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) { |
| HALT; |
| } |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| SetEvent(rl->_wakeUpPort); |
| #elif DEPLOYMENT_TARGET_LINUX |
| sem_post(rl->_wakeUpPort); |
| #endif |
| } |
| |
| void CFRunLoopStop(CFRunLoopRef rl) { |
| CHECK_FOR_FORK(); |
| __CFRunLoopLock(rl); |
| __CFRunLoopSetStopped(rl); |
| __CFRunLoopUnlock(rl); |
| CFRunLoopWakeUp(rl); |
| } |
| |
| CF_EXPORT void _CFRunLoopStopMode(CFRunLoopRef rl, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| __CFRunLoopLock(rl); |
| rlm = __CFRunLoopFindMode(rl, modeName, true); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm) { |
| rlm->_stopped = true; |
| __CFRunLoopModeUnlock(rlm); |
| } |
| CFRunLoopWakeUp(rl); |
| } |
| |
| CF_EXPORT Boolean _CFRunLoopModeContainsMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef candidateContainedName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| if (modeName == kCFRunLoopCommonModes || candidateContainedName == kCFRunLoopCommonModes) { |
| return false; |
| } else if (CFEqual(modeName, candidateContainedName)) { |
| return true; |
| } |
| __CFRunLoopLock(rl); |
| rlm = __CFRunLoopFindMode(rl, modeName, true); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm) { |
| CFArrayRef submodes; |
| if (NULL == rlm->_submodes) { |
| __CFRunLoopModeUnlock(rlm); |
| return false; |
| } |
| if (CFArrayContainsValue(rlm->_submodes, CFRangeMake(0, CFArrayGetCount(rlm->_submodes)), candidateContainedName)) { |
| __CFRunLoopModeUnlock(rlm); |
| return true; |
| } |
| submodes = (NULL != rlm->_submodes && 0 < CFArrayGetCount(rlm->_submodes)) ? CFArrayCreateCopy(kCFAllocatorSystemDefault, rlm->_submodes) : NULL; |
| __CFRunLoopModeUnlock(rlm); |
| if (NULL != submodes) { |
| CFIndex idx, cnt; |
| for (idx = 0, cnt = CFArrayGetCount(submodes); idx < cnt; idx++) { |
| CFStringRef subname = (CFStringRef)CFArrayGetValueAtIndex(submodes, idx); |
| if (_CFRunLoopModeContainsMode(rl, subname, candidateContainedName)) { |
| CFRelease(submodes); |
| return true; |
| } |
| } |
| CFRelease(submodes); |
| } |
| } |
| return false; |
| } |
| |
| CF_EXPORT void _CFRunLoopAddModeToMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef toModeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| if (__CFRunLoopIsDeallocating(rl)) return; |
| // should really do a recursive check here, to make sure that a cycle isn't |
| // introduced; of course, if that happens, you aren't going to get very far. |
| if (modeName == kCFRunLoopCommonModes || toModeName == kCFRunLoopCommonModes || CFEqual(modeName, toModeName)) { |
| return; |
| } else { |
| __CFRunLoopLock(rl); |
| rlm = __CFRunLoopFindMode(rl, toModeName, true); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm) { |
| if (NULL == rlm->_submodes) { |
| rlm->_submodes = CFArrayCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeArrayCallBacks); |
| } |
| if (!CFArrayContainsValue(rlm->_submodes, CFRangeMake(0, CFArrayGetCount(rlm->_submodes)), modeName)) { |
| CFArrayAppendValue(rlm->_submodes, modeName); |
| } |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| CF_EXPORT void _CFRunLoopRemoveModeFromMode(CFRunLoopRef rl, CFStringRef modeName, CFStringRef fromModeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| // should really do a recursive check here, to make sure that a cycle isn't |
| // introduced; of course, if that happens, you aren't going to get very far. |
| if (modeName == kCFRunLoopCommonModes || fromModeName == kCFRunLoopCommonModes || CFEqual(modeName, fromModeName)) { |
| return; |
| } else { |
| __CFRunLoopLock(rl); |
| rlm = __CFRunLoopFindMode(rl, fromModeName, true); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm) { |
| if (NULL != rlm->_submodes) { |
| CFIndex idx, cnt = CFArrayGetCount(rlm->_submodes); |
| idx = CFArrayGetFirstIndexOfValue(rlm->_submodes, CFRangeMake(0, cnt), modeName); |
| if (0 <= idx) CFArrayRemoveValueAtIndex(rlm->_submodes, idx); |
| } |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| Boolean hasValue = false; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| if (NULL != rl->_commonModeItems) { |
| hasValue = CFSetContainsValue(rl->_commonModeItems, rls); |
| } |
| __CFRunLoopUnlock(rl); |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL != rlm->_sources) { |
| hasValue = CFSetContainsValue(rlm->_sources, rls); |
| __CFRunLoopModeUnlock(rlm); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| return hasValue; |
| } |
| |
| void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */ |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| if (__CFRunLoopIsDeallocating(rl)) return; |
| if (!__CFIsValid(rls)) return; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; |
| if (NULL == rl->_commonModeItems) { |
| rl->_commonModeItems = CFSetCreateMutable(CFGetAllocator(rl), 0, &kCFTypeSetCallBacks); |
| _CFSetSetCapacity(rl->_commonModeItems, 20); |
| } |
| CFSetAddValue(rl->_commonModeItems, rls); |
| __CFRunLoopUnlock(rl); |
| if (NULL != set) { |
| CFTypeRef context[2] = {rl, rls}; |
| /* add new item to all common-modes */ |
| CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); |
| CFRelease(set); |
| } |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, true); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL == rlm->_sources) { |
| rlm->_sources = CFSetCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeSetCallBacks); |
| _CFSetSetCapacity(rlm->_sources, 10); |
| } |
| if (NULL != rlm && !CFSetContainsValue(rlm->_sources, rls)) { |
| CFSetAddValue(rlm->_sources, rls); |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopSourceSchedule(rls, rl, rlm); /* DOES CALLOUT */ |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */ |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rls)) { |
| CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; |
| CFSetRemoveValue(rl->_commonModeItems, rls); |
| __CFRunLoopUnlock(rl); |
| if (NULL != set) { |
| CFTypeRef context[2] = {rl, rls}; |
| /* remove new item from all common-modes */ |
| CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context); |
| CFRelease(set); |
| } |
| } else { |
| __CFRunLoopUnlock(rl); |
| } |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL != rlm->_sources && CFSetContainsValue(rlm->_sources, rls)) { |
| CFRetain(rls); |
| CFSetRemoveValue(rlm->_sources, rls); |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopSourceCancel(rls, rl, rlm); /* DOES CALLOUT */ |
| CFRelease(rls); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| Boolean CFRunLoopContainsObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| Boolean hasValue = false; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| if (NULL != rl->_commonModeItems) { |
| hasValue = CFSetContainsValue(rl->_commonModeItems, rlo); |
| } |
| __CFRunLoopUnlock(rl); |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL != rlm->_observers) { |
| hasValue = CFSetContainsValue(rlm->_observers, rlo); |
| __CFRunLoopModeUnlock(rlm); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| return hasValue; |
| } |
| |
| void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| if (__CFRunLoopIsDeallocating(rl)) return; |
| if (!__CFIsValid(rlo) || (NULL != rlo->_runLoop && rlo->_runLoop != rl)) return; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; |
| if (NULL == rl->_commonModeItems) { |
| rl->_commonModeItems = CFSetCreateMutable(CFGetAllocator(rl), 0, &kCFTypeSetCallBacks); |
| } |
| CFSetAddValue(rl->_commonModeItems, rlo); |
| __CFRunLoopUnlock(rl); |
| if (NULL != set) { |
| CFTypeRef context[2] = {rl, rlo}; |
| /* add new item to all common-modes */ |
| CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); |
| CFRelease(set); |
| } |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, true); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL == rlm->_observers) { |
| rlm->_observers = CFSetCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeSetCallBacks); |
| } |
| if (NULL != rlm && !CFSetContainsValue(rlm->_observers, rlo)) { |
| CFSetAddValue(rlm->_observers, rlo); |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopObserverSchedule(rlo, rl, rlm); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rlo)) { |
| CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; |
| CFSetRemoveValue(rl->_commonModeItems, rlo); |
| __CFRunLoopUnlock(rl); |
| if (NULL != set) { |
| CFTypeRef context[2] = {rl, rlo}; |
| /* remove new item from all common-modes */ |
| CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context); |
| CFRelease(set); |
| } |
| } else { |
| __CFRunLoopUnlock(rl); |
| } |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL != rlm->_observers && CFSetContainsValue(rlm->_observers, rlo)) { |
| CFRetain(rlo); |
| CFSetRemoveValue(rlm->_observers, rlo); |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopObserverCancel(rlo, rl, rlm); |
| CFRelease(rlo); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| Boolean hasValue = false; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| if (NULL != rl->_commonModeItems) { |
| hasValue = CFSetContainsValue(rl->_commonModeItems, rlt); |
| } |
| __CFRunLoopUnlock(rl); |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL != rlm->_timers) { |
| hasValue = CFSetContainsValue(rlm->_timers, rlt); |
| __CFRunLoopModeUnlock(rlm); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| return hasValue; |
| } |
| |
| void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| if (__CFRunLoopIsDeallocating(rl)) return; |
| if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; |
| if (NULL == rl->_commonModeItems) { |
| rl->_commonModeItems = CFSetCreateMutable(CFGetAllocator(rl), 0, &kCFTypeSetCallBacks); |
| } |
| CFSetAddValue(rl->_commonModeItems, rlt); |
| __CFRunLoopUnlock(rl); |
| if (NULL != set) { |
| CFTypeRef context[2] = {rl, rlt}; |
| /* add new item to all common-modes */ |
| CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context); |
| CFRelease(set); |
| } |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, true); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL == rlm->_timers) { |
| rlm->_timers = CFSetCreateMutable(CFGetAllocator(rlm), 0, &kCFTypeSetCallBacks); |
| } |
| if (NULL != rlm && !CFSetContainsValue(rlm->_timers, rlt)) { |
| CFSetAddValue(rlm->_timers, rlt); |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopTimerSchedule(rlt, rl, rlm); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) { |
| CHECK_FOR_FORK(); |
| CFRunLoopModeRef rlm; |
| __CFRunLoopLock(rl); |
| if (modeName == kCFRunLoopCommonModes) { |
| if (NULL != rl->_commonModeItems && CFSetContainsValue(rl->_commonModeItems, rlt)) { |
| CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; |
| CFSetRemoveValue(rl->_commonModeItems, rlt); |
| __CFRunLoopUnlock(rl); |
| if (NULL != set) { |
| CFTypeRef context[2] = {rl, rlt}; |
| /* remove new item from all common-modes */ |
| CFSetApplyFunction(set, (__CFRunLoopRemoveItemFromCommonModes), (void *)context); |
| CFRelease(set); |
| } |
| } else { |
| __CFRunLoopUnlock(rl); |
| } |
| } else { |
| rlm = __CFRunLoopFindMode(rl, modeName, false); |
| __CFRunLoopUnlock(rl); |
| if (NULL != rlm && NULL != rlm->_timers && CFSetContainsValue(rlm->_timers, rlt)) { |
| CFRetain(rlt); |
| CFSetRemoveValue(rlm->_timers, rlt); |
| __CFRunLoopModeUnlock(rlm); |
| __CFRunLoopTimerCancel(rlt, rl, rlm); |
| CFRelease(rlt); |
| } else if (NULL != rlm) { |
| __CFRunLoopModeUnlock(rlm); |
| } |
| } |
| } |
| |
| |
| /* CFRunLoopSource */ |
| |
| static Boolean __CFRunLoopSourceEqual(CFTypeRef cf1, CFTypeRef cf2) { /* DOES CALLOUT */ |
| CFRunLoopSourceRef rls1 = (CFRunLoopSourceRef)cf1; |
| CFRunLoopSourceRef rls2 = (CFRunLoopSourceRef)cf2; |
| if (rls1 == rls2) return true; |
| if (rls1->_order != rls2->_order) return false; |
| if (rls1->_context.version0.version != rls2->_context.version0.version) return false; |
| if (rls1->_context.version0.hash != rls2->_context.version0.hash) return false; |
| if (rls1->_context.version0.equal != rls2->_context.version0.equal) return false; |
| if (0 == rls1->_context.version0.version && rls1->_context.version0.perform != rls2->_context.version0.perform) return false; |
| if (1 == rls1->_context.version0.version && rls1->_context.version1.perform != rls2->_context.version1.perform) return false; |
| if (rls1->_context.version0.equal) |
| return rls1->_context.version0.equal(rls1->_context.version0.info, rls2->_context.version0.info); |
| return (rls1->_context.version0.info == rls2->_context.version0.info); |
| } |
| |
| static CFHashCode __CFRunLoopSourceHash(CFTypeRef cf) { /* DOES CALLOUT */ |
| CFRunLoopSourceRef rls = (CFRunLoopSourceRef)cf; |
| if (rls->_context.version0.hash) |
| return rls->_context.version0.hash(rls->_context.version0.info); |
| return (CFHashCode)rls->_context.version0.info; |
| } |
| |
| static CFStringRef __CFRunLoopSourceCopyDescription(CFTypeRef cf) { /* DOES CALLOUT */ |
| CFRunLoopSourceRef rls = (CFRunLoopSourceRef)cf; |
| CFStringRef result; |
| CFStringRef contextDesc = NULL; |
| if (NULL != rls->_context.version0.copyDescription) { |
| contextDesc = rls->_context.version0.copyDescription(rls->_context.version0.info); |
| } |
| if (NULL == contextDesc) { |
| void *addr = rls->_context.version0.version == 0 ? (void *)rls->_context.version0.perform : (rls->_context.version0.version == 1 ? (void *)rls->_context.version1.perform : NULL); |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX |
| Dl_info info; |
| const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; |
| contextDesc = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %s (%p)}"), rls->_context.version0.version, rls->_context.version0.info, name, addr); |
| #elif DEPLOYMENT_TARGET_WINDOWS |
| const char *name = "???"; |
| contextDesc = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %s (%p)}"), rls->_context.version0.version, rls->_context.version0.info, name, addr); |
| #else |
| #error Unknown or unspecified DEPLOYMENT_TARGET |
| #endif |
| } |
| result = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("<CFRunLoopSource %p [%p]>{signalled = %s, valid = %s, order = %d, context = %@}"), cf, CFGetAllocator(rls), __CFRunLoopSourceIsSignaled(rls) ? "Yes" : "No", __CFIsValid(rls) ? "Yes" : "No", rls->_order, contextDesc); |
| CFRelease(contextDesc); |
| return result; |
| } |
| |
| static void __CFRunLoopSourceDeallocate(CFTypeRef cf) { /* DOES CALLOUT */ |
| CFRunLoopSourceRef rls = (CFRunLoopSourceRef)cf; |
| CFRunLoopSourceInvalidate(rls); |
| if (rls->_context.version0.release) { |
| rls->_context.version0.release(rls->_context.version0.info); |
| } |
| } |
| |
| static const CFRuntimeClass __CFRunLoopSourceClass = { |
| _kCFRuntimeScannedObject, |
| "CFRunLoopSource", |
| NULL, // init |
| NULL, // copy |
| __CFRunLoopSourceDeallocate, |
| __CFRunLoopSourceEqual, |
| __CFRunLoopSourceHash, |
| NULL, |