world leader in high performance signal processing
Trace:

Differences

This shows you the differences between two versions of the page.

blocking_i_o [2006/12/08 01:02] (current)
Line 1: Line 1:
 +=====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.
 +
 +<code c>
 +  #include <linux/wait.h>           
 +                                    
 +  static wait_queue_head_t scmd_inq;
 +                                    
 +  init_waitqueue_head(&scmd_inq); 
 +                                   
 +</code>
 +
 +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.
 +
 +<code c>
 +  struct wait_queue_head_t wq;     
 +    init_waitqueue_head(&wq);      
 +                                   
 +     interruptible_sleep_on(&wq);  
 +     // check for a signal         
 +                                   
 +     if (signal_pending(current))  
 +        return -ERESTARTSYS;       
 +     // no signal we were woken up 
 +</code>
 +====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.
 +
 +<code c>
 +  #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)                                                              
 +                                                                           
 +                                                                           
 +</code>
 +
 +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.
 +
 +<code >
 +  struct wait_queue_head_t wq;                                              
 +    init_waitqueue_head(&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.         
 +                                                                            
 +                                                                            
 +</code>
 +