前文讲到了进入计时队列轮询,通过_dispatch_mgr_invoke进入到了下面这段代码:
static dispatch_queue_t
_dispatch_mgr_invoke(dispatch_queue_t dq)
{
static const struct timespec timeout_immediately = { 0, 0 };
struct timespec timeout;
const struct timespec *timeoutp;
struct timeval sel_timeout, *sel_timeoutp;
fd_set tmp_rfds, tmp_wfds;
struct kevent kev[1];
int k_cnt, k_err, i, r;
_dispatch_thread_setspecific(dispatch_queue_key, dq);
for (;;) {
(1). run timers;
_dispatch_run_timers();
(2). get_Next_timer_fire
timeoutp = _dispatch_get_next_timer_fire(&timeout);
……
(3). 监听kevent
k_cnt = kevent(_dispatch_kq, NULL, 0, kev, sizeof(kev) / sizeof(kev[0]), timeoutp);
k_err = errno;
switch (k_cnt) {
case -1:
if (k_err == EBADF) {
DISPATCH_CLIENT_CRASH("Do not close random Unix descriptors");
}
(void)dispatch_assume_zero(k_err);
continue;
default:
(4). 处理kev
_dispatch_mgr_thread2(kev, (size_t)k_cnt);
// fall through
case 0:
_dispatch_force_cache_cleanup();
continue;
}
}
return NULL;
}
void
_dispatch_run_timers(void)
{
unsigned int i;
for (i = 0; i < DISPATCH_TIMER_COUNT; i++) {
_dispatch_run_timers2(i);
}
}
#define DISPATCH_TIMER_COUNT (sizeof _dispatch_kevent_timer / sizeof _dispatch_kevent_timer[0])
我们进入到_dispatch_run_timers2();
static void
_dispatch_run_timers2(unsigned int timer)
{
dispatch_source_t ds;
uint64_t now, missed;
if (timer == DISPATCH_TIMER_INDEX_MACH) {
now = _dispatch_absolute_time();
} else {
now = _dispatch_get_nanoseconds();
}
while ((ds = TAILQ_FIRST(&_dispatch_kevent_timer[timer].dk_sources))) {
// We may find timers on the wrong list due to a pending update from
// dispatch_source_set_timer. Force an update of the list in that case.
if (timer != ds->ds_ident_hack) {
_dispatch_timer_list_update(ds);
continue;
}
if (!ds->ds_timer.target) {
// no configured timers on the list
break;
}
if (ds->ds_timer.target > now) {
// Done running timers for now.
break;
}
if (ds->ds_timer.flags & (DISPATCH_TIMER_ONESHOT|DISPATCH_TIMER_ABSOLUTE)) {
dispatch_atomic_inc(&ds->ds_pending_data);
ds->ds_timer.target = 0;
} else {
// Calculate number of missed intervals.
missed = (now - ds->ds_timer.target) / ds->ds_timer.interval;
dispatch_atomic_add(&ds->ds_pending_data, missed + 1);
ds->ds_timer.target += (missed + 1) * ds->ds_timer.interval;
}
_dispatch_timer_list_update(ds);
_dispatch_wakeup(ds);
}
}
GCD中提供了两种计时队列type: WALL和MACH
#define DISPATCH_TIMER_INDEX_WALL 0
#define DISPATCH_TIMER_INDEX_MACH 1
根据不同的type获取到的now 的时间分别是绝对时间和相对时间;
定义的两个定时器:
static struct dispatch_kevent_s _dispatch_kevent_timer[] = {
{
.dk_kevent = {
.ident = DISPATCH_TIMER_INDEX_WALL,
.filter = DISPATCH_EVFILT_TIMER,
.udata = &_dispatch_kevent_timer[0],
},
.dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[0].dk_sources),
},
{
.dk_kevent = {
.ident = DISPATCH_TIMER_INDEX_MACH,
.filter = DISPATCH_EVFILT_TIMER,
.udata = &_dispatch_kevent_timer[1],
},
.dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[1].dk_sources),
},
};
上面的两个定时器中有一个结构需要注意:
.dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[0].dk_sources),
这个dk_sources结构如下:
struct dispatch_kevent_s {
TAILQ_ENTRY(dispatch_kevent_s) dk_list;
TAILQ_HEAD(, dispatch_source_s) dk_sources;
struct kevent dk_kevent;
};
#define _TAILQ_ENTRY(type, qual) \
struct { \
qual type *tqe_next; /* next element */ \
qual type *qual *tqe_prev; /* address of previous next element */\
}
#define _TAILQ_HEAD(name, type, qual) \
struct name { \
qual type *tqh_first; /* first element */ \
qual type *qual *tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
是不是发现了两个链表:
下面我们开始轮询:
while ((ds = TAILQ_FIRST(&_dispatch_kevent_timer[timer].dk_sources)))
然后进入到检查阶段
说到这里,先补充一下_dispatch_kevent_timer[timer].dk_sources队列的一个特征:
按照时间先后顺序排列ds;
也就是每次插入ds时,都会按照时间先后顺序插入;
(a). 时间检查
if (!ds->ds_timer.target) {
// no configured timers on the list
break;
}
if (ds->ds_timer.target > now) {
// Done running timers for now.
break;
}
(b). 处理到时任务
if (ds->ds_timer.flags & (DISPATCH_TIMER_ONESHOT|DISPATCH_TIMER_ABSOLUTE)) {
dispatch_atomic_inc(&ds->ds_pending_data);
ds->ds_timer.target = 0;
} else {
// Calculate number of missed intervals.
missed = (now - ds->ds_timer.target) / ds->ds_timer.interval;
dispatch_atomic_add(&ds->ds_pending_data, missed + 1);
ds->ds_timer.target += (missed + 1) * ds->ds_timer.interval;
}
params->values.flags &= ~DISPATCH_TIMER_WALL_CLOCK;
dispatch_source_create(dispatch_source_type_t type,
uintptr_t handle,
unsigned long mask,
dispatch_queue_t q)
因此:
static bool
dispatch_source_type_timer_init(dispatch_source_t ds, dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t q)
{
if (!dispatch_source_type_kevent_init(ds, type, handle, mask, q)) {
return false;
}
ds->ds_needs_rearm = true;
ds->ds_timer.flags = mask;
return true;
}
设置的mask也是0;
所以进入了:
} else {
// Calculate number of missed intervals.
missed = (now - ds->ds_timer.target) / ds->ds_timer.interval;
dispatch_atomic_add(&ds->ds_pending_data, missed + 1);
ds->ds_timer.target += (missed + 1) * ds->ds_timer.interval;
}
接下来更新ds->ds_timer.target ,其实就是Missed+1; 在现在这个时间再加一个粒度,认为是它下一个到时的时间;
接测就结束了
_dispatch_timer_list_update(ds);
void
_dispatch_timer_list_update(dispatch_source_t ds)
{
dispatch_source_t dsi = NULL;
int idx;
dispatch_assert(_dispatch_queue_get_current() == &_dispatch_mgr_q);
// do not reschedule timers unregistered with _dispatch_kevent_release()
if (!ds->ds_dkev) {
return;
}
// Ensure the source is on the global kevent lists before it is removed and
// readded below.
_dispatch_kevent_merge(ds);
TAILQ_REMOVE(&ds->ds_dkev->dk_sources, ds, ds_list);
// change the list if the clock type has changed
if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) {
idx = DISPATCH_TIMER_INDEX_WALL;
} else {
idx = DISPATCH_TIMER_INDEX_MACH;
}
ds->ds_dkev = &_dispatch_kevent_timer[idx];
if (ds->ds_timer.target) {
TAILQ_FOREACH(dsi, &ds->ds_dkev->dk_sources, ds_list) {
if (dsi->ds_timer.target == 0 || ds->ds_timer.target < dsi->ds_timer.target) {
break;
}
}
}
if (dsi) {
TAILQ_INSERT_BEFORE(dsi, ds, ds_list);
} else {
TAILQ_INSERT_TAIL(&ds->ds_dkev->dk_sources, ds, ds_list);
}
}
因为流程走到这说明ds已经到时了,进入
_dispatch_wakeup(ds);
进入到唤醒的流程:
dispatch_queue_t
_dispatch_wakeup(dispatch_object_t dou)
{
dispatch_queue_t tq;
if (slowpath(DISPATCH_OBJECT_SUSPENDED(dou._do))) {
return NULL;
}
if (!dx_probe(dou._do) && !dou._dq->dq_items_tail) {
return NULL;
}
if (!_dispatch_trylock(dou._do)) {
return NULL;
}
_dispatch_retain(dou._do);
tq = dou._do->do_targetq;
_dispatch_queue_push(tq, dou._do);
return tq; // libdispatch doesn't need this, but the Instrument DTrace probe does
}