在zircon中线程同步的机制之一就是event机制。
event机制的主要函数是event_signal 和 event_wait_deadline,其他的一些API 都是这两者的变种
我们先来看看
zx_status_t event_wait_deadline(event_t* e, zx_time_t deadline, bool interruptable) {
return event_wait_worker(e, Deadline::no_slack(deadline), interruptable, 0);
}
这里直接调用event_wait_worker
static zx_status_t event_wait_worker(event_t* e,
const Deadline& deadline,
bool interruptable,
uint signal_mask) {
thread_t* current_thread = get_current_thread();
zx_status_t ret = ZX_OK;
DEBUG_ASSERT(e->magic == EVENT_MAGIC);
DEBUG_ASSERT(!arch_blocking_disallowed());
Guard guard{ThreadLock::Get()};
current_thread->interruptable = interruptable;
#如果在调用event_wait_deadline之前已经调用event_signal了,就走if的分支
if (e->signaled) {
/* signaled, we're going to fall through */
if (e->flags & EVENT_FLAG_AUTOUNSIGNAL) {
/* autounsignal flag lets one thread fall through before unsignaling */
e->signaled = false;
}
} else {
#正常情况下应该走这个case
/* unsignaled, block here */
ret = wait_queue_block_etc(&e->wait, deadline, signal_mask, ResourceOwnership::Normal);
}
current_thread->interruptable = false;
return ret;
}
继续看下wait_queue_block_etc
zx_status_t wait_queue_block_etc(wait_queue_t* wait,
const Deadline& deadline,
uint signal_mask,
ResourceOwnership reason) TA_REQ(thread_lock) {
thread_t* current_thread = get_current_thread();
DEBUG_ASSERT_MAGIC_CHECK(wait);
DEBUG_ASSERT(current_thread->state == THREAD_RUNNING);
DEBUG_ASSERT(arch_ints_disabled());
DEBUG_ASSERT(spin_lock_held(&thread_lock));
if (WAIT_QUEUE_VALIDATION) {
wait_queue_validate_queue(wait);
}
#把当前线程插入到等待队列wait中
zx_status_t res = internal::wait_queue_block_etc_pre(wait, deadline, signal_mask, reason);
if (res != ZX_OK) {
return res;
}
#设置一个timer,超过时间自动将等待的thread唤醒
return internal::wait_queue_block_etc_post(wait, deadline);
}
inline zx_status_t wait_queue_block_etc_post(wait_queue_t* wait,
const Deadline& deadline) TA_REQ(thread_lock) {
thread_t* current_thread = get_current_thread();
timer_t timer;
#如果等待的时间不是无限长,则设置一个timer
// if the deadline is nonzero or noninfinite, set a callback to yank us out of the queue
if (deadline.when() != ZX_TIME_INFINITE) {
timer_init(&timer);
timer_set(&timer, deadline, wait_queue_timeout_handler, (void*)current_thread);
}
ktrace_ptr(TAG_KWAIT_BLOCK, wait, 0, 0);
#让出当前cpu
sched_block();
#当当前thread被接收到event_signal后会从这里开始执行
ktrace_ptr(TAG_KWAIT_UNBLOCK, wait, current_thread->blocked_status, 0);
#已经被唤醒,则取消超时的timer
// we don't really know if the timer fired or not, so it's better safe to try to cancel it
if (deadline.when() != ZX_TIME_INFINITE) {
timer_cancel(&timer);
}
return current_thread->blocked_status;
}
event_signal的实现
int event_signal(event_t* e, bool reschedule) {
#加锁保护,这种锁当函数返回的时候自动销毁
Guard guard{ThreadLock::Get()};
return event_signal_internal(e, reschedule, ZX_OK);
}
event 在signal 之后可以自动unsign 或者主动调用函数event_unsigned
static int event_signal_internal(event_t* e, bool reschedule,
zx_status_t wait_result) TA_REQ(thread_lock) {
DEBUG_ASSERT(e->magic == EVENT_MAGIC);
int wake_count = 0;
if (!e->signaled) {
if (e->flags & EVENT_FLAG_AUTOUNSIGNAL) {
/* try to release one thread and leave unsignaled if successful */
我们以自动unsign为例
if ((wake_count = wait_queue_wake_one(&e->wait, reschedule, wait_result)) <= 0) {
/*
* if we didn't actually find a thread to wake up, go to
* signaled state and let the next call to event_wait
* unsignal the event.
*/
#表示这个event 已经被signed过了
e->signaled = true;
}
#需要主动调用函数event_unsigned
} else {
/* release all threads and remain signaled */
e->signaled = true;
wake_count = wait_queue_wake_all(&e->wait, reschedule, wait_result);
}
}
return wake_count;
}
int wait_queue_wake_all(wait_queue_t* wait, bool reschedule, zx_status_t wait_queue_error) {
thread_t* t;
int ret = 0;
struct list_node list = LIST_INITIAL_VALUE(list);
#从的等待wait中找到正在等待的thread,可能有多个thread在等待
while ((t = wait_queue_peek(wait))) {
internal::wait_queue_dequeue_thread_internal(wait, t, wait_queue_error);
list_add_tail(&list, &t->queue_node);
ret++;
}
DEBUG_ASSERT(ret > 0);
DEBUG_ASSERT(wait->count == 0);
ktrace_ptr(TAG_KWAIT_WAKE, wait, 0, 0);
#唤醒所有的threads,把这些thread放到一个cpu的run queue中,然后根据本地cpu的run queue是否改变来决定是否重新调度
bool local_resched = sched_unblock_list(&list);
if (reschedule && local_resched) {
sched_reschedule();
}
return ret;
}