变态的libDispatch源码分析-全局队列异步延时任务处理过程-计时轮询

13 篇文章 0 订阅

前文讲到了进入计时队列轮询,通过_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;
}

这段代码的for循环,在root队列中调度mgr_queue的时候,创建的线程中将一直轮询下去;


1. run timers

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])

表示每个计时队列都会运行一次run

我们进入到_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);
	}
}

(1). 获取now time

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),
	},
};


(2). 轮询队列

 

上面的两个定时器中有一个结构需要注意: 

.dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[0].dk_sources),

这就是存放我们延时任务队列ds的链表,还记得ds可以看看上一篇。

这个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,)

是不是发现了两个链表:

  • dk_list
  • dk_sources;


下面我们开始轮询:

while ((ds = TAILQ_FIRST(&_dispatch_kevent_timer[timer].dk_sources)))

这句代码表示从_dispatch_kevent_timer[timer].dk_sources 取出其第一个元素,

然后进入到检查阶段

(3). ds检测

说到这里,先补充一下_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;
}

这段代码中,若发现当前ds的s_timer.target > now则表示后面所有的ds都没到时间,需要继续等待一段时间,则返回了;


(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;
		}

ds_timer.flags在set_timer时,就设定了:

		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)

mask = 0;

因此:

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;
		}

这段代码。现实根据粒度interval,算出了missed intervals的次数,然后修改ds_pending_data;

接下来更新ds->ds_timer.target ,其实就是Missed+1; 在现在这个时间再加一个粒度,认为是它下一个到时的时间;

接测就结束了


(4). 更新队列


_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);
	}
}

这段代码进行了链表的操作,目的就是按照时间先后顺序,从新排列队列;


(5). 唤醒ds

因为流程走到这说明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
}





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值