Handling wait queues

The add_wait_queue( ) function inserts a nonexclusive process in the first position of a wait queue list. The add_wait_queue_exclusive( ) function inserts an exclusive process in the last position of a wait queue list. The remove_wait_queue( ) function removes a process from a wait queue list. The waitqueue_active( ) function checks whether a given wait queue list is empty.

A new wait queue may be defined by using the DECLARE_WAiT_QUEUE_HEAD(name) macro, which statically declares and initializes a new wait queue head variable called name. The init_waitqueue_head( ) function may be used to initialize a wait queue head variable that was allocated dynamically.

A process wishing to wait for a specific condition can invoke any of the functions shown in the following list.

• The sleep_on( ) function operates on the current process:

void sleep on(wait queue head t *q) {

unsigned long flags; wait_queue_t wait; wait.flags = 0; wait.task = current;

current->state = TASK_UNINTERRUPTIBLE; add_wait_queue(q, &wait); schedule( );

remove_wait_queue(q, &wait);

The function sets the state of the current process to task_uninterruptible and inserts it into the specified wait queue. Then it invokes the scheduler, which resumes the execution of another process. When the sleeping process is woken, the scheduler resumes execution of the sleep_on( ) function, which removes the process from the wait queue.

• The interruptible_sleep_on( ) is identical to sleep_on( ), except that it sets the state of the current process to task_interruptible instead of setting it to task_uninterruptible so that the process can also be woken up by receiving a signal.

• The sleep on timeout( ) and interruptible sleep on timeout( ) functions are similar to the previous ones, but they also allow the caller to define a time interval after which the process will be woken up by the kernel. To do this, they invoke the schedule_timeout( ) function instead of schedule( ) (see Section 6.6.2).

• The wait_event and wait_event_interruptible macros, introduced in Linux 2.4, put the calling process to sleep on a wait queue until a given condition is verified. For instance, the wait_event_interruptible(wq,condition) macro essentially yields the following fragment (we have omitted the code related to signal handling and return values on purpose):

init_waitqueue_entry(&_ _wait, current); add_wait_queue(&wq, &_ _wait); for (;;) {

set_current_state(TASK_INTERRUPTIBLE); if (condition)



current->state = TASK_RUNNING;

These macros should be used instead of the older sleep_on( ) and interruptible_sleep_on( ), because the latter functions cannot test a condition and atomically put the process to sleep when the condition is not verified and are thus a well-known source of race conditions.

Notice that any process put to sleep by one of the above functions or macros is nonexclusive. Whenever the kernel wants to insert an exclusive process into a wait queue, it invokes add_wait_queue_exclusive( ) directly.

Processes inserted in a wait queue enter the task_running state by means of one of the following macros: wake up, wake up nr, wake up all, wake up sync, wake up sync nr, wake up interruptible, wake up interruptible nr, wake up interruptible all, wake up interruptible sync, and wake_up_interruptible_sync_nr. We can understand what each of these ten macros does from its name:

• All macros take into consideration sleeping processes in task_interruptible state; if the macro name does not include the string "interruptible," sleeping processes in task_uninterruptible state are also considered.

• All macros wake all nonexclusive processes having the required state (see the previous bullet item).

• The macros whose name include the string "nr" wake a given number of exclusive processes having the required state; this number is a parameter of the macro. The macros whose name include the string "all" wake all exclusive processes having the required state. Finally, the macros whose names don't include "nr" or "all" wake exactly one exclusive process that has the required state.

• The macros whose names don't include the string "sync" check whether the priority of the woken processes is higher than that of the processes currently running in the systems and invoke schedule( ) if necessary. These checks are not made by the macros whose names include the string "sync."

For instance, the wake_up macro is equivalent to the following code fragment:

struct list head *tmp; wait_queue_t *curr;

curr = list_entry(tmp, wait_queue_t, task_list); wake_up_process(curr->task); if (curr->flags) break;

The list_for_each macro scans all items in the doubly linked list of q. For each item, the list_entry macro computes the address of the correspondent wait_queue_t variable.

The task field of this variable stores the pointer to the process descriptor, which is then passed to the wake_up_process( ) function. If the woken process is exclusive, the loop terminates. Since all nonexclusive processes are always at the beginning of the doubly linked list and all exclusive processes are at the end, the function always waken the nonexclusive processes and then wakes one exclusive process, if any exists. HI Notice that awoken processes are not removed from the wait queue. A process could be awoken while the wait condition is still false; in this case, the process may suspend itself again in the same wait queue.

+1 -1

Post a comment