Subversion Repositories HelenOS-historic

Rev

Rev 1502 | Rev 1580 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (C) 2001-2004 Jakub Jermar
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /**
  30.  * @file    waitq.c
  31.  * @brief   Wait queue.
  32.  *
  33.  * Wait queue is the basic synchronization primitive upon which all
  34.  * other synchronization primitives build.
  35.  *
  36.  * It allows threads to wait for an event in first-come, first-served
  37.  * fashion. Conditional operation as well as timeouts and interruptions
  38.  * are supported.
  39.  */
  40.  
  41. #include <synch/waitq.h>
  42. #include <synch/synch.h>
  43. #include <synch/spinlock.h>
  44. #include <proc/thread.h>
  45. #include <proc/scheduler.h>
  46. #include <arch/asm.h>
  47. #include <arch/types.h>
  48. #include <typedefs.h>
  49. #include <time/timeout.h>
  50. #include <arch.h>
  51. #include <context.h>
  52. #include <adt/list.h>
  53.  
  54. static void waitq_timeouted_sleep(void *data);
  55.  
  56. /** Initialize wait queue
  57.  *
  58.  * Initialize wait queue.
  59.  *
  60.  * @param wq Pointer to wait queue to be initialized.
  61.  */
  62. void waitq_initialize(waitq_t *wq)
  63. {
  64.     spinlock_initialize(&wq->lock, "waitq_lock");
  65.     list_initialize(&wq->head);
  66.     wq->missed_wakeups = 0;
  67. }
  68.  
  69. /** Handle timeout during waitq_sleep_timeout() call
  70.  *
  71.  * This routine is called when waitq_sleep_timeout() timeouts.
  72.  * Interrupts are disabled.
  73.  *
  74.  * It is supposed to try to remove 'its' thread from the wait queue;
  75.  * it can eventually fail to achieve this goal when these two events
  76.  * overlap. In that case it behaves just as though there was no
  77.  * timeout at all.
  78.  *
  79.  * @param data Pointer to the thread that called waitq_sleep_timeout().
  80.  */
  81. void waitq_timeouted_sleep(void *data)
  82. {
  83.     thread_t *t = (thread_t *) data;
  84.     waitq_t *wq;
  85.     bool do_wakeup = false;
  86.  
  87.     spinlock_lock(&threads_lock);
  88.     if (!thread_exists(t))
  89.         goto out;
  90.  
  91. grab_locks:
  92.     spinlock_lock(&t->lock);
  93.     if ((wq = t->sleep_queue)) {        /* assignment */
  94.         if (!spinlock_trylock(&wq->lock)) {
  95.             spinlock_unlock(&t->lock);
  96.             goto grab_locks;    /* avoid deadlock */
  97.         }
  98.  
  99.         list_remove(&t->wq_link);
  100.         t->saved_context = t->sleep_timeout_context;
  101.         do_wakeup = true;
  102.        
  103.         spinlock_unlock(&wq->lock);
  104.         t->sleep_queue = NULL;
  105.     }
  106.    
  107.     t->timeout_pending = false;
  108.     spinlock_unlock(&t->lock);
  109.    
  110.     if (do_wakeup)
  111.         thread_ready(t);
  112.  
  113. out:
  114.     spinlock_unlock(&threads_lock);
  115. }
  116.  
  117. /** Interrupt sleeping thread.
  118.  *
  119.  * This routine attempts to interrupt a thread from its sleep in a waitqueue.
  120.  * If the thread is not found sleeping, no action is taken.
  121.  *
  122.  * @param t Thread to be interrupted.
  123.  */
  124. void waitq_interrupt_sleep(thread_t *t)
  125. {
  126.     waitq_t *wq;
  127.     bool do_wakeup = false;
  128.     ipl_t ipl;
  129.  
  130.     ipl = interrupts_disable();
  131.     spinlock_lock(&threads_lock);
  132.     if (!thread_exists(t))
  133.         goto out;
  134.  
  135. grab_locks:
  136.     spinlock_lock(&t->lock);
  137.     if ((wq = t->sleep_queue)) {        /* assignment */
  138.         if (!(t->sleep_interruptible)) {
  139.             /*
  140.              * The sleep cannot be interrupted.
  141.              */
  142.             spinlock_unlock(&t->lock);
  143.             goto out;
  144.         }
  145.            
  146.         if (!spinlock_trylock(&wq->lock)) {
  147.             spinlock_unlock(&t->lock);
  148.             goto grab_locks;    /* avoid deadlock */
  149.         }
  150.  
  151.         list_remove(&t->wq_link);
  152.         t->saved_context = t->sleep_interruption_context;
  153.         do_wakeup = true;
  154.        
  155.         spinlock_unlock(&wq->lock);
  156.         t->sleep_queue = NULL;
  157.     }
  158.     spinlock_unlock(&t->lock);
  159.  
  160.     if (do_wakeup)
  161.         thread_ready(t);
  162.  
  163. out:
  164.     spinlock_unlock(&threads_lock);
  165.     interrupts_restore(ipl);
  166. }
  167.  
  168. /** Sleep until either wakeup, timeout or interruption occurs
  169.  *
  170.  * This is a sleep implementation which allows itself to time out or to be
  171.  * interrupted from the sleep, restoring a failover context.
  172.  *
  173.  * Sleepers are organised in a FIFO fashion in a structure called wait queue.
  174.  *
  175.  * This function is really basic in that other functions as waitq_sleep()
  176.  * and all the *_timeout() functions use it.
  177.  *
  178.  * @param wq Pointer to wait queue.
  179.  * @param usec Timeout in microseconds.
  180.  * @param flags Specify mode of the sleep.
  181.  *
  182.  * The sleep can be interrupted only if the
  183.  * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
  184.  
  185.  * If usec is greater than zero, regardless of the value of the
  186.  * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either timeout,
  187.  * interruption or wakeup comes.
  188.  *
  189.  * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags, the call
  190.  * will not return until wakeup or interruption comes.
  191.  *
  192.  * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is set in flags, the call will
  193.  * immediately return, reporting either success or failure.
  194.  *
  195.  * @return  Returns one of: ESYNCH_WOULD_BLOCK, ESYNCH_TIMEOUT, ESYNCH_INTERRUPTED,
  196.  *          ESYNCH_OK_ATOMIC, ESYNCH_OK_BLOCKED.
  197.  *
  198.  * @li ESYNCH_WOULD_BLOCK means that the sleep failed because at the time
  199.  * of the call there was no pending wakeup.
  200.  *
  201.  * @li ESYNCH_TIMEOUT means that the sleep timed out.
  202.  *
  203.  * @li ESYNCH_INTERRUPTED means that somebody interrupted the sleeping thread.
  204.  *
  205.  * @li ESYNCH_OK_ATOMIC means that the sleep succeeded and that there was
  206.  * a pending wakeup at the time of the call. The caller was not put
  207.  * asleep at all.
  208.  *
  209.  * @li ESYNCH_OK_BLOCKED means that the sleep succeeded; the full sleep was
  210.  * attempted.
  211.  */
  212. int waitq_sleep_timeout(waitq_t *wq, __u32 usec, int flags)
  213. {
  214.     ipl_t ipl;
  215.     int rc;
  216.    
  217.     ipl = waitq_sleep_prepare(wq);
  218.     rc = waitq_sleep_timeout_unsafe(wq, usec, flags);
  219.     waitq_sleep_finish(wq, rc, ipl);
  220.     return rc;
  221. }
  222.  
  223. /** Prepare to sleep in a waitq.
  224.  *
  225.  * This function will return holding the lock of the wait queue
  226.  * and interrupts disabled.
  227.  *
  228.  * @param wq Wait queue.
  229.  *
  230.  * @return Interrupt level as it existed on entry to this function.
  231.  */
  232. ipl_t waitq_sleep_prepare(waitq_t *wq)
  233. {
  234.     ipl_t ipl;
  235.    
  236. restart:
  237.     ipl = interrupts_disable();
  238.  
  239.     if (THREAD) {   /* needed during system initiailzation */
  240.         /*
  241.          * Busy waiting for a delayed timeout.
  242.          * This is an important fix for the race condition between
  243.          * a delayed timeout and a next call to waitq_sleep_timeout().
  244.          * Simply, the thread is not allowed to go to sleep if
  245.          * there are timeouts in progress.
  246.          */
  247.         spinlock_lock(&THREAD->lock);
  248.         if (THREAD->timeout_pending) {
  249.             spinlock_unlock(&THREAD->lock);
  250.             interrupts_restore(ipl);
  251.             goto restart;
  252.         }
  253.         spinlock_unlock(&THREAD->lock);
  254.     }
  255.                                                    
  256.     spinlock_lock(&wq->lock);
  257.     return ipl;
  258. }
  259.  
  260. /** Finish waiting in a wait queue.
  261.  *
  262.  * This function restores interrupts to the state that existed prior
  263.  * to the call to waitq_sleep_prepare(). If necessary, the wait queue
  264.  * lock is released.
  265.  *
  266.  * @param wq Wait queue.
  267.  * @param rc Return code of waitq_sleep_timeout_unsafe().
  268.  * @param ipl Interrupt level returned by waitq_sleep_prepare().
  269.  */
  270. void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl)
  271. {
  272.     switch (rc) {
  273.     case ESYNCH_WOULD_BLOCK:
  274.     case ESYNCH_OK_ATOMIC:
  275.         spinlock_unlock(&wq->lock);
  276.         break;
  277.     default:
  278.         break;
  279.     }
  280.     interrupts_restore(ipl);
  281. }
  282.  
  283. /** Internal implementation of waitq_sleep_timeout().
  284.  *
  285.  * This function implements logic of sleeping in a wait queue.
  286.  * This call must be preceeded by a call to waitq_sleep_prepare()
  287.  * and followed by a call to waitq_slee_finish().
  288.  *
  289.  * @param wq See waitq_sleep_timeout().
  290.  * @param usec See waitq_sleep_timeout().
  291.  * @param flags See waitq_sleep_timeout().
  292.  *
  293.  * @return See waitq_sleep_timeout().
  294.  */
  295. int waitq_sleep_timeout_unsafe(waitq_t *wq, __u32 usec, int flags)
  296. {
  297.     /* checks whether to go to sleep at all */
  298.     if (wq->missed_wakeups) {
  299.         wq->missed_wakeups--;
  300.         return ESYNCH_OK_ATOMIC;
  301.     }
  302.     else {
  303.         if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) {
  304.             /* return immediatelly instead of going to sleep */
  305.             return ESYNCH_WOULD_BLOCK;
  306.         }
  307.     }
  308.    
  309.     /*
  310.      * Now we are firmly decided to go to sleep.
  311.      */
  312.     spinlock_lock(&THREAD->lock);
  313.  
  314.     if (THREAD->interrupted) {
  315.         spinlock_unlock(&THREAD->lock);
  316.         spinlock_unlock(&wq->lock);
  317.         return ESYNCH_INTERRUPTED;
  318.     }
  319.  
  320.     if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
  321.         /*
  322.          * Set context that will be restored if the sleep
  323.          * of this thread is ever interrupted.
  324.          */
  325.         THREAD->sleep_interruptible = true;
  326.         if (!context_save(&THREAD->sleep_interruption_context)) {
  327.             /* Short emulation of scheduler() return code. */
  328.             spinlock_unlock(&THREAD->lock);
  329.             return ESYNCH_INTERRUPTED;
  330.         }
  331.     } else {
  332.         THREAD->sleep_interruptible = false;
  333.     }
  334.  
  335.     if (usec) {
  336.         /* We use the timeout variant. */
  337.         if (!context_save(&THREAD->sleep_timeout_context)) {
  338.             /* Short emulation of scheduler() return code. */
  339.             spinlock_unlock(&THREAD->lock);
  340.             return ESYNCH_TIMEOUT;
  341.         }
  342.         THREAD->timeout_pending = true;
  343.         timeout_register(&THREAD->sleep_timeout, (__u64) usec, waitq_timeouted_sleep, THREAD);
  344.     }
  345.  
  346.     list_append(&THREAD->wq_link, &wq->head);
  347.  
  348.     /*
  349.      * Suspend execution.
  350.      */
  351.     THREAD->state = Sleeping;
  352.     THREAD->sleep_queue = wq;
  353.  
  354.     spinlock_unlock(&THREAD->lock);
  355.  
  356.     scheduler();    /* wq->lock is released in scheduler_separated_stack() */
  357.    
  358.     return ESYNCH_OK_BLOCKED;
  359. }
  360.  
  361.  
  362. /** Wake up first thread sleeping in a wait queue
  363.  *
  364.  * Wake up first thread sleeping in a wait queue.
  365.  * This is the SMP- and IRQ-safe wrapper meant for
  366.  * general use.
  367.  *
  368.  * Besides its 'normal' wakeup operation, it attempts
  369.  * to unregister possible timeout.
  370.  *
  371.  * @param wq Pointer to wait queue.
  372.  * @param all If this is non-zero, all sleeping threads
  373.  *        will be woken up and missed count will be zeroed.
  374.  */
  375. void waitq_wakeup(waitq_t *wq, bool all)
  376. {
  377.     ipl_t ipl;
  378.  
  379.     ipl = interrupts_disable();
  380.     spinlock_lock(&wq->lock);
  381.  
  382.     _waitq_wakeup_unsafe(wq, all);
  383.  
  384.     spinlock_unlock(&wq->lock);
  385.     interrupts_restore(ipl);   
  386. }
  387.  
  388. /** Internal SMP- and IRQ-unsafe version of waitq_wakeup()
  389.  *
  390.  * This is the internal SMP- and IRQ-unsafe version
  391.  * of waitq_wakeup(). It assumes wq->lock is already
  392.  * locked and interrupts are already disabled.
  393.  *
  394.  * @param wq Pointer to wait queue.
  395.  * @param all If this is non-zero, all sleeping threads
  396.  *        will be woken up and missed count will be zeroed.
  397.  */
  398. void _waitq_wakeup_unsafe(waitq_t *wq, bool all)
  399. {
  400.     thread_t *t;
  401.  
  402. loop:  
  403.     if (list_empty(&wq->head)) {
  404.         wq->missed_wakeups++;
  405.         if (all)
  406.             wq->missed_wakeups = 0;
  407.         return;
  408.     }
  409.  
  410.     t = list_get_instance(wq->head.next, thread_t, wq_link);
  411.    
  412.     list_remove(&t->wq_link);
  413.     spinlock_lock(&t->lock);
  414.     if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
  415.         t->timeout_pending = false;
  416.     t->sleep_queue = NULL;
  417.     spinlock_unlock(&t->lock);
  418.  
  419.     thread_ready(t);
  420.  
  421.     if (all)
  422.         goto loop;
  423. }
  424.