阅读 127

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

上两篇文章说了Runloop初探Runloop获取,这篇来说说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.RunLoop包含多个mode,每个mode包含多个sources0事件,多个sources1事件,多个timer事件,多个observer事件和多portRunLoop总在某个mode下运行的:

对应关系

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

作者:无悔zero

原文链接:https://www.jianshu.com/p/6392d6b47ba4

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐