Slide 54
Slide 54 text
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl,
CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean
stopAfterHandle, CFRunLoopModeRef previousMode) {
int64_t startTSR = (int64_t)mach_absolute_time();
if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
return kCFRunLoopRunStopped;
}
mach_port_name_t dispatchPort = MACH_PORT_NULL;
Boolean libdispatchQSafe = pthread_main_np() &&
((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL ==
previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY
&& 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
if (libdispatchQSafe && (CFRunLoopGetMain() == rl) &&
CFSetContainsValue(rl->_commonModes, rlm->_name))
dispatchPort = _dispatch_get_main_queue_port_4CF();
dispatch_source_t timeout_timer = NULL;
struct __timeout_context *timeout_context = (struct
__timeout_context *)malloc(sizeof(*timeout_context));
if (seconds <= 0.0) { // instant timeout
seconds = 0.0;
timeout_context->termTSR = 0LL;
} else if (seconds <= TIMER_INTERVAL_LIMIT) {
dispatch_queue_t queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,
DISPATCH_QUEUE_OVERCOMMIT);
timeout_timer =
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
queue);
dispatch_retain(timeout_timer);
timeout_context->ds = timeout_timer;
timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
timeout_context->termTSR = startTSR +
__CFTimeIntervalToTSR(seconds);
dispatch_set_context(timeout_timer,
timeout_context); // source gets ownership of context
dispatch_source_set_event_handler_f(timeout_timer,
__CFRunLoopTimeout);
dispatch_source_set_cancel_handler_f(timeout_timer,
__CFRunLoopTimeoutCancel);
uint64_t nanos = (uint64_t)(seconds * 1000 * 1000 +
1) * 1000;
dispatch_source_set_timer(timeout_timer,
dispatch_time(DISPATCH_TIME_NOW, nanos),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(timeout_timer);
} else { // infinite timeout
seconds = 9999999999.0;
timeout_context->termTSR = INT64_MAX;
}
Boolean didDispatchPortLastTime = true;
int32_t retVal = 0;
do {
uint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
mach_msg_header_t *msg = NULL;
#elif DEPLOYMENT_TARGET_WINDOWS
HANDLE livePort = NULL;
Boolean windowsMessageReceived = false;
#endif
__CFPortSet waitSet = rlm->_portSet;
rl->_ignoreWakeUps = false;
if (rlm->_observerMask & kCFRunLoopBeforeTimers)
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
if (rlm->_observerMask & kCFRunLoopBeforeSources)
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
__CFRunLoopDoBlocks(rl, rlm);
Boolean sourceHandledThisLoop =
__CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
Boolean poll = sourceHandledThisLoop || (0LL ==
timeout_context->termTSR);
if (MACH_PORT_NULL != dispatchPort && !
didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort,
&msg, sizeof(msg_buffer), 0)) {
goto handle_msg;
}
#elif DEPLOYMENT_TARGET_WINDOWS
if (__CFRunLoopWaitForMultipleObjects(NULL,
&dispatchPort, 0, 0, &livePort, NULL)) {
goto handle_msg;
}
#endif
}
didDispatchPortLastTime = false;
if (!poll && (rlm->_observerMask &
kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm,
kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
// do not do any user callouts after this point
(after notifying of sleeping)
// Must push the local-to-this-activation ports in
on every loop
// iteration, as this mode could be run re-
entrantly and we don't
// want these ports to get serviced.
__CFPortSetInsert(dispatchPort, waitSet);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
if (kCFUseCollectableAllocator) {
objc_clear_stack(0);
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
__CFRunLoopServiceMachPort(waitSet, &msg,
sizeof(msg_buffer), poll ? 0 : TIMEOUT_INFINITY);
#elif DEPLOYMENT_TARGET_WINDOWS
// Here, use the app-supplied message queue mask.
They will set this if they are interested in having this
run loop receive windows messages.
// Note: don't pass 0 for polling, or this thread
will never yield the CPU.
__CFRunLoopWaitForMultipleObjects(waitSet, NULL,
poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort,
&windowsMessageReceived);
#endif
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
// Must remove the local-to-this-activation ports
in on every loop
// iteration, as this mode could be run re-
entrantly and we don't
// want these ports to get serviced. Also, we don't
want them left
// in there if this function returns.
__CFPortSetRemove(dispatchPort, waitSet);
rl->_ignoreWakeUps = true;
// user callouts now OK again
__CFRunLoopUnsetSleeping(rl);
if (!poll && (rlm->_observerMask &
kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm,
kCFRunLoopAfterWaiting);
handle_msg:;
rl->_ignoreWakeUps = true;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
mach_port_t livePort = msg ? msg->msgh_local_port :
MACH_PORT_NULL;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
if (windowsMessageReceived) {
// These Win32 APIs cause a callout, so make
sure we're unlocked first and relocked after
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
if (rlm->_msgPump) {
rlm->_msgPump();
} else {
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE
| PM_NOYIELD)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
} else
#endif
if (MACH_PORT_NULL == livePort) {
// handle nothing
} else if (livePort == rl->_wakeUpPort) {
// do nothing on Mac OS
#if DEPLOYMENT_TARGET_WINDOWS
// Always reset the wake up port, or risk
spinning forever
ResetEvent(rl->_wakeUpPort);
#endif
} else if (livePort == rlm->_timerPort) {
#if DEPLOYMENT_TARGET_WINDOWS
// We use a manual reset timer to ensure that
we don't miss timers firing because the run loop did the
wakeUpPort this time
// The only way to reset a timer is to reset
the timer using SetWaitableTimer. We don't want it to fire
again though, so we set the timeout to a large negative
value. The timer may be reset again inside the timer
handling code.
LARGE_INTEGER dueTime;
dueTime.QuadPart = LONG_MIN;
SetWaitableTimer(rlm->_timerPort, &dueTime, 0,
NULL, NULL, FALSE);
#endif
__CFRunLoopDoTimers(rl, rlm,
mach_absolute_time());
} else if (livePort == dispatchPort) {
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6,
NULL);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
_dispatch_main_queue_callback_4CF(msg);
#elif DEPLOYMENT_TARGET_WINDOWS
_dispatch_main_queue_callback_4CF();
#endif
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0,
NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
didDispatchPortLastTime = true;
} else {
// Despite the name, this works for windows
handles as well
CFRunLoopSourceRef rls =
__CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
mach_msg_header_t *reply = NULL;
sourceHandledThisLoop =
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size,
&reply) || sourceHandledThisLoop;
if (NULL != reply) {
(void)mach_msg(reply, MACH_SEND_MSG,
reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
#elif DEPLOYMENT_TARGET_WINDOWS
sourceHandledThisLoop =
__CFRunLoopDoSource1(rl, rlm, rls) ||
sourceHandledThisLoop;
#endif
}
}
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
if (msg && msg != (mach_msg_header_t *)msg_buffer)
free(msg);
#endif
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR <
(int64_t)mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm,
previousMode)) {
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}