美文网首页Runloop、释放池、事件响应
Runloop源码分析(3)——执行流程

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

作者: 无悔zero | 来源:发表于2021-01-21 18:00 被阅读0次

    上两篇文章说了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 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。

    相关文章

      网友评论

        本文标题:Runloop源码分析(3)——执行流程

        本文链接:https://www.haomeiwen.com/subject/wprlzktx.html