关于RunLoop部分源码的注释

OpenCode 架构深度解析——从 CLI 交互式输入到 Agent 工具调用的完整链路 每个 Agent 的核心属性:**name** · **mode** (primary/subagent) · **permission**(工具权限规则)· **prompt**(系统提示词)· **steps**(最大步数)· **temperature/topP**`text-delta` / `reasoning-delta` 逐字更新。返回三种结果之一:`"compact"` / `"stop"` / `"continue"`。 阅读详情

RunLoop源代码是开发的,是Core Fundation框架的一部分,可以在线查看和或者下载。本篇博文是对RunLoop部分源码进行注释,因本人知识有限,如有注释不妥的地方请指教。

#if DEPLOYMENT_TARGET_WINDOWS

// kNilPthreadT 为空的线程
static pthread_t kNilPthreadT = { nil, nil };
#define pthreadPointer(a) a.p
typedef int kern_return_t;
#define KERN_SUCCESS 0

#else

// kNilPthreadT 为空的线程
static pthread_t kNilPthreadT = (pthread_t)0;
#define pthreadPointer(a) a
#define lockCount(a) a
#endif


/************************** RunLoop的获取相关代码 ****************************/

// 声明一个全局的可变字典 __CFRunLoops 用于存储每一条线程和对应的RunLoop ,
// __CFRunLoops的键key存储线程pthread_t,键对应的值value存储RunLoop
static CFMutableDictionaryRef __CFRunLoops = NULL;

// loopsLock 用于访问 __CFRunLoops 时加锁,锁的目的是:考虑线程安全问题,防止多条线程同时访问__CFRunLoops对应的内存空间
static CFLock_t loopsLock = CFLockInit;


/**
 * 这个函数中的作用是获取RunLoop,RunLoop是懒加载的
 */
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {

    // 1. 判断t所指向的线程是否为空,如果为空,就获取当前的主线程进行赋值
    if (pthread_equal(t, kNilPthreadT)) {
        // 1.1 获取当前的主线程进行赋值
        t = pthread_main_thread_np();
    }


    // 2. 处理线程安全,即加锁
    __CFLock(&loopsLock);


    // 3. 判断全局的可变字典 __CFRunLoops 是否为空
    if (!__CFRunLoops) {

        // 3.1 解锁
        __CFUnlock(&loopsLock);

        // 3.2 定义一个局部可变字典dict
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);

        // 3.3 创建一个与主线程对应的RunLoop对象mainLoop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());

        // 3.4 把主线程和主运行循环mainLoop存储于字典dict
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);

        // 3.5 把局部创建的字典dict赋值给全局字典__CFRunLoops
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {

            // 3.5.1 赋值成功后,release局部变量
            CFRelease(dict);
        }

        // 3.6 存储mainLoop到字典以后撤销一次引用,即release一次mainLoop
        // 原因:mainLoop存储到字典后会自动retain
        CFRelease(mainLoop);

        // 3.7 加锁
        __CFLock(&loopsLock);
    }


    // 4. 从全局的字典__CFRunLoops中获取对应于当前线程的RunLoop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));


    // 5. 解锁
    __CFUnlock(&loopsLock);


    // 6. 如果获取不到RunLoop就创建并存储
    // 注意:在子线程中第一次获取RunLoop是获取不到的,获取不到时才去创建,这个if语句一般在子线程中第一次获取的时候才会执行
    if (!loop) {

        // 6.1 根据当前线程创建一个RunLoop
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);

        // 6.2 加锁
        __CFLock(&loopsLock);

        // 6.3 再一次从全局的字典__CFRunLoops中获取对应于当前线程的RunLoop
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));

        // 6.4 如果还是获取不到RunLoop,就存储当前线程和刚创建的RunLoop到全局字典__CFRunLoops中
        if (!loop) {

            // 6.4.1 存储当前线程和对应的RunLoop到全局字典__CFRunLoops中
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);

            // 6.4.2 赋值,用于返回值
            loop = newLoop;
        }


        // 6.4.3 解锁
        __CFUnlock(&loopsLock);

        // 6.4.4 release局部的newLoop
        CFRelease(newLoop);
    }


    // 7. 判断是否为当前线程,如果是就注册一个回调,当线程销毁时,也销毁其对应的 RunLoop
    if (pthread_equal(t, pthread_self())) {

        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }

    // 8. 返回当前线程对应的RunLoop
    return loop;
}


/********************************** 获取RunLoop *******************************/


/**
 * 这个函数中的作用是获取主运行循环MainRunLoop
 */
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}

/**
 * 这个函数中的作用是获取当前线程的运行循环RunLoop
 */
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}


/**************************** RunLoop运行的核心代码 *******************************/


/**
 * RunLoop的运行核心
 */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {

    // 获取系统启动后的CPU运行时间,用于控制超时时间
    uint64_t startTSR = mach_absolute_time();


    // 判断RunLoop是否已经被终止,被终止就直接退出,并返回退出原因
    if (__CFRunLoopIsStopped(rl)) {
        __CFRunLoopUnsetStopped(rl);
        return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
        rlm->_stopped = false;
        return kCFRunLoopRunStopped;
    }


    // 调度端口,用于消息的传递,初始为0
    mach_port_name_t dispatchPort = MACH_PORT_NULL;
    // 判断是否是第一次在主线程中启动RunLoop
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    // 如果是第一次启动RunLoop且当前RunLoop为主线程的RunLoop,那么就给分发一个队列调度端口
    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;

    // 如果运行模式的队列存在就
    if (rlm->_queue) {
        // 给运行模式的调度端口分发一个关于RunLoop的主队列端口
        modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
        // 如果分发不成功,提示原因
        if (!modeQueuePort) {
            CRASH("Unable to get port for run loop mode queue (%d)", -1);
        }
    }
#endif


    // 超时定时器
    dispatch_source_t timeout_timer = NULL;
    // 给超时定时器的上下文开辟一个内存空间
    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
    // 如果超时时间非常小就把超时时间设为0.0秒和期限设为0
    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;
        timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
        timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
        // 设置定时器的上下文
        dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
        // 设置source的活动
        dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
        // 设置source的取消
        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
        // 根据seconds计算超时时间
        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
        // 设置source的超时时间
        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
        // 开始计时
        dispatch_resume(timeout_timer);
    } else { // 如果超时时间不在系统可接受的范围,就认为是无限大,且期限是无限的
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }


    // 标记已经运行结束
    Boolean didDispatchPortLastTime = true;
    // 循环条件
    int32_t retVal = 0;

    do {

        // 判断当前的系统类型
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
        voucher_t voucherCopy = NULL;
#endif

        // 声明一个缓存空间
        uint8_t msg_buffer[3 * 1024];

        // 判断当前的系统类型
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI

        mach_msg_header_t *msg = NULL;
        mach_port_t livePort = MACH_PORT_NULL;

        // 系统是WINDOWS,运行下列代码
#elif DEPLOYMENT_TARGET_WINDOWS
        HANDLE livePort = NULL;
        Boolean windowsMessageReceived = false;
#endif


        // 获取RunLoop的运行模式的集合
        __CFPortSet waitSet = rlm->_portSet;
        // 设置RunLoop可以被唤醒
        __CFRunLoopUnsetIgnoreWakeUps(rl);

        // 判断是否即将要处理定时器源,如果是就通知观察者
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        // 判断是否即将要处理非定时器源,如果是就通知观察者
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

        // 根据运行模式设置RunLoop
        __CFRunLoopDoBlocks(rl, rlm);

        // 处理Sources0(非基于端口的源)
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        // Sources0处理完后,根据运行模式设置RunLoop
        if (sourceHandledThisLoop) {
            __CFRunLoopDoBlocks(rl, rlm);
        }

        // 是否轮询,如何刚刚有 Sources0事件处理 或者 时间超时 就在RunLoop的下一轮循环中继续检查
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);


        // 如果基于端口的源有消息且超时时,才执行下列代码,第一次RunLoop中不会执行下列代码,因为didDispatchPortLastTime初始为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;
            // 处理未处理的事件
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                goto handle_msg;
            }

            // 在WINDOWS系统情况下
#elif DEPLOYMENT_TARGET_WINDOWS
            if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
                goto handle_msg;
            }
#endif
        }


        // 标记运行没有结束
        didDispatchPortLastTime = false;


        // 通知观察者线程进入休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        // 设置线程进入休眠
        __CFRunLoopSetSleeping(rl);


        // 还原RunLoop的运行模式的集合
        __CFPortSetInsert(dispatchPort, waitSet);

        // RunLoop运行模式开锁
        __CFRunLoopModeUnlock(rlm);
        // RunLoop开锁
        __CFRunLoopUnlock(rl);

        // 根据是否轮询,判断睡眠的开始时间
        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI

    // 定时器源
#if USE_DISPATCH_SOURCE_FOR_TIMERS


        // 定时源处于等待时,线程进入休眠,直到收到新消息才跳出该循环,继续执行RunLoop
        do {

            if (kCFUseCollectableAllocator) {

                memset(msg_buffer, 0, sizeof(msg_buffer));
            }

            msg = (mach_msg_header_t *)msg_buffer;

            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

            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);
#else
        if (kCFUseCollectableAllocator) {

            memset(msg_buffer, 0, sizeof(msg_buffer));
        }
        msg = (mach_msg_header_t *)msg_buffer;
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
#endif

        // 如果是在WINDOWS下,运行下列代码
#elif DEPLOYMENT_TARGET_WINDOWS
        // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
        __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
#endif


        __CFRunLoopLock(rl);
        __CFRunLoopModeLock(rlm);

        rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));

        // Must remove the local-to-this-activation ports in on every loop
        // iteration, as this mode could be run re-entrantly and we don't
        // want these ports to get serviced. Also, we don't want them left
        // in there if this function returns.

        __CFPortSetRemove(dispatchPort, waitSet);

        __CFRunLoopSetIgnoreWakeUps(rl);

        // user callouts now OK again
        __CFRunLoopUnsetSleeping(rl);
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

    handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);

        // 如果是在WINDOWS下,运行下列代码
#if DEPLOYMENT_TARGET_WINDOWS
        if (windowsMessageReceived) {
            // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);

            if (rlm->_msgPump) {
                rlm->_msgPump();
            } else {
                MSG msg;
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }

            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;

            // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced
            // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later.
            // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
            __CFRunLoopSetSleeping(rl);
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);

            __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL);

            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            __CFRunLoopUnsetSleeping(rl);
            // If we have a new live port then it will be handled below as normal
        }


#endif


        if (MACH_PORT_NULL == livePort) {

            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
        } else if (livePort == rl->_wakeUpPort) {

            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS

#if DEPLOYMENT_TARGET_WINDOWS
            // Always reset the wake up port, or risk spinning forever
            ResetEvent(rl->_wakeUpPort);
#endif
        }

        // 如果是定时器源
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        // 如果运行模式的任务队列不为空,且处于活动状态
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_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
        // 如果定时器源不为空,且处于活动状态
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled.
            // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754

            // 处理定时器事件
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                // 定时器事件处理完移除当前的定时器,并准备下一个定时器
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
        // 如果活动端口是消息调度端口
        else if (livePort == dispatchPort) {

            // 唤醒RunLoop
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            // RunLoop运行模式解锁
            __CFRunLoopModeUnlock(rlm);
            // RunLoop解锁
            __CFRunLoopUnlock(rl);

            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
#if DEPLOYMENT_TARGET_WINDOWS
            void *msg = 0;
#endif

            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);

            // RunLoop加锁
            __CFRunLoopLock(rl);
            // RunLoop运行模式加锁
            __CFRunLoopModeLock(rlm);

            // RunLoop处理完事件就退出
            sourceHandledThisLoop = true;
            // 标记已经运行结束
            didDispatchPortLastTime = true;

        } else {
            // 唤醒RunLoop
            CFRUNLOOP_WAKEUP_FOR_SOURCE();

            // If we received a voucher from this mach_msg, then put a copy of the new voucher into TSD. CFMachPortBoost will look in the TSD for the voucher. By using the value in the TSD we tie the CFMachPortBoost to this received mach_msg explicitly without a chance for anything in between the two pieces of code to set the voucher again.
            voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);

            // Despite the name, this works for windows handles as well
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI

                mach_msg_header_t *reply = NULL;

                // 处理Source1(基于端口的源)
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;

                if (NULL != reply) {
                    (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
                    CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
                }
#elif DEPLOYMENT_TARGET_WINDOWS
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
            }

            // Restore the previous voucher
            _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);

        }
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI

        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif

        // 根据运行模式设置RunLoop
        __CFRunLoopDoBlocks(rl, rlm);

        // 判断是否要退出RunLoop
        if (sourceHandledThisLoop && stopAfterHandle) {
            // 处理完事件后退出
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            // 超时退出
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            // 调用CFRunLoopStop()退出
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            // 运行模式被终止时退出
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            // 没有要处理的事件时退出
            retVal = kCFRunLoopRunFinished;
        }

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_revert(voucherState);
        os_release(voucherCopy);
#endif

    } while (0 == retVal);


    // 退出RunLoop前处理超时定时器
    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}


/**
 * RunLoop的运行细节
 */
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
{

    CHECK_FOR_FORK();

    // 1. 判断RunLoop是否被销毁,已被销毁就返回退出原因kCFRunLoopRunFinished
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;

    // 2. 给加RunLoop锁
    __CFRunLoopLock(rl);

    // 3. 根据运行模式的名字modeName获取运行模式currentMode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);

    // 4. 判断运行模式currentMode是否存在,或运行模式currentMode是否为空,即判断Mode中是否有source/timer/observer
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {

        // 4.1 标记退出,如果运行模式不能正常使用,就以kCFRunLoopRunFinished方式退出RunLoop
        Boolean did = false;

        // 4.2 如果运行模式currentMode存在,就解锁运行模式currentMode
        if (currentMode)
            __CFRunLoopModeUnlock(currentMode);

        // 4.3 给RunLoop解锁
        __CFRunLoopUnlock(rl);

        // 4.4 根据标记为返回对应的退出原因
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }

    // 5. 保存正在运行的RunLoop的运行模式,把相关数据压进栈
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);

    // 6. 保存正在运行的RunLoop的运行模式
    CFRunLoopModeRef previousMode = rl->_currentMode;

    // 7. 修改当前RunLoop的运行模式
    rl->_currentMode = currentMode;

    // 声明一个RunLoop的运行结果
    int32_t result = kCFRunLoopRunFinished;

    // 8. 判断当前运行模式是否可以运行RunLoop
    // &运算符的运算规律:0 & 0 => 0, 0 & 1 => 0, 1 & 0 => 0, 1 & 1 => 1
    if (currentMode->_observerMask & kCFRunLoopEntry )
        // 8.1 通知观察者Observer,即将运行RunLoop
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

    // 9. 运行RunLoop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

    // 10. 判断当前运行模式是否要退出RunLoop
    if (currentMode->_observerMask & kCFRunLoopExit )
        // 10.1 通知观察者Observer,RunLoop即将退出
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    // 11. 解锁运行模式currentMode
    __CFRunLoopModeUnlock(currentMode);

    // 12. 还原上一个运行模式的运行数据
    __CFRunLoopPopPerRunData(rl, previousPerRun);

    // 13. 还原RunLoop的上一个运行模式
    rl->_currentMode = previousMode;

    // 14. 解锁RunLoop
    __CFRunLoopUnlock(rl);

    return result;
}


/**
 * 根据默认的运行模式kCFRunLoopDefaultMode运行RunLoop
 */
void CFRunLoopRun(void) {

    // result: 记录退出原因
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();

        // 当调用 CFRunLoopStop(); 或 所有的 Sources 都被移除时退出
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}


/**
 * 根据指定运行模式mode运行RunLoop
 */
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {

    CHECK_FOR_FORK();
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
CFRunLoop源码 Runloop的工作:如何管理事件/消息,如何让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒。 Osx/ios提供了两种runloop对象 : CFRunLoopRef是在CoreFoundation框架呢,是纯C函数的API,是线程安全的 NSRunLoop是基于CFRunLoopRef的封装,不是线程安全 CFRunLoopRef的线程安全性:CFRunLoopRef不是在线程刚创建的时候创建的,而是在线程获取的时候创建的,如果不主动获取线程,一直不会有。CFRunLoopRef在创建时通过static CFSpinLock_t loopsLock锁住CFRunLoopRef对象,所以CFRunLoopRef是安全的。 一个runloop包含若干个mode。每个mode又包含若干个Source/Timer/Observer CFRunLoopSourceRef是事件产生的地方。Source有两个版本: Source0和Source1。 Source0只包含一个回调,它不能主动出发事件,需要调用CFRunLoopSOurceSignal(source)将source标记,然后手动调用CFRunloopWakeUp()来唤醒RunLoop Source1包含了一个mach_port和一个回调,被用于通过内核和其他线程来发送消息,这种source能主动唤醒runloop。 CFRunLoopTimerRef是基于时间的触发器,它和NSTimer是toll-free bridged的,可以混用,包含一个时间长度和一个回调。当其加入到RunLoop时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒执行回调。 CFRunLoopObserverRef是观察者,每个Observer都包含了一个回调,当RunLoop的状态发生变化时,观察者就能通过回调接受到这个变化。 立即下载

相关推荐

NSRunLoopDemo

runloop相关的代码整理。Timer,CustomSource,DispatchSource,ConfigRunLoop,ObserverRunloop等

Runloop底层原理--源码分析

什么是Runloop? Runloop不仅仅是一个运行循环(do-while循环),也是提供了一个入口函数的对象,消息机制处理模式。运行循环从两种不同类型的源接收事件。 输入源提供异步事件,通常是来自另一个线程或来自不同应用程序的消息。定时器源提供同步事件,发生在预定时间或重复间隔。 两种类型的源都使用特定于应用程序的处理程序例程来处理事件。除了处理输入源之外,Runloop还会生成有关Runl...

Vincent的专栏 995

iOS Objective-C Runtime v723 & Runloop 打包源码

读懂iOS两部分核心源码: runtime & runloop,是每个iOS开发的基本素养。

深入理解RunLoop

RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理。之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池、延迟回调、触摸事件、屏幕刷新等功能的。

码农--xc 1499

RunLoop示例

《NSThread 、NSRunLoop 和 Dispatch Queue》一文示例源代码

基于CPCC_S_TASK_LIST_MAINTAIN的工艺路线组件批量分配实战与避坑指南

本文详细解析了SAP PP模块中基于CPCC_S_TASK_LIST_MAINTAIN函数实现工艺路线组件批量分配的实战技巧与避坑指南。针对批量分配的核心挑战,提供了参数配置、BAPI协同使用、Excel导入等解决方案,并分享了大容量数据处理优化经验,帮助用户高效完成工艺路线组件分配任务。

weixin_33563728的博客 305

RunLoop源码详解

文章目录RunLoop是什么NSRunLoop和CFRunLoopRunLoop内部结构RunLoop源码__CFRunLoop结构体__CFRunLoopMode结构体__CFRunLoopSource结构体CFRunLoopSourceContextCFRunLoopSourceContext1__CFRunLoopTimer结构体__CFRunLoopObserver结构体 RunLoop是什么 如果没有RunLoop,那么该线程在执行完自己的任务时就会退出;而我们在使用APP时,通常不会只执行完一个

吃橘子 980

手把手教你用手机号注册的Apple ID成功登录Developer账户(避坑指南)

本文详细解析了使用手机号注册的Apple ID登录开发者账户时常见的'不符合使用条件'报错问题,并提供了从添加可验证邮箱到设置主账号的四步解决方案。特别针对Apple Developer账户的验证机制,帮助开发者避免因邮箱缺失导致的登录失败,提升账户激活成功率。

z5a6b的博客 1219

一次次解析接口

今天分享一个免费解析接口,一天只有一次开屏广告或底部漂浮广告。默认线路为了避免蜘蛛大批量爬取浪费带宽 使用的其他网站解析!A 线路1080P 极速解析 无广告。

a1255887889的博客 485

滑模控制算法——基本原理(附MATLAB程序)

选择一个滑模面和是设计参数。是滑模面函数,通常我们希望系统状态在的面上滑动。我们以一个简单的二阶系统作为示例,其状态空间方程为:是系统矩阵是输入矩阵是输出矩阵假设我们的系统如下:滑模控制算法的核心在于设计合适的滑模面和控制律,并分析系统在滑模面上的稳定性。通过上述公式和步骤,可以有效地设计滑模控制器,以处理具有非线性和不确定性的动态系统。总的来说,滑模控制算法因其鲁棒性和灵活性在处理复杂控制问题中表现出色,但在实际应用中需要精心设计以克服抖振现象和其他潜在问题。BuxuABDC。

qq_35623594的博客 2万+

基于51单片机的简易计算器设计(原理图+仿真+源代码+参考论文)

本设计以51单片机为核心,矩阵键盘输入数字,通过1602显示屏显示计算结果,能够实现整数的加、减、乘、除四则运算,具备清除功能。

u012759563的博客 7694

iOS——解密RunLoop原理

当中),通过代码并不难发现其实只有当我们使用线程的方法主动get Runloop时才会在第一次创建该线程的Runloop,同时将它保存在全局的Dictionary中(线程和Runloop二者一一对应),默认情况下线程并不会创建Runloop(主线程的Runloop比较特殊,任何线程创建之前都会保证主线程已经存在Runloop),同时在线程结束的时候也会销毁对应的Runloop。Observer的作用是可以让外部监听RunLoop的运行状态,从而根据不同的时机,做一些操作。所以,我们就有了RunLoop。

yinshipin007的博客 658

模型剪枝实战 | Torch-Pruning 库在YOLOv8上的应用指南

本文详细介绍了如何使用Torch-Pruning库对YOLOv8模型进行结构化剪枝,以实现模型“瘦身”。通过DepGraph(依赖图)技术自动处理层间依赖,确保剪枝安全。文章提供了从环境搭建、核心原理理解到完整剪枝流程的实战指南,并强调了剪枝后的微调与性能评估,帮助开发者在保持精度的同时显著提升模型推理速度。

weixin_29046035的博客 503

runloop的source

以上是完整的 CFRunLoop 和 CFRunLoopMode 的结构体源码(太长了我的妈,用不着看完),下面我精简一下,把重要的留下,看如下代码(可以仔细看一下,加深印象): 上面是精简出来比较关键的 RunLoop 和RunLoopMode的结构体,从上面源码可以看出: 一个RunLoop对象有一个用来被唤醒的端口_wakeUpPort,一个当前运...

diaoju3333的博客 272

TC387 MCMCAN Message RAM配置详解:Tx Buffer与Rx FIFO地址布局

在嵌入式CAN/CAN FD通信系统中,缓冲区和FIFO是数据收发的核心机制,而AURIX TC3xx系列单片机将这一机制集成在了MCMCAN模块专用的Message RAM中。这是一块由CAN控制器独占的内存区域,开发人员需要按照寄存器规范手动划分Tx Buffer、Rx FIFO、过滤元素等子区间。合理的RAM布局能确保不同功能区域互不重叠、有序排列,避免数据覆盖与错帧。通过掌握TXBC、RXF0C等配置寄存器的字段含义,结合实用的地址布局表,可以显著降低多节点CAN通信的调试成本。本内容面向使用TC3

weixin_34226182的博客 221

《C语言小游戏之贪吃蛇程序代码》

//为广大编程游戏爱好者提供一个参考的游戏源代码#include&lt;stdio.h&gt;#include&lt;conio.h&gt;#include&lt;windows.h&gt;#include&lt;time.h&gt;#define framex 5#define framey 5#define wide 20#define high 20int i,j,a[2];//将光标移动到...

qq_42052821的博客 2万+

Objective-C的AutoreleasePool与Runloop的关联

注释AutoreleasePool的源码在runtime/NSObject.mm中可以找到。 相信很多人都知道,autorelease就是让对象延迟释放,很多人还更具体的以为是在出了作用域释放,也有的说是出了作用域后某个时间点,但是具体是在啥时候呢,也不清楚。 那具体如何呢,我们还是要到源码里才能一探究竟: 我们先新建一个项目,并跟踪一个对象的内存情况,我们可以看到如下截图内容,在o

zyx196的博客 782

opencv 双目摄像头拍照(分别左右镜头拍照)

准备一个usb线的双目同步摄像头: 运行环境是vc2015,opencv3.1。 将左右两个摄像头拍到的图片分别保存起来。 1)适用条件 双目同步摄像头(两个镜头,共用一个设备ID) 使用语言:C++ 、opencv库 摄像头 分辨率2560*960 或以上。 源程序: #include"stdafx.h" #include<iostream&g...

黎国溥 1万+

探究 Runloop (一)

探究 Runloop (一) 对于 iOS 开发者来说, Runloop 应该是大家都熟悉的,这篇文章只是本人在阅读源码时的一些理解。有错误请狠批!谢谢

weixin_34321977的博客 111

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

ios局域网联机——苹果官方源码之WiTap剖析(二)

这篇文章是"ios局域网联机——苹果官方源码之WiTap剖析"系列的第二部分,它和第一部分紧紧相连,因此阅读此文章的前提是你已经阅读了这个系列的第一部分。 打起精神继续战斗      好吧,让我们接着第一部分继续剖析这个TCPServer.m文件吧,上一部分中我们是讲到了TCPServerAcceptCallBack:这个回调方法,讲到了它的格式,它是一个符合CFSocketCallB

lfanys的专栏 488

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

fir.im Weekly - 进击的 Swift

最近 Swift 开源了,众开发者们欢呼雀跃。感谢开源,这是最好的时代。本期 fir.im Weekly 准备了一些关于 Swift 的“新鲜”干货分享,也包括一些优秀的 GitHub 源码、开发工具和技术文章等等。Swift 开源了美国西海岸时间,12月3日,Swift 开源了,可用于 iOS、OS X 和 Linux 平台。刚上 Github 短短 1 天,已经有近 1 万个 Star 。同时

FIRim的专栏 1311

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

AFNetworking2.0源码解析<一>

(via:bang's blog)   最近看AFNetworking2的源码,学习这个知名网络框架的实现,顺便梳理写下文章。AFNetworking的代码还在不断更新中,我看的是AFNetworking2.3.1。   本篇先看看AFURLConnectionOperation,AFURLConnectionOperation继承自NSOperation,是一个封装好的任务单元,在这里构

michaelKiven的博客 400

AFNetworking2.0源码解析<二>

续AFNetworking2.0源码解析 本篇我们继续来看看AFNetworking的下一个模块 — AFURLRequestSerialization。 AFURLRequestSerialization用于帮助构建NSURLRequest,主要做了两个事情: 1.构建普通请求:格式化请求参数,生成HTTP Header。 2.构建multipart请求。 分别看看它在这两点具

sunyazhou13的专栏 613

AFNetworking3.0源码解读(一)之 AFNetworkReachabilityManager

转载链接博主博客地址:点击打开链接 AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization AFNetworking 3.0 源码解读(五)

MinggeQingchun的博客 835
上一篇: RunLoop的学习总结
下一篇: GIT-删除远程服务端的文件
活在生活里
博客等级 码龄11年 94粉丝 82原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值