Runloop源码分析(3)——执行流程

上两篇文章说了Runloop初探Runloop获取,这篇来说说Runloop执行流程

CFRunLoopRef:代表 RunLoop 的对象
CFRunLoopModeRef:代表 RunLoop 的运行模式
CFRunLoopSourceRef:就是 RunLoop 模型图中提到的输入源 / 事件源
CFRunLoopTimerRef:就是 RunLoop 模型图中提到的定时源
CFRunLoopObserverRef:观察者,能够监听 RunLoop 的状态改变

  1. 我们直接从Runlooprun开始:
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);//调用RunLoop执行函数
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;// 如果RunLoop正在释放,返回完成
    __CFRunLoopLock(rl);
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);//查找对应的 Mode
    ...
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);//通知 Observers: 进入RunLoop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);//运行循环核心
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);//通知 Observers: 退出RunLoop
    ...
    return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    ...
    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
    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;//GCD 当中根队列的 Port,管理着所有的子队列
    if (rlm->_queue) {
        modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);//获取端口
        ...
    }
#endif
    ...
    Boolean didDispatchPortLastTime = true;//标志位默认为true
    int32_t retVal = 0;//记录最后runloop状态,用于return
    do {
        ...
        __CFRunLoopUnsetIgnoreWakeUps(rl);//设置RunLoop为可以被唤醒状态
        
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);//2、通知observer 即将处理 Timer 事件
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);//3、通知observer 即将处理 sources 事件
        
        __CFRunLoopDoBlocks(rl, rlm);//处理加入当前runloop的block
        
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);//4、处理 source0 事件
        if (sourceHandledThisLoop) {
            __CFRunLoopDoBlocks(rl, rlm);//处理加入当前runloop的block
        }
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);//如果有Sources0事件处理 或者 超时,poll都为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;
            //读取消息, 不睡眠当前线程(timeout:0)
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                goto handle_msg;//从dispatchPort读取消息
            }
#elif DEPLOYMENT_TARGET_WINDOWS
        ...
#endif
        }
        didDispatchPortLastTime = false;

        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);//8、通知 Observers: RunLoop 的线程即将进入休眠(sleep)
        __CFRunLoopSetSleeping(rl);//没有事件处理就休眠
        ...
        __CFRunLoopUnsetSleeping(rl);//9.休眠结束
        
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        //10、被谁唤醒就处理什么事件
        handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);
        ...
        if (MACH_PORT_NULL == livePort) { ... } else if (livePort == rl->_wakeUpPort) { ... }
#if USE_DISPATCH_SOURCE_FOR_TIMERS //如果是定时器事件
        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 //如果是定时器事件
        ...
#endif
        else if (livePort == dispatchPort) {//如果是dispatch到main queue的block
            ...
             __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);//处理主线程上的消息;GCD主线程回调的消息由Runloop处理,GCD子线程的消息由GCD处理
            ...
        } else {
            ...
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
                ...
                //处理 Source1
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
                ...
            }
            ...
        }
        ...
        if (sourceHandledThisLoop && stopAfterHandle) {
            retVal = kCFRunLoopRunHandledSource;//进入run loop时传入的参数,处理完事件就返回
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            retVal = kCFRunLoopRunTimedOut;//run loop超时
        } else if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);//run loop被手动终止
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;//mode被终止
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            retVal = kCFRunLoopRunFinished;//mode中没有要处理的事件
        }
        ...
    } while (0 == retVal);
    ...
    return retVal;
}
代码流程

1.在最开始的时候,渲染主线程会进入一个无限循环,每一次循环会检查消息队列中是否有任务存在。
2.如果有,就取出第一个任务执行,执行完一个后进入下一次循环;
如果没有,则进入休眠状态。
3.其他所有线程(包括其他进程的线程)可以随时向消息队列添加任务,新任务会加到消息队列的末尾;
在添加新任务时,如果主线程是休眠状态,则会将其唤醒以继续循环拿取任务。

  • 补充

1.RunLoop包含多个mode,每个mode包含多个sources0事件,多个sources1事件,多个timer事件,多个observer事件和多portRunLoop总在某个mode下运行的:

对应关系

每次 RunLoop 启动时,只能指定其中一个运行模式(CFRunLoopModeRef),这个运行模式(CFRunLoopModeRef)被称作当前运行模式(CurrentMode)。
如果需要切换运行模式(CFRunLoopModeRef),只能退出当前 Loop,再重新指定一个运行模式(CFRunLoopModeRef)进入。
这样做主要是为了分隔开不同组的输入源(CFRunLoopSourceRef)、定时源(CFRunLoopTimerRef)、观察者(CFRunLoopObserverRef),让其互不影响 。

CFRunLoopObserverRef可以监听的状态改变有以下几种:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),               // 即将进入Loop:1
    kCFRunLoopBeforeTimers = (1UL << 1),        // 即将处理Timer:2
    kCFRunLoopBeforeSources = (1UL << 2),       // 即将处理Source:4
    kCFRunLoopBeforeWaiting = (1UL << 5),       // 即将进入休眠:32
    kCFRunLoopAfterWaiting = (1UL << 6),        // 即将从休眠中唤醒:64
    kCFRunLoopExit = (1UL << 7),                // 即将从Loop中退出:128
    kCFRunLoopAllActivities = 0x0FFFFFFFU       // 监听全部状态改变
};

2.Source0Source1区别:
Source0不能主动触发事件。Source0只有一个回调(函数指针),使用时先调用 CFRunLoopSourceSignal (source)Source 标记为待处理,然后调用 CFRunLoopWakeUp (runloop) 唤醒 RunLoop,让RunLoop处理事件。
Source1 能主动唤醒 RunLoop 的线程。Source1有一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。
Sources1是用来接收、分发系统事件,然后再分发到Sources0中处理的。

  • 补充2

网上有作者说流程图不严谨:应该由"5. 如果有source1,调到第9步"改成“5. 如果当前是主线程的runloop,并且主线程有事儿,跳到第9步”

错误标注图
  1. 通知observer run loop被触发
  2. 如果有timers事件的话,通知observer
  3. 如果有source0要处理的话,通知observer
  4. 触发所有的准备完毕的source0
  5. 如果当前是主线程的runloop,并且主线程有事儿,跳到第9步
  6. 通知Observer runloop将进入sleep状态
  7. mach进入sleep和监听状态
  8. 通知observerrunloopwoke up
  9. 如果runloop是被唤醒,CFRUNLOOP_WAKEUP_FOR_WAKEUP
  10. 如果用户定义的timer被触发,处理event并重启RunLoop
  11. 如果dispatchPort,处理主线程
  12. 如果一个source1被触发,__CFRunLoopDoSource1
  13. 继续循环或通知observer runloop将要exited

参考:https://mp.weixin.qq.com/s/9UU9vls32iry8eImNLKHeQ

推荐一篇通熟易懂的runloop博客:https://mp.weixin.qq.com/s/uZFbT_bBtT-ealc-UI7Agg

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容