/* If we were woken (and unqueued), we succeeded, whatever. */
ret = 0;
/* unqueue_me() drops q.key ref */
if (!unqueue_me(&q)) {
/* unqueue_me返回值情况 */
/* 1 - if the futex_q was still queued (and we removed unqueued it); */
/* 0 - if the futex_q was already removed by the waking thread(发送信号唤醒的情况) */
goto out; // 正常等到信号后返回走这里
}
ret = -ETIMEDOUT;
if (to && !to->task) {
goto out;
}
/*
* We expect signal_pending(current), but we might be the
* victim of a spurious wakeup as well.
*/
if (!signal_pending(current)) {
trace_printk("retry\n");
goto retry;
}
ret = -ERESTARTSYS;
if (!abs_time) {
goto out;
}
restart = ¤t_thread_info()->restart_block;
restart->fn = futex_wait_restart;
restart->futex.uaddr = uaddr;
restart->futex.val = val;
restart->futex.time = abs_time->tv64;
restart->futex.bitset = bitset;
restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;
ret = -ERESTART_RESTARTBLOCK;
out:
if (to) {
hrtimer_cancel(&to->timer);
destroy_hrtimer_on_stack(&to->timer);
}
return ret; // 正常返回值为0
}
kernel\kernel\ Futex.c
static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
struct hrtimer_sleeper *timeout)
{
/*
* The task state is guaranteed to be set before another task can
* wake it. set_current_state() is implemented using set_mb() and
* queue_me() calls spin_unlock() upon completion, both serializing
* access to the hash list and forcing another memory barrier.
*/
set_current_state(TASK_INTERRUPTIBLE);
queue_me(q, hb);
/* Arm the timer */
if (timeout) {
hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
if (!hrtimer_active(&timeout->timer))
timeout->task = NULL;
}
/*
* If we have been removed from the hash list, then another task
* has tried to wake us, and we can skip the call to schedule().
*/
if (likely(!plist_node_empty(&q->list))) {
/*
* If the timer has already expired, current will already be
* flagged for rescheduling. Only call schedule if there
* is no timeout, or if it has yet to expire.
*/
if (!timeout || timeout->task) {
freezable_schedule();
}
}
__set_current_state(TASK_RUNNING);
}
如果该等待线程使用timer_create创建了定时器,并且创建的定时器超时是给当前线程发送信号(timer_create的创建在第4节分析),当定时器超时后,就会把当前线程设置为可运行的状态,等待系统调度运行。若这时该线程刚好调用了wait在等待信号,由于该线程已经被设置为可运行状态,当调度到该线程运行时,futex_wait_queue_me函数的freezable_schedule()就会返回,这时futex_wait返回流程与返回值都与正常接收到信号时返回的不一样,如下面代码标示:
kernel\kernel\ Futex.c