__CFRunLoopDoTimers 和 __CFRunLoopDoTimer 函数详解(借助AI)

2 阅读18分钟

借助AI辅助。

__CFRunLoopDoTimers 和 __CFRunLoopDoTimer 函数详解

__CFRunLoopDoTimers 函数概述

__CFRunLoopDoTimers 是 RunLoop 中负责批量处理 Timer 的函数。它遍历当前 Mode 中的所有 Timer,收集需要触发的 Timer(触发时间 <= 当前时间),然后依次调用 __CFRunLoopDoTimer 执行每个 Timer 的回调。

函数职责

  1. 收集待触发的 Timers: 遍历 Mode 中的所有 timers,筛选出到期的
  2. 批量执行: 依次调用 __CFRunLoopDoTimer 处理每个 timer
  3. 返回状态: 告知 RunLoop 是否处理了至少一个 timer

与上层的关系

__CFRunLoopRun (主循环)
  ↓
  ├─ 计算 limitTSR(最大允许延迟时间)
  ↓
__CFRunLoopDoTimers(rl, rlm, limitTSR)  ⭐ 本函数
  ↓
  ├─ 收集所有 fireTSR <= limitTSR 的 timers
  ├─ 遍历调用 ──→ __CFRunLoopDoTimer(rl, rlm, rlt)
  │                   ↓
  │                 执行 timer 回调
  │                   ↓
  │                 计算下次触发时间
  └─ 返回是否处理了 timer

【完整代码逐行注释】

// rl and rlm are locked on entry and exit
// 📝 锁状态约定:函数入口和出口时,rl 和 rlm 都必须处于加锁状态
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) {	/* DOES CALLOUT */
    // ⚠️ 重要标注:此函数会执行外部回调(通过 __CFRunLoopDoTimer)
    // 可能导致长时间阻塞、重入问题、死锁风险
    
    // 📊 记录性能追踪点:开始处理 Timers
    // 参数:事件类型、RunLoop、Mode、limitTSR(时间限制)、额外参数
    // 可用 Instruments 的 System Trace 查看
    cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_TIMERS | DBG_FUNC_START, rl, rlm, limitTSR, 0);
    
    // 标志位:记录是否至少处理了一个 timer
    Boolean timerHandled = false;
    
    // 用于存储收集到的待触发 timers 的数组
    // 初始为 NULL,只在有需要触发的 timer 时才创建
    CFMutableArrayRef timers = NULL;
    
    // ==================== 第一阶段:收集待触发的 Timers ====================
    
    // 遍历当前 Mode 中的所有 timers
    // rlm->_timers 是一个 CFArray,包含所有添加到此 mode 的 timers
    // 获取数组长度,如果 _timers 为 NULL 则 cnt = 0
    for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) {
        
        // 获取第 idx 个 timer
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
        
        // 🔍 三重过滤条件(必须全部满足):
        if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) {
            // 条件1: __CFIsValid(rlt)
            //   检查 timer 是否有效(未被 invalidate)
            //   timer 可能在之前的回调中被标记为无效
            
            // 条件2: !__CFRunLoopTimerIsFiring(rlt)
            //   检查 timer 是否正在执行回调
            //   防止重入:如果 timer 的回调函数中触发了 RunLoop 循环,
            //   不会重复调用该 timer(避免无限递归)
            
            // 🕐 检查 timer 是否到期
            // rlt->_fireTSR: timer 下次触发的时间戳(TSR = TimeStamp Register)
            // limitTSR: 本次处理的时间上限
            // 如果 _fireTSR <= limitTSR,说明 timer 应该触发了
            if (rlt->_fireTSR <= limitTSR) {
                // Timer 需要触发
                
                // 💡 延迟创建数组优化
                // 只在第一次发现需要触发的 timer 时才创建数组
                // 避免没有到期 timer 时的无谓内存分配
                if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
                
                // 📦 将 timer 添加到收集数组
                // CFArrayAppendValue 会自动 CFRetain(rlt)
                CFArrayAppendValue(timers, rlt);
            }
        }
    }

    // ==================== 第二阶段:执行收集到的 Timers ====================
    
    // 遍历收集到的待触发 timers
    // 如果 timers 为 NULL(没有到期的 timer),cnt = 0,不会进入循环
    for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
        
        // 获取第 idx 个待触发的 timer
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
        
        // ⚡ 调用 __CFRunLoopDoTimer 处理单个 timer
        // 这个函数会:
        // 1. 再次检查 timer 的有效性和触发条件
        // 2. 执行 timer 的回调函数
        // 3. 计算下次触发时间
        // 4. 重新调整 timer 在 mode 中的位置
        // 返回:是否成功执行了该 timer
        Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
        
        // 逻辑或运算:只要有一个 timer 被处理,timerHandled 就为 true
        // 等价于:if (did) timerHandled = true;
        timerHandled = timerHandled || did;
    }
    
    // 📉 释放 timers 数组
    // 如果创建了数组,需要释放(减少引用计数)
    // CFArrayAppendValue 时对每个 timer 进行了 CFRetain,
    // CFRelease 数组时会对每个元素调用 CFRelease
    if (timers) CFRelease(timers);

    // 📊 记录性能追踪点:完成 Timers 处理
    cf_trace(KDEBUG_EVENT_CFRL_IS_DOING_TIMERS | DBG_FUNC_END, rl, rlm, limitTSR, 0);
    
    // 返回是否至少处理了一个 timer
    // true: 执行了至少一个 timer
    // false: 没有执行任何 timer(没有到期的 timers 或所有 timers 都无效)
    return timerHandled;
}

【函数执行流程图】

                    __CFRunLoopDoTimers
                            ↓
                📊 开始性能追踪
                            ↓
                    初始化 timers = NULL
                            ↓
        ┌───────────────────────────────────────┐
        │  第一阶段:收集待触发的 Timers           │
        └───────────────────┬───────────────────┘
                            ↓
            遍历 rlm->_timers 中的每个 timer
                            ↓
            ┌───────────────┴───────────────┐
            │   Is Valid && !IsFiring?      │
            └───────────────┬───────────────┘
                            │
              ┌─────────────┴─────────────┐
              NO                         YES
              ↓                           ↓
           跳过此 timer          ┌────────┴────────┐
                                 │ fireTSR <= limit? │
                                 └────────┬────────┘
                                          │
                          ┌───────────────┴───────────────┐
                          NO                             YES
                          ↓                               ↓
                       跳过此 timer               添加到 timers 数组
                                                  (延迟创建数组)
                            ↓
        ┌───────────────────────────────────────┐
        │  第二阶段:执行收集到的 Timers           │
        └───────────────────┬───────────────────┘
                            ↓
              遍历 timers 数组中的每个 timer
                            ↓
            调用 __CFRunLoopDoTimer(rl, rlm, rlt)
                            ↓
                  timerHandled |= did
                            ↓
                    释放 timers 数组
                            ↓
                📊 结束性能追踪
                            ↓
                    return timerHandled

【关键设计要点】

1. 两阶段处理模式

阶段1: 收集(持有锁)          阶段2: 执行(会解锁)
    ↓                              ↓
遍历 _timers 数组          遍历收集的 timers 数组
    ↓                              ↓
检查条件并收集            调用 __CFRunLoopDoTimer
    ↓                              ↓
快速,不执行回调          慢速,执行用户回调

为什么要分两阶段?

原因说明
安全性在执行回调期间可能修改 rlm->_timers,如果边遍历边执行会导致数组变化
一致性确保所有判断基于同一时刻的状态
可预测性避免回调中添加/删除 timer 影响当前循环

2. 延迟创建数组优化

if (!timers) timers = CFArrayCreateMutable(...);  // 只在需要时创建

场景分析:

情况1: 没有到期的 timer
  - timers 保持为 NULL
  - 不分配内存
  - 第二阶段循环不执行(cnt = 0)

情况2: 有到期的 timer
  - 第一次遇到时创建数组
  - 添加所有到期的 timers
  - 第二阶段执行

3. limitTSR 的作用

if (rlt->_fireTSR <= limitTSR)

limitTSR 的计算(在 __CFRunLoopRun 中):

// 通常是当前时间加上一个容忍度
uint64_t currentTSR = mach_absolute_time();
uint64_t limitTSR = currentTSR + toleranceTSR;

// 作用:
// - 允许稍微延迟的 timer 也在本次循环中执行
// - 减少唤醒次数,提高电池效率
// - 实现 timer coalescing(合并触发)

示例:

当前时间: 1000
tolerance: 10
limitTSR: 1010

Timer A: _fireTSR = 995   执行(已过期)
Timer B: _fireTSR = 1005  执行(在容忍度内)
Timer C: _fireTSR = 1015  跳过(超出容忍度)

4. 防止重入的机制

if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt))

重入场景:

// Timer 的回调函数
void timerCallback(CFRunLoopTimerRef timer, void *info) {
    // 在回调中再次运行 RunLoop(重入)
    CFRunLoopRun();
    
    // 如果没有 IsFiring 检查,这个 timer 可能被再次触发
    // 导致无限递归
}

// 防护机制:
// 1. __CFRunLoopDoTimer 在执行前设置 IsFiring = true
// 2. __CFRunLoopDoTimers 检查 !IsFiring,跳过正在执行的 timer
// 3. __CFRunLoopDoTimer 执行完后设置 IsFiring = false

__CFRunLoopDoTimer 函数概述

函数概述

__CFRunLoopDoTimer 是 RunLoop 中负责执行单个 Timer 的核心函数。它负责:

  1. 检查 timer 是否应该触发
  2. 执行 timer 的回调函数
  3. 计算下次触发时间
  4. 重新调整 timer 在各个 mode 中的位置
  5. 处理一次性 timer 的失效

这是一个复杂的函数,涉及精细的锁管理、时间计算、多 mode 协调等。

完整代码逐行注释

// mode and rl are locked on entry and exit
// 📝 锁状态约定:函数入口和出口时,rl 和 rlm 都必须处于加锁状态
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) {	/* DOES CALLOUT */
    // ⚠️ 重要标注:此函数会执行外部回调,可能导致长时间阻塞、重入、死锁
    
    // 📊 记录性能追踪点:开始触发 Timer
    cf_trace(KDEBUG_EVENT_CFRL_TIMERS_FIRING | DBG_FUNC_START, rl, rlm, rlt, 0);
    
    // 标志位:记录 timer 是否被成功处理
    Boolean timerHandled = false;
    
    // 保存 timer 本次触发的时间戳
    // 用于后续计算下次触发时间
    uint64_t oldFireTSR = 0;

    /* Fire a timer */
    // 🔥 触发一个 timer
    
    // ==================== 准备阶段:增加引用计数并加锁 ====================
    
    // 📌 增加 timer 的引用计数
    // 防止在执行期间 timer 被其他线程释放
    // 必须在最后配对 CFRelease(rlt)
    CFRetain(rlt);
    
    // 🔒 锁定 timer 对象(细粒度锁)
    __CFRunLoopTimerLock(rlt);

    // ==================== 条件检查:四重验证 ====================
    
    // 🔍 四重条件检查(必须全部满足才执行回调)
    if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
        
        // 条件1: __CFIsValid(rlt)
        //   检查 timer 是否有效(未被 invalidate)
        //   在收集和现在执行之间,timer 可能被其他线程 invalidate
        
        // 条件2: rlt->_fireTSR <= mach_absolute_time()
        //   再次检查 timer 是否确实到期
        //   使用当前精确时间,而不是 __CFRunLoopDoTimers 中的 limitTSR
        //   这是最后一道防线,确保不会过早触发
        
        // 条件3: !__CFRunLoopTimerIsFiring(rlt)
        //   检查 timer 是否正在执行回调
        //   防止重入(回调中再次运行 RunLoop)
        
        // 条件4: rlt->_runLoop == rl
        //   检查 timer 是否仍然属于这个 RunLoop
        //   timer 可能在执行前被移动到其他 RunLoop
        
        // ==================== 子阶段1:准备回调上下文 ====================
        
        // 用于存储 timer 的上下文信息
        void *context_info = NULL;
        // 上下文信息的释放函数(可选)
        void (*context_release)(const void *) = NULL;
        
        // 如果 timer 的上下文有 retain 函数,需要持有上下文
        if (rlt->_context.retain) {
            // 调用用户提供的 retain 函数
            // 增加上下文对象的引用计数
            // 防止回调执行期间上下文被释放
            context_info = (void *)rlt->_context.retain(rlt->_context.info);
            
            // 保存 release 函数,稍后需要调用
            context_release = rlt->_context.release;
        } else {
            // 没有 retain 函数,直接使用原始指针
            // ⚠️ 用户需要保证 info 在回调期间有效
            context_info = rlt->_context.info;
        }
        
        // 判断 timer 是否是一次性的(non-repeating)
        // interval = 0.0 表示不重复
        Boolean doInvalidate = (0.0 == rlt->_interval);
        
        // 🚩 设置"正在执行"标志位
        // 防止重入(与 __CFRunLoopDoTimers 中的检查配合)
        __CFRunLoopTimerSetFiring(rlt);
        
        // ==================== 子阶段2:重置 Mode 的 Timer 截止时间 ====================
        
        // Just in case the next timer has exactly the same deadlines as this one, 
        // we reset these values so that the arm next timer code can correctly 
        // find the next timer in the list and arm the underlying timer.
        // 🕐 重置 mode 的 timer 截止时间为最大值
        // 原因:如果下一个 timer 与当前 timer 的截止时间完全相同,
        //       重置这些值可以让 __CFArmNextTimerInMode 正确找到列表中的下一个 timer
        //       并设置底层的系统 timer
        rlm->_timerSoftDeadline = UINT64_MAX;
        rlm->_timerHardDeadline = UINT64_MAX;
        
        // 🔓 解锁 timer
        // 准备执行回调,需要在回调前解锁(防止死锁)
        __CFRunLoopTimerUnlock(rlt);
        
        // ==================== 子阶段3:保存旧的触发时间 ====================
        
        // 🔒 锁定 RunLoop 的 timer 时间戳锁(细粒度锁)
        // 这是一个专门用于保护 timer 时间相关字段的锁
        __CFLock(&rl->_timerTSRLock);
        
        // 保存 timer 本次触发的时间戳
        // 用于后续计算下次触发时间
        oldFireTSR = rlt->_fireTSR;
        
        // 🔓 解锁 timer 时间戳锁
        __CFUnlock(&rl->_timerTSRLock);

        // ==================== 子阶段4:准备下一个 Timer ====================
        
        // ⏰ 设置 mode 中下一个要触发的 timer
        // 这个函数会:
        // 1. 遍历 mode 中的所有 timers
        // 2. 找到最早需要触发的 timer
        // 3. 设置系统的底层 timer(mk_timer 或 dispatch_source)
        // 4. 更新 mode 的 _timerSoftDeadline 和 _timerHardDeadline
        __CFArmNextTimerInMode(rlm, rl);

        // ==================== 子阶段5:解锁准备执行回调 ====================
        
        // 🔓 解锁 RunLoop Mode
        __CFRunLoopModeUnlock(rlm);
        
        // 🔓 解锁 RunLoop
        __CFRunLoopUnlock(rl);
        
        // ⚠️ 为什么要解锁?
        // 1. 防止死锁:回调函数中可能调用 RunLoop API
        // 2. 避免长时间持锁:timer 回调可能执行耗时操作
        // 3. 提高并发性:允许其他线程在回调执行期间访问 RunLoop
        
        // ==================== 子阶段6:执行回调函数 ====================
        
        // 提取回调函数指针
        // 类型:void (*)(CFRunLoopTimerRef timer, void *info)
        CFRunLoopTimerCallBack callout = rlt->_callout;
        
        // 🔄 开始自动释放池
        // 管理回调中创建的临时对象
        CFRUNLOOP_ARP_BEGIN(NULL)
        
        // 📊 记录性能追踪点:开始调用 Timer 回调
        cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_TIMER | DBG_FUNC_START, callout, rlt, context_info, 0);
        
        // ⚡ 执行 timer 的回调函数
        // 这是整个函数的核心目的!
        // 宏展开后通常是:
        //   if (callout) {
        //       callout(rlt, context_info);
        //   }
        // 
        // ⚠️ 回调中可能发生的事情:
        // - UI 更新
        // - 网络请求
        // - 数据处理
        // - 调用 CFRunLoopTimerSetNextFireDate 修改下次触发时间
        // - 添加/移除其他 timers
        // - 重入 RunLoop(CFRunLoopRun)
        // - 长时间阻塞(如果回调写得不好)
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(callout, rlt, context_info);
        
        // 📊 记录性能追踪点:结束调用 Timer 回调
        cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_TIMER | DBG_FUNC_END, callout, rlt, context_info, 0);
        
        // 🔄 结束自动释放池
        // 释放回调中创建的所有 autorelease 对象
        CFRUNLOOP_ARP_END()

        // ==================== 子阶段7:回调后处理 ====================
        
        // 🔒 检查进程是否被 fork
        // 如果在回调执行期间进程被 fork,需要重新初始化
        CHECK_FOR_FORK();
        
        // 如果是一次性 timer,需要 invalidate
        if (doInvalidate) {
            // ❌ 使 timer 失效
            // 会从 RunLoop 中移除,并标记为无效
            // 注意:这个函数本身也会执行 callout(通知回调)
            CFRunLoopTimerInvalidate(rlt);      /* DOES CALLOUT */
        }
        
        // 如果有上下文释放函数,调用它
        if (context_release) {
            // 减少上下文对象的引用计数
            // 对应前面的 context_info = retain(...)
            context_release(context_info);
        }
        
        // ==================== 子阶段8:重新加锁 ====================
        
        // 🔒 重新锁定 RunLoop
        __CFRunLoopLock(rl);
        
        // 🔒 重新锁定 RunLoop Mode
        __CFRunLoopModeLock(rlm);
        
        // 🔒 重新锁定 timer
        __CFRunLoopTimerLock(rlt);
        
        // ✅ 标记:成功处理了 timer
        timerHandled = true;
        
        // 🚩 清除"正在执行"标志位
        // 允许此 timer 在下次到期时再次被触发
        __CFRunLoopTimerUnsetFiring(rlt);
    }
    
    // ==================== 计算下次触发时间 ====================
    
    // 如果 timer 有效且本次被处理了
    if (__CFIsValid(rlt) && timerHandled) {
        
        /* This is just a little bit tricky: we want to support calling
         * CFRunLoopTimerSetNextFireDate() from within the callout and
         * honor that new time here if it is a later date, otherwise
         * it is completely ignored. */
        // 💡 这里有个微妙的设计:
        // 我们希望支持在回调中调用 CFRunLoopTimerSetNextFireDate()
        // 如果在回调中设置了一个更晚的时间,我们会采用它
        // 否则,按照 interval 计算的时间
        
        // 比较旧的触发时间和当前的触发时间
        if (oldFireTSR < rlt->_fireTSR) {
            
            /* Next fire TSR was set, and set to a date after the previous
            * fire date, so we honor it. */
            // 下次触发时间已经被设置(在回调中调用了 SetNextFireDate)
            // 且设置的时间比之前的触发时间晚,所以我们采用它
            
            // 🔓 解锁 timer
            __CFRunLoopTimerUnlock(rlt);
            
            // The timer was adjusted and repositioned, during the
            // callout, but if it was still the min timer, it was
            // skipped because it was firing.  Need to redo the
            // min timer calculation in case rlt should now be that
            // timer instead of whatever was chosen.
            // timer 在回调期间被调整和重新定位了,
            // 但如果它仍然是最早的 timer,之前因为正在触发而被跳过了。
            // 需要重新计算最早的 timer,以防 rlt 现在应该是那个 timer。
            __CFArmNextTimerInMode(rlm, rl);
            
        } else {
            // 回调中没有修改触发时间,或者修改的时间更早(被忽略)
            // 按照 interval 计算下次触发时间
            
            // 下次触发时间(TSR 格式)
            uint64_t nextFireTSR = 0LL;
            // interval 对应的 TSR 值
            uint64_t intervalTSR = 0LL;
            
            // ==================== 子步骤1:计算 interval 的 TSR 值 ====================
            
            // 如果 interval <= 0,timer 不重复
            if (rlt->_interval <= 0.0) {
                // intervalTSR 保持为 0
            } else if (TIMER_INTERVAL_LIMIT < rlt->_interval) {
                // interval 太大(超过限制),使用最大允许值
                // TIMER_INTERVAL_LIMIT 通常是几年(防止溢出)
                intervalTSR = __CFTimeIntervalToTSR(TIMER_INTERVAL_LIMIT);
            } else {
                // 正常情况:将秒转换为 TSR
                intervalTSR = __CFTimeIntervalToTSR(rlt->_interval);
            }
            
            // ==================== 子步骤2:计算下次触发时间 ====================
            
            // 检查是否会溢出
            if (LLONG_MAX - intervalTSR <= oldFireTSR) {
                // 加上 interval 会导致溢出,使用最大值
                nextFireTSR = LLONG_MAX;
            } else {
                // 正常情况
                
                if (intervalTSR == 0) {
                    // 15304159: Make sure we don't accidentally loop forever here
                    // 🐛 防止无限循环
                    // 如果 interval 为 0 但 timer 是 repeating 的,会导致死循环
                    CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat");
                    HALT;
                }
                
                // 获取当前时间
                uint64_t currentTSR = mach_absolute_time();
                
                // 从旧的触发时间开始
                nextFireTSR = oldFireTSR;
                
                // ⏰ 循环跳过已经过去的触发点
                // 如果系统繁忙或休眠,可能错过了多个触发点
                // 需要找到第一个未来的触发时间
                //
                // 示例:
                // oldFireTSR = 1000, interval = 10, currentTSR = 1035
                // 迭代1: nextFireTSR = 1010 <= 1035, 继续
                // 迭代2: nextFireTSR = 1020 <= 1035, 继续
                // 迭代3: nextFireTSR = 1030 <= 1035, 继续
                // 迭代4: nextFireTSR = 1040 > 1035, 退出
                // 最终: nextFireTSR = 1040
                while (nextFireTSR <= currentTSR) {
                    nextFireTSR += intervalTSR;
                }
            }
            
            // ==================== 子步骤3:更新 timer 在所有 mode 中的位置 ====================
            
            // 获取 timer 所属的 RunLoop
            // 可能为 NULL(如果 timer 已经被从 RunLoop 移除)
            CFRunLoopRef rlt_rl = rlt->_runLoop;
            
            if (rlt_rl) {
                // timer 仍然在 RunLoop 中
                
                // 📌 增加 RunLoop 的引用计数
                // 防止在后续操作期间 RunLoop 被释放
                CFRetain(rlt_rl);
                
                // 获取 timer 关联的所有 modes 的数量
                CFIndex cnt = CFSetGetCount(rlt->_rlModes);
                
                // 📦 在栈上分配数组(如果数量合理)
                // 用于存储所有 mode 名称
                STACK_BUFFER_DECL(CFTypeRef, modes, cnt);
                
                // 获取所有 mode 名称(CFStringRef)
                CFSetGetValues(rlt->_rlModes, (const void **)modes);
                
                // To avoid A->B, B->A lock ordering issues when coming up
                // towards the run loop from a source, the timer has to be
                // unlocked, which means we have to protect from object
                // invalidation, although that's somewhat expensive.
                // 💡 锁顺序问题:
                // 为了避免 A->B, B->A 的死锁问题(当从 source 向上访问 run loop 时),
                // timer 必须被解锁,这意味着我们必须防止对象失效,
                // 尽管这有点昂贵。
                
                // 📌 对所有 mode 名称增加引用计数
                // 防止在解锁期间 mode 被释放
                for (CFIndex idx = 0; idx < cnt; idx++) {
                    CFRetain(modes[idx]);
                }
                
                // 🔓 解锁 timer
                // 接下来要锁定多个 modes,需要先解锁 timer 避免死锁
                __CFRunLoopTimerUnlock(rlt);
                
                // 🔍 获取所有 mode 对象并锁定
                for (CFIndex idx = 0; idx < cnt; idx++) {
                    // mode 的名称(CFStringRef)
                    CFStringRef name = (CFStringRef)modes[idx];
                    
                    // 根据名称获取 mode 对象
                    // false 表示如果 mode 不存在,不创建
                    modes[idx] = (CFTypeRef)__CFRunLoopCopyMode(rlt_rl, name, false);
                    
                    // 如果 mode 存在,锁定它
                    if (modes[idx]) {
                        __CFRunLoopModeLock((CFRunLoopModeRef)modes[idx]);
                    }
                    
                    // 释放 mode 名称
                    CFRelease(name);
                }
                
                // 🔒 锁定 RunLoop 的 timer 时间戳锁
                __CFLock(&rl->_timerTSRLock);
                
                // 🕐 更新 timer 的触发时间
                rlt->_fireTSR = nextFireTSR;
                
                // 📅 更新 timer 的下次触发日期(CFAbsoluteTime 格式)
                // 用于外部查询(CFRunLoopTimerGetNextFireDate)
                rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
                
                // 🔄 在所有 mode 中重新定位 timer
                // 因为触发时间改变了,timer 在排序数组中的位置可能改变
                for (CFIndex idx = 0; idx < cnt; idx++) {
                    CFRunLoopModeRef rlm = (CFRunLoopModeRef)modes[idx];
                    if (rlm) {
                        // 重新定位 timer 在 mode 的 _timers 数组中的位置
                        // true 表示需要重新计算 mode 的下一个触发时间
                        __CFRepositionTimerInMode(rlm, rlt, true);
                    }
                }
                
                // 🔓 解锁 timer 时间戳锁
                __CFUnlock(&rl->_timerTSRLock);
                
                // 🔓 逆序解锁所有 modes(与加锁顺序相反)
                // reverse index here so we unlock in the right order
                for (CFIndex idx = cnt - 1; idx >= 0; idx--) {
                    if (modes[idx] != NULL) {
                        // 解锁 mode
                        __CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]);
                        // 释放 mode 对象
                        CFRelease((CFRunLoopModeRef)modes[idx]);
                    }
		}
                
                // 📉 释放 RunLoop
                CFRelease(rlt_rl);
            } else {
                // timer 不在 RunLoop 中(已被移除)
                // 只更新时间,不需要重新定位
                
                // 🔓 解锁 timer
                __CFRunLoopTimerUnlock(rlt);
                
                // 🔒 锁定 timer 时间戳锁
                __CFLock(&rl->_timerTSRLock);
                
                // 🕐 更新触发时间
                rlt->_fireTSR = nextFireTSR;
                
                // 📅 更新下次触发日期
                rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
                
                // 🔓 解锁 timer 时间戳锁
                __CFUnlock(&rl->_timerTSRLock);
            }
        }
    } else {
        // timer 无效或未被处理
        
        // 🔓 解锁 timer
        __CFRunLoopTimerUnlock(rlt);
    }
    
    // 📉 释放 timer 的引用计数
    // 对应开头的 CFRetain(rlt)
    CFRelease(rlt);
    
    // 📊 记录性能追踪点:完成 Timer 触发
    cf_trace(KDEBUG_EVENT_CFRL_TIMERS_FIRING | DBG_FUNC_END, rl, rlm, rlt, 0);
    
    // 返回是否成功处理了 timer
    return timerHandled;
}

【函数执行流程图】

                    __CFRunLoopDoTimer
                            ↓
                📊 开始性能追踪
                            ↓
                    📌 CFRetain(rlt)
                            ↓
                    🔒 Lock timer
                            ↓
        ┌───────────────────────────────────────────┐
        │  四重条件检查                              │
        │  1. IsValid?                              │
        │  2. _fireTSR <= now?                      │
        │  3. !IsFiring?                            │
        │  4. _runLoop == rl?                       │
        └───────────────────┬───────────────────────┘
                            │
              ┌─────────────┴─────────────┐
              NO                         YES
              ↓                           ↓
         🔓 Unlock timer         准备回调上下文
              ↓                  (retain context_info)
         📉 CFRelease(rlt)              ↓
              ↓                  doInvalidate = (interval == 0)
         📊 结束追踪                    ↓
              ↓                  🚩 SetFiring = true
         return false                   ↓
                              重置 mode 的 deadline
                                        ↓
                              🔓 Unlock timer
                                        ↓
                              保存 oldFireTSR
                                        ↓
                              ⏰ ArmNextTimerInMode
                                        ↓
                        🔓 Unlock mode & RunLoop
                                        ↓
                ┌───────────────────────────────────┐
                │  执行回调                          │
                │  🔄 AutoRelease Pool Begin        │
                │  📊 开始追踪                      │
                │  ⚡ callout(rlt, context_info)   │
                │  📊 结束追踪                      │
                │  🔄 AutoRelease Pool End          │
                └───────────────┬───────────────────┘
                                ↓
                        🔒 CHECK_FOR_FORK
                                ↓
                        if (doInvalidate)
                                ↓
                        CFRunLoopTimerInvalidateif (context_release)
                                ↓
                        context_release(context_info)
                                ↓
                🔒 Lock RunLoop & mode & timer
                                ↓
                        timerHandled = true
                                ↓
                        🚩 SetFiring = false
                                ↓
        ┌───────────────────────────────────────────┐
        │  计算下次触发时间                          │
        └───────────────────┬───────────────────────┘
                            │
              ┌─────────────┴─────────────┐
              │  oldFireTSR < _fireTSR?   │
              │  (回调中修改了时间?)      │
              └─────────────┬─────────────┘
                            │
              ┌─────────────┴─────────────┐
             YES                          NO
              ↓                            ↓
         采用新时间              按 interval 计算
              ↓                            ↓
     __CFArmNextTimerInMode      计算 nextFireTSR
                                          ↓
                                  while (nextFireTSR <= now)
                                      nextFireTSR += interval
                                          ↓
                                  更新所有 modes 中的位置
                                          ↓
                                  🔒 锁定所有 modes
                                          ↓
                                  __CFRepositionTimerInMode
                                          ↓
                                  🔓 逆序解锁 modes
                                          ↓
                                  📉 CFRelease(rlt)
                                          ↓
                                  📊 结束追踪
                                          ↓
                                  return timerHandled

【关键设计要点】

1. 四重条件检查

if (__CFIsValid(rlt) && 
    rlt->_fireTSR <= mach_absolute_time() && 
    !__CFRunLoopTimerIsFiring(rlt) && 
    rlt->_runLoop == rl)
条件目的防止的问题
IsValid确认未 invalidate访问已释放的对象
_fireTSR <= now确认确实到期过早触发
!IsFiring确认未在执行重入、无限递归
_runLoop == rl确认属于此 RunLoop在错误的 RunLoop 中触发

2. 支持回调中修改触发时间

if (oldFireTSR < rlt->_fireTSR) {
    // 回调中调用了 CFRunLoopTimerSetNextFireDate,设置了更晚的时间
    // 采用新时间
} else {
    // 按 interval 计算
}

使用场景:

void timerCallback(CFRunLoopTimerRef timer, void *info) {
    // 动态调整下次触发时间
    if ([self shouldDelayNextFire]) {
        CFRunLoopTimerSetNextFireDate(timer, CFAbsoluteTimeGetCurrent() + 10.0);
    }
}

3. 跳过错过的触发点

while (nextFireTSR <= currentTSR) {
    nextFireTSR += intervalTSR;
}

示例:

Timer 每 1 秒触发一次
上次触发: 10:00:00
系统休眠 5 秒
当前时间: 10:00:05

计算过程:
nextFireTSR = 10:00:01 (错过了)
nextFireTSR = 10:00:02 (错过了)
nextFireTSR = 10:00:03 (错过了)
nextFireTSR = 10:00:04 (错过了)
nextFireTSR = 10:00:05 (错过了)
nextFireTSR = 10:00:06 ✅ 下次触发

结果:不会补发 5 次,直接跳到下一次未来的触发点

4. 多 Mode 协调的锁管理

// 避免死锁的锁顺序:
1. 解锁 timer
2. 锁定所有 modes(按索引顺序)
3. 锁定 _timerTSRLock
4. 更新时间
5. 在所有 modes 中重新定位
6. 解锁 _timerTSRLock
7. 逆序解锁所有 modes(与加锁顺序相反)

为什么要逆序解锁?

  • 遵循 LIFO(后进先出)原则
  • 与加锁顺序相反,减少死锁风险
  • 最后锁定的资源最先释放

5. 一次性 Timer 的处理

Boolean doInvalidate = (0.0 == rlt->_interval);
if (doInvalidate) {
    CFRunLoopTimerInvalidate(rlt);
}

生命周期:

创建: interval = 0
  ↓
添加到 RunLoop
  ↓
触发: __CFRunLoopDoTimer
  ↓
执行回调
  ↓
CFRunLoopTimerInvalidate  <- 自动失效
  ↓
从 RunLoop 移除
  ↓
销毁

【性能优化技巧】

1. 使用合理的 tolerance

// ❌ 不好:tolerance = 0(严格触发)
CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(NULL, fireDate, 1.0, 0, 0, ^{
    // 每次都要精确唤醒,耗电
});

// ✅ 好:设置合理的 tolerance
CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(NULL, fireDate, 1.0, 0, 0, ^{
    // 处理
});
CFRunLoopTimerSetTolerance(timer, 0.1);  // 100ms 容忍度
// 允许系统合并唤醒,省电

2. 避免在回调中做耗时操作

// ❌ 阻塞主线程
void timerCallback(CFRunLoopTimerRef timer, void *info) {
    sleep(1);  // 阻塞 1 秒
    // 影响其他 timers 和事件处理
}

// ✅ 异步处理
void timerCallback(CFRunLoopTimerRef timer, void *info) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 耗时操作
    });
}

3. 动态调整触发频率

void timerCallback(CFRunLoopTimerRef timer, void *info) {
    if ([self isUserActive]) {
        // 用户活跃:频繁更新
        CFRunLoopTimerSetNextFireDate(timer, CFAbsoluteTimeGetCurrent() + 0.1);
    } else {
        // 用户不活跃:降低频率
        CFRunLoopTimerSetNextFireDate(timer, CFAbsoluteTimeGetCurrent() + 1.0);
    }
}

【总结】

这两个函数体现了 RunLoop Timer 机制的精妙设计:

__CFRunLoopDoTimers(批量处理)

  • 两阶段处理: 收集 + 执行,保证安全性和一致性
  • 延迟创建优化: 没有到期 timer 时不分配内存
  • 防重入机制: IsFiring 标志位防止无限递归
  • 时间容忍度: limitTSR 实现 timer coalescing

__CFRunLoopDoTimer(单个处理)

  • 四重条件检查: 确保安全执行
  • 精细的锁管理: 回调前解锁,多 mode 协调
  • 灵活的时间计算: 支持回调中修改触发时间
  • 智能跳过: 不补发错过的触发点
  • 多 mode 同步: 在所有关联的 modes 中更新 timer 位置

这些设计使得 RunLoop Timer 既高效又安全,是 iOS/macOS 定时任务的基础设施。