// should only be called by Foundation // t==0 is a synonym for "main thread" that always works CF_EXPORT CFRunLoopRef _CFRunLoopGet0(_CFThreadRef t) { /// kNilPthreadT == (pthread *)0x0 if (pthread_equal(t, kNilPthreadT)) { t = pthread_main_thread_np(); } __CFLock(&loopsLock); if (!__CFRunLoops) { /// 创建缓存字典。随后创建主线程的 run loop,并且以 key(thread)/value(runloop) 的形式保存到缓存字典 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); /// 将 main runloop 保存到缓存字典中,其中 key 为主线程 CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) { CFRelease(dict); } CFRelease(mainLoop); }
CFRunLoopRef newLoop = NULL; /// 在缓存中查找 runloop 是否已经创建 CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); if (!loop) { /// 如果 runloop 不存在则新建一个,并保存到缓存中 newLoop = __CFRunLoopCreate(t); cf_trace(KDEBUG_EVENT_CFRL_LIFETIME|DBG_FUNC_START, newLoop, NULL, NULL, NULL); CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop); loop = newLoop; } __CFUnlock(&loopsLock); // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it if (newLoop) { CFRelease(newLoop); } if (pthread_equal(t, pthread_self())) { /// TSD means thread specific data _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL); if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) { #if _POSIX_THREADS /// PTHREAD_DESTRUCTOR_ITERATIONS 的定义为 4 _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop); #else _CFSetTSD(__CFTSDKeyRunLoopCntr, 0, &__CFFinalizeRunLoop); #endif } } return loop; }
_CFRunLoopGet0 函数大致做了下面几样事情
进入到这个函数,首先会判断缓存字典 dict 是否存在,如果不存在则创建
随后在缓存中查找 main runloop 是否存在,若不存在则创建 main runloop,并以 key(thread)/value(runloop) 的形式保存到缓存中,
注意到 runloop 使用 loop->_wakeUpPort = __CFPortAllocate((uintptr_t)loop); 代码为其生成了一个 wake up port。就像申请了一个 qq 号一样,你可以登录这个账号接收到别人发给你的消息,在这里,runloop 使用这个 mach port 来接受被 wake up 的消息 同时,runloop 还会创建一个 kCFRunLoopDefaultMode 默认类型的 mode
rlm->_timerFired = false; rlm->_queue = _dispatch_runloop_root_queue_create_4CF("Run Loop Mode Queue", 0); mach_port_t queuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue); if (queuePort == MACH_PORT_NULL) CRASH("*** Unable to create run loop mode queue port. (%d) ***", -1); rlm->_timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, rlm->_queue); __block Boolean *timerFiredPointer = &(rlm->_timerFired); dispatch_source_set_event_handler(rlm->_timerSource, ^{ *timerFiredPointer = true; }); // Set timer to far out there. The unique leeway makes this timer easy to spot in debug output. dispatch_source_set_timer(rlm->_timerSource, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 321); dispatch_resume(rlm->_timerSource); ret = __CFPortSetInsert(queuePort, rlm->_portSet);
rlm->_timerPort = mk_timer_create(); if (rlm->_timerPort == MACH_PORT_NULL) { CRASH("*** Unable to create timer Port (%d) ***", rlm->_timerPort); } ret = __CFPortSetInsert(rlm->_timerPort, rlm->_portSet); if (KERN_SUCCESS != ret) CRASH("*** Unable to insert timer port into port set. (%d) ***", ret);
ret = __CFPortSetInsert(rl->_wakeUpPort, rlm->_portSet); if (KERN_SUCCESS != ret) CRASH("*** Unable to insert wake up port into port set. (%d) ***", ret);
static void __CFRepositionTimerInMode(CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt, Boolean isInArray) { if (!rlt) return; CFMutableArrayRef timerArray = rlm->_timers; if (!timerArray) return; Boolean found = false; // If we know in advance that the timer is not in the array (just being added now) then we can skip this search /// 如果已经在数组中,则执行这一步 if (isInArray) { CFIndex idx = CFArrayGetFirstIndexOfValue(timerArray, CFRangeMake(0, CFArrayGetCount(timerArray)), rlt); if (kCFNotFound != idx) { CFRetain(rlt); CFArrayRemoveValueAtIndex(timerArray, idx); found = true; } } if (!found && isInArray) return; /// 在数组中找到合适的位置 CFIndex newIdx = __CFRunLoopInsertionIndexInTimerArray(timerArray, rlt); /// 在 timer 插入到上一步找到的位置中 CFArrayInsertValueAtIndex(timerArray, newIdx, rlt); __CFArmNextTimerInMode(rlm, rlt->_runLoop); if (isInArray) CFRelease(rlt); }
static void __CFArmNextTimerInMode(CFRunLoopModeRef rlm, CFRunLoopRef rl) { /// min soft uint64_t nextHardDeadline = UINT64_MAX; /// min hard uint64_t nextSoftDeadline = UINT64_MAX;
if (rlm->_timers) { // Look at the list of timers. We will calculate two TSR values; the next soft and next hard deadline. // The next soft deadline is the first time we can fire any timer. This is the fire date of the first timer in our sorted list of timers. // The next hard deadline is the last time at which we can fire the timer before we've moved out of the allowable tolerance of the timers in our list. for (CFIndex idx = 0, cnt = CFArrayGetCount(rlm->_timers); idx < cnt; idx++) { CFRunLoopTimerRef t = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers , idx); // discount timers currently firing if (__CFRunLoopTimerIsFiring(t)) continue; uint64_t oneTimerHardDeadline; uint64_t oneTimerSoftDeadline = t->_fireTSR; if (os_add_overflow(t->_fireTSR, __CFTimeIntervalToTSR(t->_tolerance), &oneTimerHardDeadline)) { oneTimerHardDeadline = UINT64_MAX; } // We can stop searching if the soft deadline for this timer exceeds(超过 the current hard deadline. Otherwise, later timers with lower tolerance could still have earlier hard deadlines. if (oneTimerSoftDeadline > nextHardDeadline) { break; } if (oneTimerSoftDeadline < nextSoftDeadline) { nextSoftDeadline = oneTimerSoftDeadline; } if (oneTimerHardDeadline < nextHardDeadline) { nextHardDeadline = oneTimerHardDeadline; } } /// 如果 _timerHardDeadline 和 _timerSoftDeadline 没变就不需要重新设置定时器了 if (nextSoftDeadline < UINT64_MAX && (nextHardDeadline != rlm->_timerHardDeadline || nextSoftDeadline != rlm->_timerSoftDeadline)) { if (CFRUNLOOP_NEXT_TIMER_ARMED_ENABLED()) { CFRUNLOOP_NEXT_TIMER_ARMED((unsigned long)(nextSoftDeadline - mach_absolute_time())); } cf_trace(KDEBUG_EVENT_CFRL_NEXT_TIMER_ARMED, rl, rlm, (nextSoftDeadline - mach_absolute_time()), 0); #if USE_DISPATCH_SOURCE_FOR_TIMERS // We're going to hand off the range of allowable timer fire date to dispatch and let it fire when appropriate for the system. /// 宽容度 uint64_t leeway = __CFTSRToNanoseconds(nextHardDeadline - nextSoftDeadline); dispatch_time_t deadline = __CFTSRToDispatchTime(nextSoftDeadline); if (leeway > 0) {/// 截止时间 - 预期时间 > 0 // Only use the dispatch timer if we have any leeway // <rdar://problem/14447675> // Cancel the mk timer if (rlm->_mkTimerArmed && rlm->_timerPort) { AbsoluteTime dummy; mk_timer_cancel(rlm->_timerPort, &dummy); rlm->_mkTimerArmed = false; } // Arm the dispatch timer dispatch_source_set_timer(rlm->_timerSource, deadline, DISPATCH_TIME_FOREVER, leeway); rlm->_dispatchTimerArmed = true; } else { // Cancel the dispatch timer if (rlm->_dispatchTimerArmed) { // Cancel the dispatch timer dispatch_source_set_timer(rlm->_timerSource, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 888); rlm->_dispatchTimerArmed = false; } // Arm the mk timer if (rlm->_timerPort) { mk_timer_arm(rlm->_timerPort, nextSoftDeadline); rlm->_mkTimerArmed = true; } } #else if (rlm->_timerPort) { mk_timer_arm(rlm->_timerPort, nextSoftDeadline); } #endif } else if (nextSoftDeadline == UINT64_MAX) { // Disarm the timers - there is no timer scheduled if (rlm->_mkTimerArmed && rlm->_timerPort) { AbsoluteTime dummy; mk_timer_cancel(rlm->_timerPort, &dummy); rlm->_mkTimerArmed = false; } #if USE_DISPATCH_SOURCE_FOR_TIMERS if (rlm->_dispatchTimerArmed) { dispatch_source_set_timer(rlm->_timerSource, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 333); rlm->_dispatchTimerArmed = false; } #endif } } rlm->_timerHardDeadline = nextHardDeadline; rlm->_timerSoftDeadline = nextSoftDeadline; }
/* Fire a timer */ CFRetain(rlt); __CFRunLoopTimerLock(rlt);
if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) { /// if1 start void *context_info = NULL; void (*context_release)(const void *) = NULL; if (rlt->_context.retain) { context_info = (void *)rlt->_context.retain(rlt->_context.info); context_release = rlt->_context.release; } else { context_info = rlt->_context.info; } /// _interval = 0 表示 timer 触发过一次过就会被置为不可用状态 Boolean doInvalidate = (0.0 == rlt->_interval); __CFRunLoopTimerSetFiring(rlt); // Just in case the next timer has exactly the same deadlines as this one, we reset these values so that the arm next timer code can correctly find the next timer in the list and arm the underlying timer. /// SoftDeadline 表示第一个 timer 第一次 fire 的时间,HardDeadline 表示最后一个 timer 第一次 fire 的时间 /// 重设这两个值,便于找到下一个未处理的 timer rlm->_timerSoftDeadline = UINT64_MAX; rlm->_timerHardDeadline = UINT64_MAX; __CFRunLoopTimerUnlock(rlt); __CFLock(&rl->_timerTSRLock); oldFireTSR = rlt->_fireTSR; __CFUnlock(&rl->_timerTSRLock);
__CFArmNextTimerInMode(rlm, rl);
__CFRunLoopModeUnlock(rlm); __CFRunLoopUnlock(rl); CFRunLoopTimerCallBack callout = rlt->_callout; cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_TIMER | DBG_FUNC_START, callout, rlt, context_info, 0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(callout, rlt, context_info); cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_TIMER | DBG_FUNC_END, callout, rlt, context_info, 0); CHECK_FOR_FORK(); if (doInvalidate) { /// remove timer CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */ } if (context_release) { context_release(context_info); } __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); __CFRunLoopTimerLock(rlt); timerHandled = true; __CFRunLoopTimerUnsetFiring(rlt); } /// if1 end if (__CFIsValid(rlt) && timerHandled) { /// if2 start /* This is just a little bit tricky: we want to support calling * CFRunLoopTimerSetNextFireDate() from within the callout and * honor that new time here if it is a later date, otherwise * it is completely ignored. */ if (oldFireTSR < rlt->_fireTSR) { /// if3 start /* Next fire TSR was set, and set to a date after the previous * fire date, so we honor it. */ __CFRunLoopTimerUnlock(rlt); // The timer was adjusted and repositioned, during the // callout, but if it was still the min timer, it was // skipped because it was firing. Need to redo the // min timer calculation in case rlt should now be that // timer instead of whatever was chosen. __CFArmNextTimerInMode(rlm, rl); } else { uint64_t nextFireTSR = 0LL; uint64_t intervalTSR = 0LL; if (rlt->_interval <= 0.0) { } else if (TIMER_INTERVAL_LIMIT < rlt->_interval) { intervalTSR = __CFTimeIntervalToTSR(TIMER_INTERVAL_LIMIT); } else { intervalTSR = __CFTimeIntervalToTSR(rlt->_interval); } if (LLONG_MAX - intervalTSR <= oldFireTSR) { nextFireTSR = LLONG_MAX; } else { if (intervalTSR == 0) { // 15304159: Make sure we don't accidentally loop forever here CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat"); HALT; } uint64_t currentTSR = mach_absolute_time(); nextFireTSR = oldFireTSR; while (nextFireTSR <= currentTSR) { nextFireTSR += intervalTSR; } } CFRunLoopRef rlt_rl = rlt->_runLoop; if (rlt_rl) { /// if3 start CFRetain(rlt_rl); CFIndex cnt = CFSetGetCount(rlt->_rlModes); STACK_BUFFER_DECL(CFTypeRef, modes, cnt); CFSetGetValues(rlt->_rlModes, (const void **)modes); // To avoid A->B, B->A lock ordering issues when coming up // towards the run loop from a source, the timer has to be // unlocked, which means we have to protect from object // invalidation, although that's somewhat expensive. for (CFIndex idx = 0; idx < cnt; idx++) { CFRetain(modes[idx]); } __CFRunLoopTimerUnlock(rlt); for (CFIndex idx = 0; idx < cnt; idx++) { CFStringRef name = (CFStringRef)modes[idx]; modes[idx] = (CFTypeRef)__CFRunLoopFindMode(rlt_rl, name, false); CFRelease(name); } __CFLock(&rl->_timerTSRLock); rlt->_fireTSR = nextFireTSR; rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR); for (CFIndex idx = 0; idx < cnt; idx++) { CFRunLoopModeRef rlm = (CFRunLoopModeRef)modes[idx]; if (rlm) { __CFRepositionTimerInMode(rlm, rlt, true); } } __CFUnlock(&rl->_timerTSRLock); for (CFIndex idx = 0; idx < cnt; idx++) { __CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]); } CFRelease(rlt_rl); } else { __CFRunLoopTimerUnlock(rlt); __CFLock(&rl->_timerTSRLock); rlt->_fireTSR = nextFireTSR; rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR); __CFUnlock(&rl->_timerTSRLock); } } /// if3 end } else { __CFRunLoopTimerUnlock(rlt); } /// if2 end CFRelease(rlt); cf_trace(KDEBUG_EVENT_CFRL_TIMERS_FIRING | DBG_FUNC_END, rl, rlm, rlt, 0); return timerHandled; }