【iOS 底层】GCD源码分析 单例&信号量&调度组

大憨熊 提交于 2021-01-15 02:14:36

Pre

  • 源码分析版本:libdispatch-1173.100.2

GCD - 单例

  • 老规矩分析之前带着问题:
    • GCD如何保证单例中的代码只执行一次?
    • 如果多次调用会有什么结果?

基本使用

//将token用static修饰可以保证其在整个程序生命周期都保持不被销毁
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
		NSLog(@"");
    });

开始往里走!

void
//实际调用的是下面这个方法。此处只留下关键部分。
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
	// 1. 初始化一个dispatch_once_gate_t结构体l
	dispatch_once_gate_t l = (dispatch_once_gate_t)val;
	
	/* 2. 取l中的dgo_once和acquire字符串传入os_atomic_load函数处理得到v值
	* 尝试过进宏定义去看,但是实在走不下去了
	*大概就是创建了一个对象并存储
	*如果执行过则返回1,没执行过返回0
	*/
	uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
	
	/*
	*3.1 若v的值为1,表明已经执行过,直接返回,不作处理
	*/
	if (likely(v == DLOCK_ONCE_DONE)) {
		return;
	}
	
	/*
	* 3.2 若v不为1,进入_dispatch_once_gate_tryenter(1)函数(见下方*内部调用函数*)
	*/
	if (_dispatch_once_gate_tryenter(l)) {
		//调用。此处函数嵌套见*代码块A*
		return _dispatch_once_callout(l, ctxt, func);
	}
	// 等待,及一些异常判断和处理。此处见*代码块B*
	return _dispatch_once_wait(l);
}

/*
*用到的宏定义
*/
#define DLOCK_ONCE_DONE  (~(uintptr_t)0)

/*
* 内部调用函数
*/
static inline bool
_dispatch_once_gate_tryenter(dispatch_once_gate_t l)
{
	//判断该对象是否存储过,若存储过返回false,没存储过返回true
	return os_atomic_cmpxchg(&l->dgo_once, DLOCK_ONCE_UNLOCKED,
			(uintptr_t)_dispatch_lock_value_for_self(), relaxed);
}

代码块A


static void
_dispatch_once_callout(dispatch_once_gate_t l, void *ctxt,
		dispatch_function_t func)
{
	//block执行
	_dispatch_client_callout(ctxt, func);
	//告诉系统执行完了,并为l对象内存地址存储加锁
	_dispatch_once_gate_broadcast(l);
}

static inline void
_dispatch_once_gate_broadcast(dispatch_once_gate_t l)
{
	dispatch_lock value_self = _dispatch_lock_value_for_self();
	uintptr_t v;
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
	//正在操作
	v = _dispatch_once_mark_quiescing(l);
#else
	//操作完成
	v = _dispatch_once_mark_done(l);
#endif
	if (likely((dispatch_lock)v == value_self)) return;
	_dispatch_gate_broadcast_slow(&l->dgo_gate, (dispatch_lock)v);
}

代码块B

//摘取关键部分
void
_dispatch_once_wait(dispatch_once_gate_t dgo)
{
	dispatch_lock self = _dispatch_lock_value_for_self();
	uintptr_t old_v, new_v;
	dispatch_lock *lock = &dgo->dgo_gate.dgl_lock;
	uint32_t timeout = 1;

	for (;;) {
		os_atomic_rmw_loop(&dgo->dgo_once, old_v, new_v, relaxed, {
			if (likely(old_v == DLOCK_ONCE_DONE)) {
				os_atomic_rmw_loop_give_up(return);
			}
			new_v = old_v | (uintptr_t)DLOCK_WAITERS_BIT;
			if (new_v == old_v) os_atomic_rmw_loop_give_up(break);
		});
		if (unlikely(_dispatch_lock_is_locked_by((dispatch_lock)old_v, self))) {
			//若old_v已经被加过锁会crash。
			//留下疑问,什么情况下会lock recursively?
			DISPATCH_CLIENT_CRASH(0, "trying to lock recursively");
		}
		_dispatch_thread_switch(new_v, flags, timeout++);
		(void)timeout;
	}
}

GCD 信号量

基本使用

    //信号量创建
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    //+1
    dispatch_semaphore_signal(sem);
    //-1 堵塞
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

开始往里走!dispatch_semaphore_create

dispatch_semaphore_t
dispatch_semaphore_create(long value){
	dispatch_semaphore_t dsema;
	if (value < 0) {//value必须>=0
		return DISPATCH_BAD_INPUT;
	}
	// 初始化及变量设置。可见信号量底层是一个dispatch_object_t对象。
	dsema = _dispatch_object_alloc(DISPATCH_VTABLE(semaphore),
			sizeof(struct dispatch_semaphore_s));
	dsema->do_next = DISPATCH_OBJECT_LISTLESS;
	dsema->do_targetq = _dispatch_get_default_queue(false);
	dsema->dsema_value = value;
	_dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
	dsema->dsema_orig = value;
	//返回该对象
	return dsema;
}

开始往里走!dispatch_semaphore_signal

long
dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
	/*
	* 1. 信号量的值+1
	*/
	long value = os_atomic_inc2o(dsema, dsema_value, release);
	/*
	* 2.1 若value>0,直接返回0
	*/
	if (likely(value > 0)) {
		return 0;
	}
	/*
	* 2.2 若value == -1,crash
	*/
	if (unlikely(value == LONG_MIN)) {
		DISPATCH_CLIENT_CRASH(value,
				"Unbalanced call to dispatch_semaphore_signal()");
	}
	/*
	* 2.3 返回1。
	*     这里才是重点!
	*/
	return _dispatch_semaphore_signal_slow(dsema);
}

来看重点

long
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
	//1. 数据判断及异常处理。见代码块C
	_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
	//见代码块D
	_dispatch_sema4_signal(&dsema->dsema_sema, 1);
	//一通处理完之后返回1
	return 1;
}

代码块C

static inline void
_dispatch_sema4_create(_dispatch_sema4_t *sema, int policy)
{
	if (!_dispatch_sema4_is_created(sema)) {
		//若该信号量没有被初始化过,create一波,有兴趣的话见下方*嵌套函数*
		_dispatch_sema4_create_slow(sema, policy);
	}
}

/*
* 相关宏定义
*/
#define _dispatch_sema4_is_created(sema)   (*(sema) != MACH_PORT_NULL)

/*
* 嵌套函数
*/
void
_dispatch_sema4_create_slow(_dispatch_sema4_t *s4, int policy)
{
	semaphore_t tmp = MACH_PORT_NULL;
	_dispatch_fork_becomes_unsafe();
	// 延迟分配信号量端口。
	//这个kern_return_t就是个int类型
	kern_return_t kr = semaphore_create(mach_task_self(), &tmp, policy, 0);
	DISPATCH_SEMAPHORE_VERIFY_KR(kr);

	if (!os_atomic_cmpxchg(s4, MACH_PORT_NULL, tmp, relaxed)) {
		kr = semaphore_destroy(mach_task_self(), tmp);
		DISPATCH_SEMAPHORE_VERIFY_KR(kr);
	}
}

代码块D

void
_dispatch_sema4_signal(_dispatch_sema4_t *sema, long count)
{//一个do-while循环判断有无异常,并--count,意思就是走一遍没问题count为0了就不等了
	do {
		//这个kern_return_t就是个int类型
		kern_return_t kr = semaphore_signal(*sema);
		DISPATCH_SEMAPHORE_VERIFY_KR(kr);
	} while (--count);
}

/*
* 相关宏定义
*/
//会给mach发送消息进行相关判断处理
#define DISPATCH_SEMAPHORE_VERIFY_KR(x) do { \
		DISPATCH_VERIFY_MIG(x); \
		if (unlikely((x) == KERN_INVALID_NAME)) { \
			DISPATCH_CLIENT_CRASH((x), \
				"Use-after-free of dispatch_semaphore_t or dispatch_group_t"); \
		} else if (unlikely(x)) { \
			DISPATCH_INTERNAL_CRASH((x), "mach semaphore API failure"); \
		} \
	} while (0)

// MIG_REPLY_MISMATCH表示:
// 1)信号处理程序没有使用异步安全API。更多信息请参阅sigaction(2)手册页。
// 2)手工制作的mach_msg*()调用失败。使用MIG。
#define DISPATCH_VERIFY_MIG(x) do { \
		if ((x) == MIG_REPLY_MISMATCH) { \
			_dispatch_set_crash_log_cause_and_message((x), \
					"MIG_REPLY_MISMATCH"); \
			_dispatch_hardware_crash(); \
		} \
	} while (0)

开始往里走!dispatch_semaphore_wait

long
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
{
	// 1. 信号量的值-1
	long value = os_atomic_dec2o(dsema, dsema_value, acquire);
	// 2.1 若>=0,则直接返回0
	if (likely(value >= 0)) {
		return 0;
	}
	// 2.2 若<0,进入慢速处理。见代码块E
	return _dispatch_semaphore_wait_slow(dsema, timeout);
}

代码块E

static long
_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema,
		dispatch_time_t timeout)
{
	long orig;

	_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
	switch (timeout) {
	default:
		if (!_dispatch_sema4_timedwait(&dsema->dsema_sema, timeout)) {
			break;
		}
		//失败,并尝试撤销快速路径对信号量要做的事
	case DISPATCH_TIME_NOW:
		orig = dsema->dsema_value;
		while (orig < 0) {
			if (os_atomic_cmpxchgvw2o(dsema, dsema_value, orig, orig + 1,
					&orig, relaxed)) {
				return _DSEMA4_TIMEOUT();
			}
		}
		// 如果有另一个线程调用dispatch_semaphore_signal()
		//失败,并等待唤醒。
	case DISPATCH_TIME_FOREVER:
		_dispatch_sema4_wait(&dsema->dsema_sema);
		break;
	}
	//返回0
	return 0;
}

GCD 调度组

基本使用

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("label", DISPATCH_QUEUE_CONCURRENT);

    dispatch_group_enter(group);
    dispatch_group_leave(group);

    //内部封装dispatch_group_enter dispatch_group_leave
    dispatch_group_async(group, queue, ^{
    });
    dispatch_group_notify(group, queue, ^{
    });

开始往里走!dispatch_group_enter

void
dispatch_group_enter(dispatch_group_t dg)
{
	// 该值在一个32位宽的原子上递减,这样0 -> -1跃迁的进位不会传播到上32位
	uint32_t old_bits = os_atomic_sub_orig2o(dg, dg_bits,
			DISPATCH_GROUP_VALUE_INTERVAL, acquire);
	uint32_t old_value = old_bits & DISPATCH_GROUP_VALUE_MASK;
	//若为0,则retain(参考下ARC里的retain的意思,就很好理解了)
	if (unlikely(old_value == 0)) {
		_dispatch_retain(dg);
	}
	if (unlikely(old_value == DISPATCH_GROUP_VALUE_MAX)) {
		DISPATCH_CLIENT_CRASH(old_bits,
				"Too many nested calls to dispatch_group_enter()");
	}
}

/*
* 相关宏定义
*/
#define DISPATCH_GROUP_VALUE_MASK       0x00000000fffffffcULL

开始往里走!dispatch_group_leave

void
dispatch_group_leave(dispatch_group_t dg)
{
	// 该值在64位宽的原子上递增,因此-1 -> 0跃迁的进位以原子方式递增。
	uint64_t new_state, old_state = os_atomic_add_orig2o(dg, dg_state,
			DISPATCH_GROUP_VALUE_INTERVAL, release);
	uint32_t old_value = (uint32_t)(old_state & DISPATCH_GROUP_VALUE_MASK);

	if (unlikely(old_value == DISPATCH_GROUP_VALUE_1)) {
		old_state += DISPATCH_GROUP_VALUE_INTERVAL;
		do {
			new_state = old_state;
			if ((old_state & DISPATCH_GROUP_VALUE_MASK) == 0) {
				new_state &= ~DISPATCH_GROUP_HAS_WAITERS;
				new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
			} else {
				// 如果在上面的atomic_add之后再次进入组,我们不能再清除等待者位,因为我们不知道等待者是属于哪一代的
				new_state &= ~DISPATCH_GROUP_HAS_NOTIFS;
			}
			if (old_state == new_state) break;
		} while (unlikely(!os_atomic_cmpxchgv2o(dg, dg_state,
				old_state, new_state, &old_state, relaxed)));
		//看这里就行了!!!!!见代码块F
		return _dispatch_group_wake(dg, old_state, true);
	}

	if (unlikely(old_value == 0)) {
		DISPATCH_CLIENT_CRASH((uintptr_t)old_value,
				"Unbalanced call to dispatch_group_leave()");
	}
}

代码块F

static void
_dispatch_group_wake(dispatch_group_t dg, uint64_t dg_state, bool needs_release)
{
	uint16_t refs = needs_release ? 1 : 0; 

	if (dg_state & DISPATCH_GROUP_HAS_NOTIFS) {/
		dispatch_continuation_t dc, next_dc, tail;

		// 在任何事情被通知/唤醒之前的快照
		dc = os_mpsc_capture_snapshot(os_mpsc(dg, dg_notify), &tail);
		do {
			dispatch_queue_t dsn_queue = (dispatch_queue_t)dc->dc_data;
			next_dc = os_mpsc_pop_snapshot_head(dc, tail, do_next);
			_dispatch_continuation_async(dsn_queue, dc,
					_dispatch_qos_from_pp(dc->dc_priority), dc->dc_flags);
			//release(参考下ARC里的release的意思,就很好理解了)
			_dispatch_release(dsn_queue);
		} while ((dc = next_dc));

		refs++;
	}

	if (dg_state & DISPATCH_GROUP_HAS_WAITERS) {
		_dispatch_wake_by_address(&dg->dg_gen);
	}
//release(参考下ARC里的release的意思,就很好理解了)
	if (refs) _dispatch_release_n(dg, refs);
}

开始往里走!dispatch_group_async

  • 问题
    • 如何异步?
    • 如何通知?
void
dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
		dispatch_block_t db)
{
	//1. 创建一个dispatch_continuation_t结构体并分配地址
	dispatch_continuation_t dc = _dispatch_continuation_alloc();
	uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_GROUP_ASYNC;
	dispatch_qos_t qos;
	// 2. 初始化。见代码块G
	qos = _dispatch_continuation_init(dc, dq, db, 0, dc_flags);
	// 3. 异步执行。见代码块H
	_dispatch_continuation_group_async(dg, dq, dc, qos);
}

/*
* 相关宏定义
*/
// this is set on async or for non event_handler source handlers
#define DC_FLAG_CONSUME					0x004ul
// continuation has a group in dc_data
#define DC_FLAG_GROUP_ASYNC				0x008ul

代码块G

static inline dispatch_qos_t
_dispatch_continuation_init(dispatch_continuation_t dc,
		dispatch_queue_class_t dqu, dispatch_block_t work,
		dispatch_block_flags_t flags, uintptr_t dc_flags)
{
	void *ctxt = _dispatch_Block_copy(work);

	dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
	if (unlikely(_dispatch_block_has_private_data(work))) {
		dc->dc_flags = dc_flags;
		dc->dc_ctxt = ctxt;
		// will initialize all fields but requires dc_flags & dc_ctxt to be set
		//这个函数中的嵌套会走到dispatch_group_leave
		return _dispatch_continuation_init_slow(dc, dqu, flags);
	}

	dispatch_function_t func = _dispatch_Block_invoke(work);
	if (dc_flags & DC_FLAG_CONSUME) {
		func = _dispatch_call_block_and_release;
	}
	//设置优先级
	return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}

static inline dispatch_qos_t
_dispatch_continuation_init_f(dispatch_continuation_t dc,
		dispatch_queue_class_t dqu, void *ctxt, dispatch_function_t f,
		dispatch_block_flags_t flags, uintptr_t dc_flags)
{
	pthread_priority_t pp = 0;
	dc->dc_flags = dc_flags | DC_FLAG_ALLOCATED;
	dc->dc_func = f;
	dc->dc_ctxt = ctxt;
	//在这种情况下,DISPATCH_BLOCK_HAS_PRIORITY意味着不应该传播优先级,只从有优先级的处理程序中获取优先级
	if (!(flags & DISPATCH_BLOCK_HAS_PRIORITY)) {
		pp = _dispatch_priority_propagate();
	}
	_dispatch_continuation_voucher_set(dc, flags);
	return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
}

代码块H

static inline void
_dispatch_continuation_group_async(dispatch_group_t dg, dispatch_queue_t dq,
		dispatch_continuation_t dc, dispatch_qos_t qos)
{
	//******内部实现了enter!!******
	dispatch_group_enter(dg);
	dc->dc_data = dg;
	//异步调用
	_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}

static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
		dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#if DISPATCH_INTROSPECTION
	if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
		_dispatch_trace_item_push(dqu, dc);
	}
#else
	(void)dc_flags;
#endif
	return dx_push(dqu._dq, dc, qos);
}

/*
* 相关宏定义
*/
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

那么这个dx_push就回到了init.c文件中的队列数组

开始往里走!dispatch_group_notify

void
dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
		dispatch_block_t db)
{
	//1. 初始化操作
	dispatch_continuation_t dsn = _dispatch_continuation_alloc();
	_dispatch_continuation_init(dsn, dq, db, 0, DC_FLAG_CONSUME);
	//2. 实际调用。见代码块I
	_dispatch_group_notify(dg, dq, dsn);
}

代码块I

static inline void
_dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
		dispatch_continuation_t dsn)
{
	uint64_t old_state, new_state;
	dispatch_continuation_t prev;

	dsn->dc_data = dq;
	_dispatch_retain(dq);

	prev = os_mpsc_push_update_tail(os_mpsc(dg, dg_notify), dsn, do_next);
	if (os_mpsc_push_was_empty(prev)) _dispatch_retain(dg);
	os_mpsc_push_update_prev(os_mpsc(dg, dg_notify), prev, dsn, do_next);
	if (os_mpsc_push_was_empty(prev)) {
		os_atomic_rmw_loop2o(dg, dg_state, old_state, new_state, release, {
			new_state = old_state | DISPATCH_GROUP_HAS_NOTIFS;
			if ((uint32_t)old_state == 0) {
				os_atomic_rmw_loop_give_up({
					return _dispatch_group_wake(dg, new_state, false);
				});
			}
		});
	}
}

..dbq。累了。回头补!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!