Table of Contents
CFRunLoopRun.c
/** * 运行run loop * * @param rl 运行的RunLoop对象 * @param rlm 运行的mode * @param seconds run loop超时时间 * @param stopAfterHandle true:run loop处理完事件就退出 false:一直运行直到超时或者被手动终止 * @param previousMode 上一次运行的mode * * @return 返回4种状态 */ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { // 获取系统启动后的CPU运行时间,用于控制超时时间 uint64_t startTSR = mach_absolute_time(); // 如果RunLoop或者mode是stop状态,则直接return,不进入循环 if (__CFRunLoopIsStopped(rl)) { __CFRunLoopUnsetStopped(rl); return kCFRunLoopRunStopped; } else if (rlm->_stopped) { rlm->_stopped = false; return kCFRunLoopRunStopped; } // mach端口,内核进程通信消息端口。初始为0 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))); // 如果在主线程 && runloop是主线程的runloop && 该mode是commonMode,则给 dispatchPort 赋值为主线程收发消息的端口 if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF(); #if USE_DISPATCH_SOURCE_FOR_TIMERS mach_port_name_t modeQueuePort = MACH_PORT_NULL; if (rlm->_queue) { // modeQueuePort 赋值为 dispatch 端口 modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue); if (!modeQueuePort) { CRASH("Unable to get port for run loop mode queue (%d)", -1); } } #endif // GCD管理的定时器,用于实现runloop超时机制 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 = 0ULL; } // seconds为超时时间,超时时执行__CFRunLoopTimeout函数 else if (seconds <= TIMER_INTERVAL_LIMIT) { // 启动一个 dispatch timer 来监控超时 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 ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL); dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL); dispatch_resume(timeout_timer); } // 永不超时 else { // infinite timeout seconds = 9999999999.0; timeout_context->termTSR = UINT64_MAX; } // 标志位默认为true Boolean didDispatchPortLastTime = true; // 记录最后runloop状态,用于return 退出 runloop int32_t retVal = 0; do { // 初始化一个存放内核消息的缓冲池 uint8_t msg_buffer[3 * 1024]; #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI mach_msg_header_t *msg = NULL; // 活动端口,本地用来接收消息的端口 mach_port_t livePort = MACH_PORT_NULL; #elif DEPLOYMENT_TARGET_WINDOWS HANDLE livePort = NULL; Boolean windowsMessageReceived = false; #endif // 取所有需要监听的port __CFPortSet waitSet = rlm->_portSet; // 设置RunLoop为可以被唤醒状态 __CFRunLoopUnsetIgnoreWakeUps(rl); // 2. 通知observer,即将触发timer回调,处理timer事件 if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); // 3. 通知observer,即将触发Source0回调 if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); // 执行加入当前 runloop 的 block __CFRunLoopDoBlocks(rl, rlm); // 4. 处理 source0 事件 // 有事件处理返回 true,没有事件返回 false Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); if (sourceHandledThisLoop) { // 执行加入当前 runloop 的 block __CFRunLoopDoBlocks(rl, rlm); } // 如果没有 Sources0 事件处理 并且 没有超时,poll 为 false // 如果有 Sources0 事件处理 或者 超时,poll 都为 true Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); // 第一次do..whil循环不会走该分支,因为 didDispatchPortLastTime 初始化是 true if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI // 从缓冲区读取消息 msg = (mach_msg_header_t *)msg_buffer; // 5. 接收 dispatchPort 端口的消息,dispatch 到 main queue 的事件。 if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) { // 如果接收到了消息的话,前往 handle_msg 开始处理 msg goto handle_msg; } #elif DEPLOYMENT_TARGET_WINDOWS if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) { goto handle_msg; } #endif } didDispatchPortLastTime = false; // 6. 通知观察者RunLoop即将进入休眠 if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); // 设置RunLoop为休眠状态 __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 || DEPLOYMENT_TARGET_EMBEDDED_MINI #if USE_DISPATCH_SOURCE_FOR_TIMERS // 这里有个内循环,用于接收等待端口的消息 // 进入此循环后,线程进入休眠,直到收到新消息才跳出该循环,继续执行run loop do { if (kCFUseCollectableAllocator) { objc_clear_stack(0); // 清空 msg_buffer memset(msg_buffer, 0, sizeof(msg_buffer)); } msg = (mach_msg_header_t *)msg_buffer; // 7. 接收waitSet端口的消息 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY); // 收到消息之后,livePort 的值为本地接收消息的活动端口 if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer. while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue)); if (rlm->_timerFired) { // Leave livePort as the queue port, and service timers below rlm->_timerFired = false; break; } else { if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); } } else { // Go ahead and leave the inner loop. break; } } while (1); #else 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), &livePort, poll ? 0 : TIMEOUT_INFINITY); #endif #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. __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); // 设置 runloop 不可被唤醒 __CFRunLoopSetIgnoreWakeUps(rl); // user callouts now OK again // 取消runloop的休眠状态 __CFRunLoopUnsetSleeping(rl); // 8. 通知观察者runloop被唤醒 if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); // 9. 处理通过端口收到的消息 handle_msg:; // 设置 runloop 不可被唤醒 __CFRunLoopSetIgnoreWakeUps(rl); #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; // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later. // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling. __CFRunLoopSetSleeping(rl); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL); __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); __CFRunLoopUnsetSleeping(rl); // If we have a new live port then it will be handled below as normal } #endif if (MACH_PORT_NULL == livePort) { CFRUNLOOP_WAKEUP_FOR_NOTHING(); // handle nothing } // 通过CFRunloopWake唤醒, else if (livePort == rl->_wakeUpPort) { // 什么都不干,跳回2重新循环 CFRUNLOOP_WAKEUP_FOR_WAKEUP(); // do nothing on Mac OS #if DEPLOYMENT_TARGET_WINDOWS // Always reset the wake up port, or risk spinning forever ResetEvent(rl->_wakeUpPort); #endif } #if USE_DISPATCH_SOURCE_FOR_TIMERS // 如果是 timer 事件 else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); // 处理timer事件 if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { // Re-arm the next timer, because we apparently fired early __CFArmNextTimerInMode(rlm, rl); } } #endif #if USE_MK_TIMER_TOO else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { CFRUNLOOP_WAKEUP_FOR_TIMER(); // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled. // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754 if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { // Re-arm the next timer __CFArmNextTimerInMode(rlm, rl); } } #endif // 如果是 dispatch 到 main queue 的 block else if (livePort == dispatchPort) { CFRUNLOOP_WAKEUP_FOR_DISPATCH(); __CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL); #if DEPLOYMENT_TARGET_WINDOWS void *msg = 0; #endif // 执行block __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL); __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); sourceHandledThisLoop = true; didDispatchPortLastTime = true; } // source1 事件 else { CFRUNLOOP_WAKEUP_FOR_SOURCE(); // Despite the name, this works for windows handles as well // 根据接收消息的 port 寻找 source1 事件 CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort); // 有 source1 事件待处理 if (rls) { #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI mach_msg_header_t *reply = NULL; // 处理 source1 事件 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 || DEPLOYMENT_TARGET_EMBEDDED_MINI if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); #endif __CFRunLoopDoBlocks(rl, rlm); // 处理完当前事件 & runloop 执行完就退出 if (sourceHandledThisLoop && stopAfterHandle) { retVal = kCFRunLoopRunHandledSource; } // run loop超时 else if (timeout_context->termTSR < mach_absolute_time()) { retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(rl)) { // run loop被手动终止 __CFRunLoopUnsetStopped(rl); retVal = kCFRunLoopRunStopped; } else if (rlm->_stopped) { // mode被终止 rlm->_stopped = false; retVal = kCFRunLoopRunStopped; } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { // mode中没有要处理的事件 retVal = kCFRunLoopRunFinished; } } while (0 == retVal); if (timeout_timer) { dispatch_source_cancel(timeout_timer); dispatch_release(timeout_timer); } else { free(timeout_context); } return retVal; }
CFRunLoopRunSpecific.c
/* * 指定mode运行runloop * @param rl 当前运行的runloop * @param modeName 需要运行的mode的name * @param seconds runloop的超时时间 * @param returnAfterSourceHandled 是否处理完事件就返回 */ SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ CHECK_FOR_FORK(); if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished; __CFRunLoopLock(rl); //根据modeName找到本次运行的mode CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false); //如果没找到 || mode中没有注册任何事件,则就此停止,不进入循环 if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) { Boolean did = false; if (currentMode) __CFRunLoopModeUnlock(currentMode); __CFRunLoopUnlock(rl); return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished; } volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl); //取上一次运行的mode CFRunLoopModeRef previousMode = rl->_currentMode; // 切换 runkloop 当前 mode rl->_currentMode = currentMode; int32_t result = kCFRunLoopRunFinished; // 1. 通知observer即将进入runloop if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode); // 10. 通知observer已退出runloop if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); __CFRunLoopModeUnlock(currentMode); __CFRunLoopPopPerRunData(rl, previousPerRun); // 执行完成之后把复位 mode rl->_currentMode = previousMode; __CFRunLoopUnlock(rl); return result; }
CFRunLoopServiceMachPort.c
/** * 接收指定内核端口的消息 * * @param port 接收消息的端口 * @param buffer 消息缓冲区 * @param buffer_size 消息缓冲区大小 * @param livePort 暂且理解为活动的端口,接收消息成功时候值为msg->msgh_local_port,超时时为MACH_PORT_NULL * @param timeout 超时时间,单位是ms,如果超时,则RunLoop进入休眠状态 * * @return 接收消息成功时返回true 其他情况返回false */ static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout) { Boolean originalBuffer = true; kern_return_t ret = KERN_SUCCESS; for (;;) { /* In that sleep of death what nightmares may come ... */ mach_msg_header_t *msg = (mach_msg_header_t *)*buffer; msg->msgh_bits = 0;// 消息头的标志位 msg->msgh_local_port = port;// 本地消息端口 msg->msgh_remote_port = MACH_PORT_NULL;// 目标消息端口 msg->msgh_size = buffer_size;// 消息缓冲区大小 msg->msgh_id = 0;// msg id if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); } // 系统底层处理消息的机制 ret = mach_msg( msg, MACH_RCV_MSG|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, port, timeout, MACH_PORT_NULL); CFRUNLOOP_WAKEUP(ret); // 接收/发送消息成功,给 livePort 赋值为 msgh_local_port if (MACH_MSG_SUCCESS == ret) { *livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL; return true; } // 接收消息超时 if (MACH_RCV_TIMED_OUT == ret) { if (!originalBuffer) free(msg); *buffer = NULL; *livePort = MACH_PORT_NULL; return false; } if (MACH_RCV_TOO_LARGE != ret) break; // 消息体过大,分配更多的内存 buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE); if (originalBuffer) *buffer = NULL; originalBuffer = false; *buffer = realloc(*buffer, buffer_size); } HALT; return false; }