Shu Suzuki
Shu Suzuki

Reputation: 1184

Wait queue and race condition

I'm reading "Linux Kernel Development" by Robert Love and found the code below to wait for an event.

DEFINE_WAIT(wait);

add_wait_queue(q, &wait); 
while (!condition) {
    // What happens if condition is changed and wake_up() is called here ?
    prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE); 
    if (signal_pending(current))
        /* handle signal */ 

    schedule();
}

finish_wait(&q, &wait);

My question is as in the code above. What happens if condition is changed and wake_up() is called after condition check but before prepare_to_wait? My (probably wrong) interpretation here is that because prepare_to_wait makes the thread TASK_INTERRUPTIBLE and calls schedule() after the condition is changed, it sleeps forever (unless it gets signaled or another wake_up is called).

Upvotes: 2

Views: 841

Answers (1)

Tsyvarev
Tsyvarev

Reputation: 65870

Yes, this code is actually raced, between prepare_to_wait and schedule calls it should be a check for the condition (and breaking if it is satisfied).

Funny enough, in the following description the book refers (at page 60) to the implementation of function inotify_read() in fs/notify/inotify/inotify_user.c file:

DEFINE_WAIT(wait);
...
while (1) {
  prepare_to_wait(&group->notification_waitq,
    &wait,
    TASK_INTERRUPTIBLE);

  if (<condition>) // very simplified form of checks
    break;
  if (signal_pending(current))
    break;

  schedule();
}
finish_wait(&group->notification_waitq, &wait);
...

which according to the author "follows the pattern":

This function follows the pattern laid out in our example. The main difference is that it checks for the condition in the body of the while() loop, instead of in the while() statement itself. This is because checking the condition is complicated and requires grabbing locks. The loop is terminated via break.

However, that code exhibits another pattern, which checks the condition between prepare_to_wait and schedule call. And that code is actually correct (race-free). Also that code doesn't use add_wait_queue, which is superfluous in presence of prepare_to_wait.

In another book of the same author, Linux Driver Development (3d revision), usage of wait queues seems to be more accurate. See e.g. chapter 6, Advanced Char Driver Operations.

Upvotes: 5

Related Questions