Wait Queue#
wait_queue in the Linux kernel is a data structure to manage threads that are waiting for some condition to become true.
workflow for using wait_queue is to first initialize it, and sleep the
thread using family of wait_event*. However driver writers should almost always use the _interruptible instances of these functions/macros.
Please read the lwn.net explaination on wait queue.
Code explanation#
- This code uses
wait_queue_head_tto let the thread sleep until there is some data availble to read from or space to write write to with blocking.
- This code uses
- Read/Write methods are blocking in nature if there is no data to read/no space to write.
- However if file open with
O_NONBLOCK. I/O is returned immediately instead of blocking.
- Reading the
Nbytes from the buffer reset thef_posto start position and wakes up the write wait queue. Read call sleeps if there is no data to read.
- Reading the
- Similarly Write call wake up read wake queue if it has wrote some bytes to buffer, sleeps if there is no space to write.
- Mutex is used to prevent data corruption.
Simple usecase#
see: example
Read Method#
| |
this method has similar workflow as poll blog read method. Which sleeps when no data to read, which is done by wait_event*, and Wake up the write call path using wake_up*. The thread running this read method is waken up by write call.
Write Method#
| |
Same as read method, but vice-versa in nature.
Advanced usecase#
this usecase uses the underlying mechanism of the wait_event* function.
see: example
Read Method#
| |
Here read call first check if there is any data to read. If not it define and initilizes a wait_queue_entry using DEFINE_WAIT. About Mutex locks and O_NONBLOCK already discussed in poll method.
Now it calls prepare_to_wait, which add the wait_queue_entry node into the queue of wait_queue_head. and checks the condition again then yield the cpu, so schedular can run other tasks.
And when this thread is waked up by another thread by calling wake_up* family function. It calls finish_wait, which basically set the process state to TASK_RUNNING and remove the wait_queue_entry node from the queue.
At the end, Now buffer has some space availble to write. So it wakes up the thread running the write call.
Write Method#
this method is also similar to read method. But vice-versa in nature.
Note: You can see that the
prepare_to_waitfunction does the initial wait queue setup, whilefinish_waitcleans up. However for the default cases, whenwait_queue_entryis initlized byDEFINE_WAIT. It attaches itsfuncmember toautoremove_wake_functionfunction. Which is eventually called bywake_up*function and if the function is successfully woken up. It removes thewait_queue_entrynode from the queue.
| |
Here it initilizes the wait_queue_entry with autoremove_wake_function function.
And if you underlying core wake function __wake_up_common. which calls
| |
so now autoremove_wake_function function wake up the process.
| |
If the function is woken up then it removes the node from queue.
And if you check the finish_wait
| |
which set the current running process state to TASK_RUNNING and try to remove the node from queue, if it is not removed (in case when custom wait_queue_func_t func is used).