1、CFRunLoopModeRef 什么时候创建的?

在调用__CFRunLoopFindMode(rl, modeName, create)

1.1)首先通过modeName 在RunLoop 中的_modes/_commentModes 中查找,查找到直接返回,否则要进行 create 判断。

1.2)当 create 参数为true 的时候创建CFRunLoopModeRef, 并加入到rl(RunLoop)中。创建的过程默认创建了 timer端口(mk_timer(iOS & Mac OS X)/dispatch_timer(Mac OS X)), 并且将RunLoop 的wakeupPort 加入到 CFRunLoopModeRef的_portSet 中。注意: _portSet 实际调用了 mach_port_allocate 并分配了端口集合权限(MACH_PORT_RIGHT_PORT_SET),创建完成后 _portSet 至少有两个成员 一个是timer的端口,一个是RunLoop的wakup端口。

1.3)Mac OS下支持mk和dispatch timer,iOS下仅支持mk timer

2、CFRunLoop 何时创建的?

2.1) 当前的线程调用 CFRunLoopGetCurrent的时候创建,否则是没有任何创建的,创建的过程为, 首先判断pthread 的TSD 是否存储了CFRunLoopRef 对象,如果有直接提取,如果没有 则调用私有函数__CFRunLoopGet0, 并传入当前线程的 pthread_t 的值(pthread_self()取得)。

2.1.1) __CFRunLoopGet0(pthread_t) 概述

2.1.1.1) 首先判断 pthread_t 是否为空,如果为空,则赋值为主线程 pthread_main_thread_np取得

2.1.1.2) 然后创建一个全局字典,通过原子函数(OSAtomicCompareAndSwapPtrBarrier),确保这个RunLoop字典唯一创建,并在创建字典的同时通过调用 __CFRunLoopCreate 创建一个 主线程的RunLoop, 并以线程pthread_t.p 值为键值,存入全局字典

2.1.1.3) 从全局字典中取出以pthread_t.p 为键的RunLoop 对象,如果没有则调用__CFRunLoopCreate 创建一个 RunLoop 对象

2.1.1.4) 如果传入的线程pthread_t 与当前线程相同,则存入线程私有数据.(TSD),

2.1.1.5) 返回RunLoop 对象

2.1.2) __CFRunLoopCreate 概述

这里简单分配一个内存区域,然后返回这个内存区域,重点在于分配了一个wakeup port 端口,这个端口对于RunLoop 十分重要。

3、CFRunLoopRun 如何工作的?

3.1) CFRunLoopRun 内部是一个do{}while() 循环,调用了CFRunLoopRunSpecific, 通过判断 CFRunLoopRunSpecific 返回值确定是否继续执行, 传入modeName 为默认的模式(kCFRunLoopDefaultMode),返回值如果为kCFRunLoopRunStopped 或者 kCFRunLoopRunFinished 则退出循环, CFRunLoop 也就结束了

3.2) CFRunLoopRunSpecific 函数说明

3.2.1) 参数说明,

a) rl(CFRunLoopRef),

b) modeName(CFStringRef)

c) seconds(CFTimeInterval) 这个是设置超时时间

d) returnAfterSourceHandled(Boolean) 当source0处理后,是否还继续处理其余的source0,这个CFRunLoopRun 传入的是 false,意味这继续处理所有souce0

3.2.2) 执行过程

a) 首先判断 runloop 的deallocating的标志位,如果是销毁中状态,则直接返回kCFRunLoopRunFinished

b) 判断模式是否为空,为空也返回 kCFRunLoopRunFinished

c) 根据观察者掩码执行本次进入循环 观察者回调

d) 执行 __CFRunLoopRun 函数

e) 根据观察者掩码执行本次退出循环 观察者回调

f) 返回 _CFRunLoopRun 的返回值

3.3) __CFRunLoopRun 函数说明

3.3.1) 参数说明

a) CFRunLoopRef rl

b) CFRunLoopModeRef rlm

c) CFTimeInterval seconds

d) Boolean stopAfterHandle 同 CFRunLoopRunSpecific 的 returnAfterSourceHandled

e) CFRunLoopModeRef previousMode 之前的模式

3.3.2) 执行过程

a) 首先判断了 runloop和runloopmode的停止状态,如果是停止状态则直接返回 kCFRunLoopRunStopped

b) 在主线程下取得主线程的 mach 端口(dispatchPort)

c) 根据seconds 小于 间隔限制 貌似一个常量,建立一个 dispatch_source 并加入timer,队列如果是主线程,则使用主线程队列,如果后台线程则使用后台线程,这个dispatch_source, 应该在CFRunLoopRunInMode 是传入的seconds 有效,CFRunLoop 传入的1.0e10 并不创建这个source。这个source 貌似仅仅为了唤醒 runloop

d) 进入 do{}while() 循环

  d.1) 定义基于 mach 消息机制的变量

  d.2) 观察者beforeXXX回调(timer 和 source)

  d.3) 调用__CFRunLoopDoSources0 处理 source0, 仅仅处理通过 CFRunLoopSourceSignal 发信号的信号源,循环处理取决于 stopAfterHandle 这个变量的值, 如果处理过则返回true, 否则返回 false,

  d.4) 定义mach_msg 的超时时间设置的变量(布尔值 poll ),如果 seconds 为0 或者 __CFRunLoopDoSources0 返回 true 则证明拉取过,则设置 mach_msg 超时为0, mach_msg 不进行任何等待,直接返回,否则设置超时时间为无穷大,永远等待直到有消息返回给指定的端口

  d.5) 如果主线程端口不为空并且不是第一次进入循环则接收消息,如果没有消息,立即返回,如果接收到消息,跳转到处理消息地点,这里使用goto handlemsg;

  d.6) 处理观察者回调为afterXXX

  d.7) 如果可能将 主线程端口加入等待端口集合中portSet(初始值为runloop mode 的portSet)

  d.8) 继续接收消息,端口设置为集合端口 portSet , 根据poll值来设置超时时间,如果poll则为0, 否则无穷大时间。如果为无穷大超时时间,则线程处于休眠状态,直到向这个端口集合中的任意端口发送了 mach 消息

  d.9) 消息处理,通过判断 mach 消息发送给了那个端口进行处理

    i) 如果为空或者是runloop的wakup端口,则什么也不做,继续循环

    ii) 如果是timer时间端口,则处理timer时间回调(遍历回调,尽可能的处理一个timer事件)

    iii) 如果是主线程端口,则主线程处理msg,貌似回调GCD ????

    iv) 否则的情况下处理 source1

  d.10) 设置返回值

__CFRunLoopRun 源码, 去掉windows、disaptch_timer 支持

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
uint64_t startTSR = mach_absolute_time();
/* 首先判断 CFRunLoopRef 是否停止,停止的话,重置停止状态并且返回停止状态 */
if (__CFRunLoopIsStopped(rl)) {
/* 是否设置了停止位 */
  __CFRunLoopUnsetStopped(rl);
/* 重置停止位、并返回停止状态 */
       return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
/* 这个模式下是否停止了、并且重置模式停止状态 */
rlm->_stopped = false;
/* 返回停止 */
return kCFRunLoopRunStopped;
} 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))
); if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF(); /* libDispatch -> private.h */
/* 定义一个 dispatch_source_t 为了CFRunLoopRunInMode 自定义时间时存在,为了做唤醒使用 */
dispatch_source_t timeout_timer = NULL;
struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
if (seconds <= 0.0) { // instant timeout
seconds = 0.0;
timeout_context->termTSR = 0ULL;
} else if (seconds <= TIMER_INTERVAL_LIMIT/* TIMER_INTERVAL_LIMIT 504911232.0*/) {
/* 尝试超时后唤醒,如果当前线程出于等待状态的情况下 */
dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
/* 创建source */
timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_retain(timeout_timer);
/* 写入source 的 上下文中 */
timeout_context->ds = timeout_timer;
timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
/* 设置source的上下文 */
dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
/* 设置时间事件的回调函数,调用了CFRunLoopWakeUp, 这个函数向等待端口发送了一个消息, 用来激活循环 */
dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
/* 设置时间事件的取消函数 */
dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
/* 计算触发的时间点(纳秒) */
uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
/* 设置source的timer, 仅仅延迟执行一次 */
dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at)/* start */, DISPATCH_TIME_FOREVER/* interval */, 1000ULL); /* 只执行一次 */
/* 唤醒source */
dispatch_resume(timeout_timer);
} else { // infinite timeout
seconds = 9999999999.0;
timeout_context->termTSR = UINT64_MAX;
} Boolean didDispatchPortLastTime = true;
int32_t retVal = 0;
do {
voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
voucher_t voucherCopy = NULL;
/* 定义mach接收 msg 缓冲区 3k 大小 */
uint8_t msg_buffer[3 * 1024]; /* 3K */
mach_msg_header_t *msg = NULL;
mach_port_t livePort = MACH_PORT_NULL;
/* CFRunLoopModeRef->_portSet 实际上就是 mach_port_t -->>> __CFPort OR __CFPortSet */
__CFPortSet waitSet = rlm->_portSet; /* mac 下 就是 mach_port_t 等待端口集合 */
/* 重置 CFRunLoopRef->_perRunData->ignoreWakeUps = 0x57414B45; WAKUP 标识 */
__CFRunLoopUnsetIgnoreWakeUps(rl);
/* 回调 Before Timer && Source */
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
/* 回调block, 只能回到一次block? */
__CFRunLoopDoBlocks(rl, rlm);
/* source0 是否handled(回调) */
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle/* __CFRunLoopRun 参数 */); //source 0 必须signed
if (sourceHandledThisLoop) {
/* 回调了Source0 */
__CFRunLoopDoBlocks(rl, rlm); //source 0
}
     /* 确定端口集合接收消息是否设置永远超时,如果poll为真,则立即返回,否则无限等待直到有消息过来 */
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); /* 从CFRunLoopRun 过来的话 timeout_context->termTSR 永远不等于0*/ if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
/* 首次循环不执行,因为外部定义 didDispatchPortLastTime 为 true */
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0/*timeout == 0*/, &voucherState, NULL)) {
/* 主队列接收消息,接收消息成功则跳转到处理消息过程source1 */
goto handle_msg;
}
}
/* 执行到这里意味着 dispatchPort 没有成功接收到 mach message */
didDispatchPortLastTime = false; /* do while 循环外定义,初始化值 true */
/* 没有回调并且timerTSR 大于 0 的情况下 并且 有等待掩码, poll 定义为 -> source0 处理了 或者 定义唤醒时间为0 */
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
/* 设置CFRunLoopRef 睡眠标志位。因为后面有进行一个 mach_msg 的等待操作 */
__CFRunLoopSetSleeping(rl);
__CFPortSetInsert(dispatchPort, waitSet); /* 将主端口 插入等待集合中 */
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
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);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
/* 从端口集合中移除 disaptchPort */
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopSetIgnoreWakeUps(rl);
// user callouts now OK again
    __CFRunLoopUnsetSleeping(rl); //重置睡眠状态
/* pool代表: 回调过 或者 触发时间为0, 根据掩码进行回调 */
    if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
/* 处理 mach 消息成功的标签*/
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
if (MACH_PORT_NULL == livePort) {
CFRUNLOOP_WAKEUP_FOR_NOTHING();
} else if (livePort == rl->_wakeUpPort) {
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
} else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
} else if (livePort == dispatchPort) {
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
didDispatchPortLastTime = true;
} else {
/* 接收端口不为空,并且没有满足 timer、主队列端口情况下,处理 source1 */
CFRUNLOOP_WAKEUP_FOR_SOURCE();
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) {
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);
}
} // Restore the previous voucher
_CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release); }
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle/* CFRunLoopRun ->false */) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;
}
voucher_mach_msg_revert(voucherState);
os_release(voucherCopy); } while (0 == retVal); // 循环结束, retVal 等于 0
/* 释放超时timer */
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
} return retVal;
} //__CFRunLoopRun

  

CFRunLoop 源码学习笔记(CF-1151.16)的更多相关文章

  1. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  2. Underscore.js 源码学习笔记(上)

    版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}());  这样的东西,我们应该知道这是一个 IIFE(立即执行 ...

  3. RocketMQ 源码学习笔记————Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记----Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest ...

  4. RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest Roc ...

  5. JUC源码学习笔记4——原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法

    JUC源码学习笔记4--原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法 volatile的原理和内存屏障参考<Java并发编程的艺术> 原子类源码基于JDK8 ...

  6. AXI_LITE源码学习笔记

    AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ...

  7. Hadoop源码学习笔记(6)——从ls命令一路解剖

    Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...

  8. Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构

    Hadoop源码学习笔记(5) ——回顾DataNode和NameNode的类结构 之前我们简要的看过了DataNode的main函数以及整个类的大至,现在结合前面我们研究的线程和RPC,则可以进一步 ...

  9. Hadoop源码学习笔记(4) ——Socket到RPC调用

    Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ...

随机推荐

  1. 关于Unity中物体分别在本地和世界坐标系对应方向的移动

    方向 Vector3可以定义以世界坐标轴为参考的三维矢量,Vector3.forward,Vector3.up,Vector3.right方别对应物体世界坐标系的Z,Y,X轴方向的单位向量,或者叫三维 ...

  2. SAP MDM 简介 --- MDM 实施方法论 - 企业的SOA 数据总线基础

    SAP NetWeaver Master Data Management (MDM) ,SAP NetWeaver 主数据管理,MDM可以进行数据合并,降低数据维护成本,确保跨系统的数据一致性,加快业 ...

  3. Vue .Net 前后端分离框架搭建

    [参考]IntellIJ IDEA 配置 Vue 支持 打开Vue项目 一.前端开发环境搭建 1.零基础 Vue 开发环境搭建 打开运行Vue项目 2.nodejs http-proxy-middle ...

  4. Spring源码导入IDEA

    导入Spring 4.3.8的源码,JDK的版本1.8以上:因为 1.下载Spring源码 方法一: (1)下载并安装GIt,下载地址:https://git-scm.com/download/win ...

  5. tensorflow 调试tfdbg

    1.执行pip install pyreadline 安装pyreadline 2.修改对应代码如下 with tf.Session() as sess: sess.run(tf.global_var ...

  6. A real example of vioplot in R (sample data and code attached)

    Basic information Package name: vioplot Package homepage: https://cran.r-project.org/web/packages/vi ...

  7. One example to understand SemFix: Program Repair via Semantic Analysis

    One example to understand SemFix: Program Repair via Semantic Analysis Basic Information Authors: Ho ...

  8. MySql.Data.dll官网下载

    Mysql.Data.dll官网下载 在项目开发中链接MySQL数据库经常要用到Mysql.Data.dll,网上虽然有很多,但是还是比较信赖官网的 今天就从官网下载一次记录一下过程 1.下载地址 官 ...

  9. git使用——推送本地文件到远程仓库

    捣鼓了一下午之后总结如下:   1.首先可以照着这个链接里面博主给出的详细方法进行操作和配置: http://www.open-open.com/lib/view/open1454507333214. ...

  10. JAVA课程课后作业03之动手动脑

    一.构造函数 问题一: 错误代码如图: 错误原因:从图片中的编译报错的地方来看,程序是在给新的对象分配空间是出现了问题,因而我们往下观察Foo类,Foo类的构造方法是有一个参数的有参方法,而前面构造新 ...