world leader in high performance signal processing
Trace: » blocking_i_o

Blocking I/O and Wait Queues

What happens if you try to read a device that has no data available? The “default” option is to put the calling process to sleep until data is available. The calling process will be blocked and will not return from the read system call until data becomes available. In the meantime, other processes can execute.

This default behaviour can be overridden and the system call can be made to return an error code (-EAGAIN) if no data or space is available.

Note interruptible_sleep_on these functions should be replaced by the newer, race free, wait_event_interruptible

A process can be put to sleep by calling one of the following functions:

  • void interruptible_sleep_on(struct wait_queue_head_t *q)
  • void sleep_on(struct wait_queue_head_t *q);

Later on, the process can be awakened by using one of these functions:

  • void wake_up_interruptible(struct wait_queue_head_t *q);
  • void wake_up(struct wait_queue_head_t *q);

The wait_queue_head_t structure will need to be initialized.

  #include <linux/wait.h>           
  static wait_queue_head_t scmd_inq;

Once set up the wait_queue can be used in the wait queue calls intended for this patr of the process.

The interruptible_sleep_on call will return if a signal is sent to the sleeping process or if another process has issued a wake_up call to the wait queue. The signal can be detected using the following sequence.

  struct wait_queue_head_t wq;     
     // check for a signal         
     if (signal_pending(current))  
        return -ERESTARTSYS;       
     // no signal we were woken up 

Wake up Races

There is an important race condition that can be introduced by these function.

Consider that you are waiting for a condition (like space available in the data buffer for a write). You detect no space and use the interruptible_sleep_on call.

Another process reads from the buffer, create the space and issues a wake_up call to members of the sleeping queue. The race occurs if the wake up call is received after you have detected the no_space condition but before you have added yourself to the wake_up queue.

The following code sequence eliminates this race condition. In addition to sleeping waiting for a wakeup a condition statement is introduced to indicate that the event has happened. Note that since this is a macro and NOT a function, the condition can be an expression.

  #define __wait_event_interruptible(wq, condition, ret)                  \
  do {                                                                    \
          wait_queue_t __wait;                                            \
          init_waitqueue_entry(&__wait, current);                         \
          add_wait_queue(&wq, &__wait);                                   \
          for (;;) {                                                      \
                  set_current_state(TASK_INTERRUPTIBLE);                  \
                  if (condition)                                          \
                          break;                                          \
                  if (!signal_pending(current)) {                         \
                          schedule();                                     \
                          continue;                                       \
                  }                                                       \
                  ret = -ERESTARTSYS;                                     \
                  break;                                                  \
          }                                                               \
          current->state = TASK_RUNNING;       
          remove_wait_queue(&wq, &__wait);                                \
  } while (0)                                                              

NOTE the do {…} while(0) wrapper will cause this code to be executed once.

The scheduler is fooled into thinking that the process is on the wait queue and waiting to be woken up. Meanwhile the process does one last check on the condition before finally using the schedule() call to put the process to sleep.

Any task changing the condition will wake this process up by changing its state to TASK_RUNNING. This will cause a subsequent call to the schedule() function to wake the task up.

New wait event solution

This feature is defined as an inline function in <linux/sched.h>. The blocking code example demonstrates the correct use of this function.

  struct wait_queue_head_t wq;                                              
     int old_count = event_count;                                                                      
     if (wait_event_interruptible(wq ,(old_count!=event_count))) {// NOTE no & on wq
     // check for a signal                                                  
        return -ERESTARTSYS;                                                
     // no signal; we were woken up and event_count had changed.         

Complete Table of Contents/Topics