CFRunLoop源码分析笔记
Posted WoodBear009
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CFRunLoop源码分析笔记相关的知识,希望对你有一定的参考价值。
之前看过不少别人博客里对CFRunLoop源码的分析,但自己一直没有去看过,今天回顾相关知识时,决定自己去看看相关的源码实现,确实又有了一些新的认识与收获.
CFRunLoopRun、CFRunLoopRunInMode
首先是两个简单的入口函数
//两个函数分别是让runloop跑在kCFRunLoopDefaultMode下,与让runloop跑在指定mode下
//两个函数区别不大,最终都调用CFRunLoopRunSpecific
/*runloop同时只能跑在一个mode下,切换mode,要退出当前runloop
什么叫退出当前runloop?所谓的退出,通过源码有了更直观的认识,其实就是传递不同的modeName,
一遍一遍的跑CFRunLoopRunSpecific->CFRunLoopRun逻辑代码,每跑完一遍,就是所谓的退出,马上迎来下一次CFRunLoopRunInMode调用
*/
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);
CFRunLoopRunSpecific
/*
参数1:传入runloop对象,实际传入的都是CFRunLoopGetCurrent
参数2:传入当前要运行的mode名称
参数3:runloop的超时时间
参数4:主要__CFRunLoopRun会用到,后面说
*/
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) /* DOES CALLOUT */
//得到当前的mode对象
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
//看看是不是一个空mode,后面会简单分析__CFRunLoopModeIsEmpty实现
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode))
//通知进入kCFRunLoopEntry状态,,后面会简单看看__CFRunLoopDoObservers实现
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//进入关键函数,__CFRunLoopRun
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//通知进入kCFRunLoopExit状态
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
看看几个重要相关函数
__CFRunLoopModeIsEmpty
//就是看看这个mode里有没有source0、source1、timer,只要存在source0或source1或timer就不是空的.同时看看这个mode是不是属于当前的runloop
static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopModeRef previousMode)
if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0)) return false;
if (NULL != rlm->_sources1 && 0 < CFSetGetCount(rlm->_sources1)) return false;
if (NULL != rlm->_timers && 0 < CFArrayGetCount(rlm->_timers)) return false;
struct _block_item *item = rl->_blocks_head;
while (item)
struct _block_item *curr = item;
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode))
doit = CFEqual(curr->_mode, rlm->_name) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(rl->_commonModes, rlm->_name));
else
doit = CFSetContainsValue((CFSetRef)curr->_mode, rlm->_name) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(rl->_commonModes, rlm->_name));
if (doit) return false;
return true;
__CFRunLoopDoObservers
//调用observer回调
//原来用过添加obserber监听runloop状态,在特定状态下执行相关逻辑,现在通过源码,看到了相关的具体实现
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) /* DOES CALLOUT */
//遍历rlm->_observers,放到collectedObservers数组中
for (CFIndex idx = 0; idx < cnt; idx++)
CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo))
collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);
//遍历collectedObservers,调用rlo->_callout,外部设置好的CFRunLoopObserverRef回调函数
/*回顾一下添加obserber监听runloop状态的代码,两部分就对应上了
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFStringRef runLoopMode = kCFRunLoopDefaultMode;
//此处传入的block应该就是赋值给rlo->_callout
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity _)
);
//observer被放到了rlm->_observers数组中
CFRunLoopAddObserver(runLoop, observer, runLoopMode);
*/
for (CFIndex idx = 0; idx < obs_cnt; idx++)
CFRunLoopObserverRef rlo = collectedObservers[idx];
__CFRunLoopObserverLock(rlo);
if (__CFIsValid(rlo))
//调用rlo->_callout,外部设置好的CFRunLoopObserverRef回调函数
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);
else
__CFRunLoopServiceMachPort
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout, voucher_mach_msg_state_t *voucherState, voucher_t *voucherCopy, CFRunLoopRef rl, CFRunLoopModeRef rlm)
这个函数可以监听Mach ports, soure1、timer、CGD都是借助Mach ports与runloop通信的。
当timeout参数为0时,立刻返回当前是否存在未处理的soure1、timer或GCD任务,通过livePort返回
当timeout参数不为0时,会一直处于监听状态,直到产生一个任务
__CFRunLoopRun
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)
uint64_t startTSR = mach_absolute_time();
//依赖GCDtimer实现超时机制
dispatch_source_t timeout_timer = NULL;
struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
//参数CFTimeInterval seconds就是外部设置的超时时间
if (seconds <= 0.0) // instant timeout
seconds = 0.0;
timeout_context->termTSR = 0ULL;
else if (seconds <= TIMER_INTERVAL_LIMIT)
dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_retain(timeout_timer);
timeout_context->ds = timeout_timer;
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;
Boolean didDispatchPortLastTime = true;
//retVal只要为0,runloop就一直在当前mode下循环
int32_t retVal = 0;
//进入runloop循环
do
__CFPortSet waitSet = rlm->_portSet;
__CFRunLoopUnsetIgnoreWakeUps(rl);
//通知observer kCFRunLoopBeforeTimers kCFRunLoopBeforeSources
if (rlm->_observerMask & kCFRunLoopBeforeTimers)
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
if (rlm->_observerMask & kCFRunLoopBeforeSources)
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
__CFRunLoopDoBlocks(rl, rlm);
//处理source0,如果有source0被处理,sourceHandledThisLoop = true
/*returnAfterSourceHandled(stopAfterHandle)参数作用:看了一下__CFRunLoopDoSources0的实现
好像是如果有一组source0要处理时,如果stopAfterHandle设置为true,则只处理第一个
*/
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop)
__CFRunLoopDoBlocks(rl, rlm);
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime)
msg = (mach_msg_header_t *)msg_buffer;
//是否有source1或timer或mainDispatch
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL, rl, rlm))
//goto label:handle_msg
//有source1或timer或mainDispatch
goto handle_msg;
didDispatchPortLastTime = false;
//根据poll决定是否通知kCFRunLoopBeforeWaiting
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
//进入循环等待
do
msg = (mach_msg_header_t *)msg_buffer;
//是否有source1或timer或mainDispatch
//注意poll对timeout参数影响
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy, rl, rlm);
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); //循环等待
rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
//根据poll决定是否通知kCFRunLoopAfterWaiting
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
//上面,如果有source1/timer/mainDispatch直接跳转到这里,label:handle_msg handle_msg
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
if (MACH_PORT_NULL == livePort)
CFRUNLOOP_WAKEUP_FOR_NOTHING();
cf_trace(KDEBUG_EVENT_CFRL_WAKEUP_FOR_NOTHING, rl, rlm, livePort, 0);
// handle nothing
else if (livePort == rl->_wakeUpPort)
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
cf_trace(KDEBUG_EVENT_CFRL_WAKEUP_FOR_WAKEUP, rl, rlm, livePort, 0);
// do nothing on Mac OS
//有timer要处理
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort)
CFRUNLOOP_WAKEUP_FOR_TIMER();
cf_trace(KDEBUG_EVENT_CFRL_WAKEUP_FOR_TIMER, rl, rlm, livePort, 0);
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time()))
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl); //有timer要处理
else if (livePort == dispatchPort)
//有dispatch_main_queue任务要处理
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
else
//处理soure1
CFRUNLOOP_WAKEUP_FOR_SOURCE();
cf_trace(KDEBUG_EVENT_CFRL_WAKEUP_FOR_SOURCE, rl, rlm, 0, 0);
// Despite the name, this works for windows handles as well
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
/* --- BLOCKS --- */
#if TARGET_OS_MAC
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif
__CFRunLoopDoBlocks(rl, rlm);
//stopAfterHandle设置为true时,有source0/1被处理了,就退出当前runloop
if (sourceHandledThisLoop && stopAfterHandle)
retVal = kCFRunLoopRunHandledSource;
else if (timeout_context->termTSR < 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)) //mode中都没有source0、source1、timer了,runloop完成
retVal = kCFRunLoopRunFinished;
while (0 == retVal);
/*retVal != kCFRunLoopRunHandledSource/kCFRunLoopRunTimedOut/ __CFRunLoopUnsetStopped/kCFRunLoopRunFinished runloop就一直在当前mode状态下循环*/
return retVal;
Mark:
- __CFRunLoopDoBlocks: 可以使用 CFRunLoopPerformBlock 函数往 run loop 中添加 blocks
- mach_port机制不仅限于source1唤醒runloop, dispatch_main_queue、timer、CFRunLoopWakeUp等都有对应的port唤醒runloop
- __CFRunLoopServiceMachPort函数用于监听port,内部依赖mach_msg函数(MACH_RCV_MSG接收消息)
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout, voucher_mach_msg_state_t *voucherState, voucher_t *voucherCopy, CFRunLoopRef rl, CFRunLoopModeRef rlm)
mach_port_name_t port: 要监听的ports
mach_port_t livePort: 接收将要处理的msg对应的port
mach_msg_timeout_t timeout: 在 timeout 之前如果没有读到 msg,当前线程会一直处于休眠状态.传0时不进入睡眠.
CFRunLoopRef rl: runloop对象
CFRunLoopModeRef rlm: mode对象
逻辑流程图
- 逻辑执行都是在beforeSource或afterWating之后的
- 以下两种情况下,runloop会跳过Waiting状态
- 在一次runloop循环开始是有source0被处理了,poll变量为true .当poll为true时,不会通知before(after)Waiting,同时第二次__CFRunLoopServiceMachPort的timeout为0,也就是立即返回,不睡眠
- 在处理source0后,通过mach port发现存在mainDispatch任务,会goto直接到最后的处理阶段
- beforeWaiting及afterWaiting之前是“睡眠”状态,属于空闲
- timer/mainDispatch/souce1任务每次循环只执行其中一种
以上是关于CFRunLoop源码分析笔记的主要内容,如果未能解决你的问题,请参考以下文章