Contrary to expectations, the classic functions sleep_on() and
interruptible_sleep_on() were not removed in the 2.5 series. It
seems that they are still needed in a few places where (1) taking them
out is quite a bit of work, and (2) they are actually used in a way
that is safe. Most authors of kernel code should, however, pretend that
those functions no longer exist. There are very few situations in which
they can be used safely, and better alternatives exist.
wait_event() and friends
Most of those alternatives have been around since 2.3 or earlier. In many
situations, one can use the wait_event() macros:
DECLARE_WAIT_QUEUE_HEAD(queue);
wait_event(queue, condition);
int wait_event_interruptible (queue, condition);
These macros work the same as in 2.4: condition is a boolean
condition which will be tested within the macro; the wait will end when the
condition evaluates true.
It is worth noting that these macros have moved from
<linux/sched.h> to <linux/wait.h>, which
seems a more sensible place for them. There is also a new one:
int wait_event_interruptible_timeout(queue, condition, timeout);
which will terminate the wait if the timeout expires.
prepare_to_wait() and friends
In many situations, wait_event() does not provide enough
flexibility - often because tricky locking is involved.
The alternative in those cases has been to do a full "manual"
sleep, which involves the following steps (shown here in a sort of
pseudocode, of course):
DECLARE_WAIT_QUEUE_HEAD(queue);
DECLARE_WAITQUEUE(wait, current);
for (;;) {
add_wait_queue(&queue, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (condition)
break;
schedule();
remove_wait_queue(&queue, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
}
set_current_state(TASK_RUNNING);
A sleep coded in this manner is safe against missed wakeups. It is also a
fair amount of error-prone boilerplate code for a very common situation.
In 2.6, a set of helper functions has been added which makes this task
easier. The modern equivalent of the above code would look like:
DECLARE_WAIT_QUEUE_HEAD(queue);
DEFINE_WAIT(wait);
while (! condition) {
prepare_to_wait(&queue, &wait, TASK_INTERRUPTIBLE);
if (! condition)
schedule();
finish_wait(&queue, &wait)
}
prepare_to_wait_exclusive() should be used when an exclusive wait
is needed. Note that the new macro DEFINE_WAIT() is used here,
rather than DECLARE_WAITQUEUE(). The former should be used when
the wait queue entry is to be used with prepare_to_wait(), and
should probably not be used in other situations unless you
understand what it is doing (which we'll get into next).
Wait queue changes
In addition to being more concise and less error prone,
prepare_to_wait() can yield higher performance in situations where
wakeups happen frequently. This improvement is obtained by causing the
process to be removed from the wait queue immediately upon wakeup; that
removal keeps the process from seeing multiple wakeups if it doesn't
otherwise get around to removing itself for a bit.
The automatic wait queue removal is implemented via a change in the wait
queue mechanism. Each wait queue entry now includes its own "wake
function," whose job it is to handle wakeups. The default wake function
(which has the surprising name default_wake_function()), behaves
in the customary way: it sets the waiting task into the
TASK_RUNNING state and handles scheduling issues. The
DEFINE_WAIT() macro creates a wait queue entry with a different
wake function, autoremove_wake_function(), which automatically
takes the newly-awakened task out of the queue.
And that, of course, is how DEFINE_WAIT() differs from
DECLARE_WAITQUEUE() - they set different wake functions. How the
semantics of the two differ is not immediately evident from their names,
but that's how it goes. (The new runtime initialization function
init_wait() differs from the older init_waitqueue_entry()
in exactly the same way).
If need be, you can define your own wake function - though the need for
that should be quite rare (about the only user, currently, is the support
code for the epoll() system calls). The wake function has this
prototype:
typedef int (*wait_queue_func_t)(wait_queue_t *wait,
unsigned mode, int sync);
A wait queue entry can be given a different wakeup function with:
void init_waitqueue_func_entry(wait_queue_t *queue,
wait_queue_func_t func);
One other change that most programmers won't notice: a bunch of wait queue
cruft from 2.4 (two different kinds of wait queue lock, wait queue
debugging) has been removed from 2.6.
Post a comment
|