__CFRunLoopDoSource1函数详解(借助AI)

4 阅读10分钟

借助AI辅助。

__CFRunLoopDoSource1 函数详解

函数概述

__CFRunLoopDoSource1 是 RunLoop 中负责处理 Source1(基于 Mach Port 的事件源)的核心函数。与 Source0 不同,Source1 是基于内核端口的事件源,可以自动唤醒 RunLoop,常用于进程间通信(IPC)、系统事件处理等场景。

函数职责

  1. 检查 Source1 的有效性: 确认 source 未被 invalidate
  2. 清除信号标记: 重置 source 的待处理状态
  3. 执行回调函数: 调用用户提供的 perform 回调,传递 Mach 消息
  4. 返回处理状态: 告知 RunLoop 是否成功处理了 source

Source1 的特点

特性Source1Source0
基础基于 Mach Port非基于端口
触发方式内核端口消息自动触发手动 signal + wakeup
唤醒 RunLoop自动唤醒需要手动唤醒
回调参数Mach 消息内容用户自定义 info
使用场景IPC、系统事件、端口通信触摸事件、自定义事件
性能涉及内核调用(较慢)用户态(较快)

与上层的关系

__CFRunLoopRun (主循环)
  ↓
__CFRunLoopServiceMachPort(...) 
  ↓ (收到 Mach 消息)
找到对应的 Source1
  ↓
__CFRunLoopDoSource1(rl, rlm, rls, msg, size, reply)  ⭐ 本函数
  ↓
执行 perform 回调(处理 Mach 消息)

【完整代码逐行注释】

// msg, size and reply are unused on Windows
// 💡 Windows 平台说明:
// msg(消息)、size(大小)和 reply(回复)参数在 Windows 上未使用
// 因为 Windows 没有 Mach Port 机制,使用其他 IPC 机制

// ==================== 平台相关的函数签名 ====================

#if TARGET_OS_MAC
// 🍎 macOS/iOS 平台的函数声明
// 包含 Mach 消息相关的参数
static Boolean __CFRunLoopDoSource1(
    CFRunLoopRef rl,              // RunLoop 对象
    CFRunLoopModeRef rlm,         // RunLoop Mode
    CFRunLoopSourceRef rls,       // Source1 对象
    mach_msg_header_t *msg,       // Mach 消息头(输入)
    CFIndex size,                 // 消息大小
    mach_msg_header_t **reply     // 回复消息(输出,可选)
) __attribute__((noinline));
// __attribute__((noinline)): 防止编译器内联优化
// 原因:便于调试、性能分析、保持调用栈可读性

// macOS/iOS 平台的函数实现
static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls, mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply)
#else
// 🪟 Windows 平台的函数声明和实现
// 不需要 Mach 消息参数
static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls)
#endif

{
    
    // ==================== 初始化和 Fork 检查 ====================
    
    // 🔒 检查进程是否被 fork
    // 如果在 fork 后的子进程中,需要重新初始化 RunLoop 的锁和状态
    // 防止继承父进程的锁状态导致死锁
    CHECK_FOR_FORK();
    
    // 标志位:记录 source 是否被成功处理
    // true: 执行了回调函数
    // false: source 无效或处理失败
    Boolean sourceHandled = false;

    /* Fire a version 1 source */
    // 🔥 触发一个版本 1 的 source(基于 Mach Port)
    
    // ==================== 准备阶段:增加引用计数 ====================
    
    // 📌 增加 source 的引用计数
    // 防止在执行期间 source 被其他线程释放
    // 必须在最后配对 CFRelease(rls)
    CFRetain(rls);
    
    // ==================== 解锁阶段:准备执行回调 ====================
    
    // 🔓 解锁 RunLoop Mode
    __CFRunLoopModeUnlock(rlm);
    
    // 🔓 解锁 RunLoop
    __CFRunLoopUnlock(rl);
    
    // ⚠️ 为什么要解锁?
    // 1. 防止死锁:回调函数中可能调用 RunLoop API
    // 2. 避免长时间持锁:source 的回调可能执行耗时操作
    // 3. 提高并发性:允许其他线程在回调执行期间访问 RunLoop
    
    // 🔒 锁定 source 对象(细粒度锁)
    // 保护 source 的状态检查和修改
    __CFRunLoopSourceLock(rls);
    
    // ==================== 条件检查:验证 Source 有效性 ====================
    
    // 🔍 检查 source 是否有效(未被 invalidate)
    if (__CFIsValid(rls)) {
        // Source 有效,可以执行回调
        
        // 🚩 清除 source 的信号标记(signaled flag)
        // Source1 虽然是基于端口的,但也有 signaled 标记
        // 清除标记表示此事件已被处理
        // 如果后续有新的端口消息到达,会重新设置标记
        __CFRunLoopSourceUnsetSignaled(rls);
        
        // 🔓 解锁 source
        // 在执行回调前解锁,防止回调中操作 source 时死锁
        __CFRunLoopSourceUnlock(rls);
        
        // 📊 记录调试信息(如果启用了调试模式)
        // 输出 source 的详细信息,用于问题诊断
        __CFRunLoopDebugInfoForRunLoopSource(rls);
        
        // ==================== 准备回调参数 ====================
        
        // 提取回调函数指针
        // version1.perform 的类型:
        // void (*)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info)
        // 或在 Windows 上:
        // void (*)(void *info)
        void *perform = rls->_context.version1.perform;
        
        // 提取用户上下文信息(创建 source 时传入的)
        void *info = rls->_context.version1.info;
        
        // ==================== 执行回调函数 ====================
        
        // 🔄 开始自动释放池(ARP = AutoRelease Pool)
        // 管理回调中创建的临时对象
        // 在 ObjC 运行时环境中,等价于 @autoreleasepool {
        CFRUNLOOP_ARP_BEGIN(rl)
        
        // 📊 记录性能追踪点:开始调用 Source1 回调
        // 参数:事件类型、RunLoop、Mode、perform 函数指针、info
        // 可用 Instruments 的 System Trace 查看
        cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE1 | DBG_FUNC_START, rl, rlm, perform, info);
        
        // ⚡ 执行 Source1 的回调函数
        // 这是整个函数的核心目的!
        // 
        // macOS/iOS 平台的宏展开(简化):
        //   if (perform) {
        //       ((void (*)(void *, CFIndex, CFAllocatorRef, void *))perform)
        //           (msg, size, kCFAllocatorSystemDefault, info);
        //   }
        // 
        // Windows 平台的宏展开(简化):
        //   if (perform) {
        //       ((void (*)(void *))perform)(info);
        //   }
        // 
        // ⚠️ 回调中可能发生的事情:
        // - 处理进程间通信(IPC)消息
        // - 处理系统事件(如 CFMachPort、CFMessagePort)
        // - 解析 Mach 消息内容
        // - 构造并发送回复消息(通过 reply 参数)
        // - 更新 UI(如果是主线程)
        // - 再次操作 RunLoop(添加/移除 source、timer 等)
        // - 长时间阻塞(如果回调写得不好)
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(perform,
#if TARGET_OS_MAC
            // macOS/iOS: 传递 Mach 消息相关参数
            msg,    // 消息头指针
            size,   // 消息大小
            reply,  // 回复消息指针的指针(输出参数)
#endif
            info    // 用户上下文
        );
        
        // 📊 记录性能追踪点:结束调用 Source1 回调
        // 可计算回调执行耗时 = end - start
        cf_trace(KDEBUG_EVENT_CFRL_IS_CALLING_SOURCE1 | DBG_FUNC_END, rl, rlm, perform, info);
        
        // 🔄 结束自动释放池
        // 释放回调中创建的所有 autorelease 对象
        // 等价于:} // @autoreleasepool
        CFRUNLOOP_ARP_END()
        
        // 🔒 再次检查进程是否被 fork
        // 如果在回调执行期间进程被 fork,需要重新初始化
        // 这个检查放在回调后很重要,因为 fork 可能发生在任何时候
        CHECK_FOR_FORK();
        
        // ✅ 标记:成功处理了 source
        sourceHandled = true;
        
    } else {
        // Source 已无效(被 invalidate)
        // 不执行回调,直接解锁并返回
        
        // 📝 记录日志(如果启用了 RunLoop 日志)
        // 输出:当前 RunLoop、程序名、无效的 source 指针
        if (_LogCFRunLoop) { 
            CFLog(kCFLogLevelDebug, 
                  CFSTR("%p (%s) __CFRunLoopDoSource1 rls %p is invalid"), 
                  CFRunLoopGetCurrent(),  // 当前 RunLoop
                  *_CFGetProgname(),      // 程序名
                  rls);                   // source 指针
        }
        
        // 🔓 解锁 source
        __CFRunLoopSourceUnlock(rls);
        
        // sourceHandled 保持 false
    }
    
    // ==================== 清理阶段:释放引用计数并重新加锁 ====================
    
    // 📉 释放 source 的引用计数
    // 对应开头的 CFRetain(rls)
    // 如果引用计数归零,source 对象会被销毁
    CFRelease(rls);
    
    // 🔒 重新锁定 RunLoop
    __CFRunLoopLock(rl);
    
    // 🔒 重新锁定 RunLoop Mode
    __CFRunLoopModeLock(rlm);
    
    // ✅ 恢复函数入口时的锁状态
    // 满足调用约定:函数入口和出口时 rl 和 rlm 都处于加锁状态
    
    // 返回是否成功处理了 source
    // true: 执行了回调函数
    // false: source 无效,未执行回调
    return sourceHandled;
}

【函数执行流程图】

                __CFRunLoopDoSource1
                        ↓
                🔒 CHECK_FOR_FORK
                        ↓
                📌 CFRetain(rls)
                        ↓
        🔓 Unlock mode & RunLoop
                        ↓
                🔒 Lock source
                        ↓
        ┌───────────────────────────┐
        │     Is Valid?             │
        └───────────┬───────────────┘
                    │
        ┌───────────┴───────────┐
        NO                     YES
        ↓                       ↓
    📝 Log debug          🚩 Unset Signaled
        ↓                       ↓
    🔓 Unlock source      🔓 Unlock source
        ↓                       ↓
    📉 CFRelease          📊 Debug Info
        ↓                       ↓
    🔒 Lock rl & rlm      提取 perform & info
        ↓                       ↓
    return false          🔄 AutoRelease Begin
                                ↓
                          📊 开始性能追踪
                                ↓
                    ┌───────────────────────────┐
                    │  ⚡ 执行回调函数           │
                    │  macOS: perform(msg,      │
                    │         size, reply, info)│
                    │  Windows: perform(info)   │
                    └───────────┬───────────────┘
                                ↓
                          📊 结束性能追踪
                                ↓
                          🔄 AutoRelease End
                                ↓
                          🔒 CHECK_FOR_FORK
                                ↓
                          sourceHandled = true
                                ↓
                          📉 CFRelease(rls)
                                ↓
                    🔒 Lock RunLoop & mode
                                ↓
                          return true

【关键设计要点】

1. 跨平台差异处理

macOS/iOS 平台(Mach Port)
// 函数签名包含 Mach 消息参数
static Boolean __CFRunLoopDoSource1(
    CFRunLoopRef rl, 
    CFRunLoopModeRef rlm, 
    CFRunLoopSourceRef rls,
    mach_msg_header_t *msg,      // Mach 消息头
    CFIndex size,                 // 消息大小
    mach_msg_header_t **reply    // 回复消息
);

// 回调函数类型
typedef void (*CFRunLoopSourcePerform)(
    void *msg,                    // 消息内容
    CFIndex size,                 // 消息大小
    CFAllocatorRef allocator,     // 内存分配器
    void *info                    // 用户上下文
);

Mach 消息结构:

typedef struct {
    mach_msg_bits_t   msgh_bits;        // 标志位
    mach_msg_size_t   msgh_size;        // 消息大小
    mach_port_t       msgh_remote_port; // 远程端口
    mach_port_t       msgh_local_port;  // 本地端口
    mach_msg_id_t     msgh_id;          // 消息 ID
    // ... 消息体
} mach_msg_header_t;
Windows 平台
// 函数签名不包含消息参数
static Boolean __CFRunLoopDoSource1(
    CFRunLoopRef rl, 
    CFRunLoopModeRef rlm, 
    CFRunLoopSourceRef rls
);

// 回调函数类型
typedef void (*CFRunLoopSourcePerform)(
    void *info  // 仅用户上下文
);

2. 锁的管理策略

入口状态:rl 锁定 + rlm 锁定
  ↓
解锁 rlm 和 rl
  ↓
锁定 source(细粒度锁)
  ↓
检查有效性并解锁 source
  ↓
执行回调(无全局锁)
  ↓
重新锁定 rl 和 rlm
  ↓
出口状态:rl 锁定 + rlm 锁定

为什么这样设计?

阶段锁状态原因
入口rl + rlm 加锁保证调用时的状态一致性
检查前只锁 source减少锁竞争,细粒度控制
回调时无锁防止死锁,允许回调中操作 RunLoop
出口rl + rlm 加锁恢复调用约定

3. Source1 的生命周期

创建: CFMachPortCreateRunLoopSource / CFMessagePortCreateRunLoopSource
  ↓
配置: 设置 Mach Port、perform 回调
  ↓
添加到 RunLoop: CFRunLoopAddSource(rl, source, mode)
  ↓
等待端口消息
  ↓
消息到达 → 自动唤醒 RunLoop
  ↓
__CFRunLoopServiceMachPort 接收消息
  ↓
__CFRunLoopDoSource1 处理
  ↓
  ├─ 执行 perform 回调
  ├─ 处理消息内容
  └─ 可选:构造回复消息
  ↓
移除: CFRunLoopRemoveSource(rl, source, mode)
  ↓
销毁: CFRelease(source)

4. 与 Source0 的对比

特性Source0Source1
触发机制手动 signal + wakeup端口消息自动触发
回调参数void *infomsg, size, reply, info
状态标记IsSignaledIsSignaled(也有)
执行时机__CFRunLoopDoSources0__CFRunLoopDoSource1
使用复杂度简单较复杂(需要理解 Mach Port)
性能快(用户态)慢(内核态 IPC)
可移植性好(跨平台)差(依赖 Mach)

5. 信号标记的作用

__CFRunLoopSourceUnsetSignaled(rls);

为什么 Source1 也需要 signaled 标记?

虽然 Source1 基于端口,但仍然使用 signaled 标记来:

  1. 标记待处理状态: 端口收到消息时设置
  2. 防止重复处理: 处理后清除标记
  3. 统一接口: 与 Source0 保持一致的状态管理

设置时机:

// 在 __CFRunLoopServiceMachPort 中
if (收到端口消息) {
    __CFRunLoopSourceSetSignaled(source);  // 设置标记
    // 稍后调用 __CFRunLoopDoSource1
}

【使用场景】

1. CFMachPort(Mach 端口通信)

// 创建 Mach Port
CFMachPortContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
CFMachPortRef machPort = CFMachPortCreate(
    kCFAllocatorDefault,
    machPortCallback,  // 回调函数
    &context,
    NULL
);

// 回调函数
void machPortCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) {
    mach_msg_header_t *header = (mach_msg_header_t *)msg;
    
    // 解析 Mach 消息
    NSLog(@"Received Mach message, ID: %d", header->msgh_id);
    
    // 处理消息内容
    // ...
}

// 创建 RunLoop Source
CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(
    kCFAllocatorDefault,
    machPort,
    0  // order
);

// 添加到 RunLoop
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);

// 清理
CFRelease(source);
CFRelease(machPort);

2. CFMessagePort(高层消息通信)

// 创建本地 Message Port
CFMessagePortContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
CFMessagePortRef localPort = CFMessagePortCreateLocal(
    kCFAllocatorDefault,
    CFSTR("com.example.messageport"),  // 端口名称
    messagePortCallback,                // 回调函数
    &context,
    NULL
);

// 回调函数
CFDataRef messagePortCallback(CFMessagePortRef port, 
                              SInt32 msgid, 
                              CFDataRef data, 
                              void *info) {
    // 处理接收到的数据
    NSData *receivedData = (__bridge NSData *)data;
    NSLog(@"Received message ID: %d, data: %@", msgid, receivedData);
    
    // 返回回复数据(可选)
    NSString *reply = @"Message received";
    return (__bridge_retained CFDataRef)[reply dataUsingEncoding:NSUTF8StringEncoding];
}

// 创建 RunLoop Source
CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(
    kCFAllocatorDefault,
    localPort,
    0
);

// 添加到 RunLoop
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);

// 从其他进程发送消息
CFMessagePortRef remotePort = CFMessagePortCreateRemote(
    kCFAllocatorDefault,
    CFSTR("com.example.messageport")
);

NSString *message = @"Hello";
CFDataRef data = (__bridge CFDataRef)[message dataUsingEncoding:NSUTF8StringEncoding];

SInt32 status = CFMessagePortSendRequest(
    remotePort,
    100,  // 消息 ID
    data,
    10.0, // 发送超时
    10.0, // 接收超时
    NULL, // 回复 mode
    NULL  // 回复数据
);

3. 进程间通信(IPC)

// 进程 A:服务端
- (void)setupIPCServer {
    // 创建 Mach Port
    mach_port_t serverPort;
    mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &serverPort);
    
    // 注册端口(通过 Bootstrap Server)
    // 这样其他进程可以查找到这个端口
    
    // 创建 CFMachPort 包装
    CFMachPortContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
    CFMachPortRef cfPort = CFMachPortCreateWithPort(
        kCFAllocatorDefault,
        serverPort,
        ipcCallback,
        &context,
        NULL
    );
    
    // 添加到 RunLoop
    CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, cfPort, 0);
    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopCommonModes);
}

void ipcCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) {
    mach_msg_header_t *header = (mach_msg_header_t *)msg;
    
    // 处理 IPC 消息
    NSLog(@"IPC message received from port: %d", header->msgh_remote_port);
}

// 进程 B:客户端
- (void)sendIPCMessage {
    // 查找服务端的端口
    mach_port_t serverPort = /* 从 Bootstrap Server 查找 */;
    
    // 构造消息
    mach_msg_header_t msg;
    msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
    msg.msgh_size = sizeof(msg);
    msg.msgh_remote_port = serverPort;
    msg.msgh_local_port = MACH_PORT_NULL;
    msg.msgh_id = 100;
    
    // 发送消息
    mach_msg_send(&msg);
}

【总结】

__CFRunLoopDoSource1 是 RunLoop 处理基于 Mach Port 事件源的核心实现,其设计特点:

  1. 跨平台支持: 通过条件编译适配不同平台(macOS/iOS vs Windows)
  2. 简洁高效: 相比 Source0,逻辑更简单(不需要 signal/wakeup)
  3. 内核驱动: 依赖 Mach Port 的内核机制,自动唤醒 RunLoop
  4. 安全的锁管理: 回调前解锁,防止死锁
  5. 统一接口: 与 Source0 保持一致的状态管理(signaled 标记)

适用场景

  • 进程间通信(IPC): 高效的跨进程消息传递
  • 系统事件: 文件系统监控、网络事件等
  • 底层服务: 需要与系统守护进程通信
  • 高频事件: 不适合非常高频的事件(用 Source0)

与 Source0 的选择

场景推荐
触摸事件、手势识别Source0
进程间通信Source1
自定义事件Source0
系统服务通信Source1
高频轮询Source0

理解 Source1 的实现对于掌握 macOS/iOS 的 IPC 机制、系统事件处理和 RunLoop 的完整工作流程至关重要。