Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified kernel/generic/src/synch/waitq.c

    r6c4a56f rdf58e44  
    3333/**
    3434 * @file
    35  * @brief       Wait queue.
     35 * @brief Wait queue.
    3636 *
    3737 * Wait queue is the basic synchronization primitive upon which all
     
    4141 * fashion. Conditional operation as well as timeouts and interruptions
    4242 * are supported.
     43 *
    4344 */
    4445
     
    4950#include <proc/scheduler.h>
    5051#include <arch/asm.h>
    51 #include <arch/types.h>
     52#include <typedefs.h>
    5253#include <time/timeout.h>
    5354#include <arch.h>
    5455#include <context.h>
    5556#include <adt/list.h>
    56 
    57 static void waitq_sleep_timed_out(void *data);
     57#include <arch/cycle.h>
     58
     59static void waitq_sleep_timed_out(void *);
    5860
    5961/** Initialize wait queue
     
    6163 * Initialize wait queue.
    6264 *
    63  * @param wq            Pointer to wait queue to be initialized.
     65 * @param wq Pointer to wait queue to be initialized.
     66 *
    6467 */
    6568void waitq_initialize(waitq_t *wq)
    6669{
    67         spinlock_initialize(&wq->lock, "waitq_lock");
     70        irq_spinlock_initialize(&wq->lock, "wq.lock");
    6871        list_initialize(&wq->head);
    6972        wq->missed_wakeups = 0;
     
    8083 * timeout at all.
    8184 *
    82  * @param data          Pointer to the thread that called waitq_sleep_timeout().
     85 * @param data Pointer to the thread that called waitq_sleep_timeout().
     86 *
    8387 */
    8488void waitq_sleep_timed_out(void *data)
    8589{
    86         thread_t *t = (thread_t *) data;
    87         waitq_t *wq;
     90        thread_t *thread = (thread_t *) data;
    8891        bool do_wakeup = false;
    8992        DEADLOCK_PROBE_INIT(p_wqlock);
    90 
    91         spinlock_lock(&threads_lock);
    92         if (!thread_exists(t))
     93       
     94        irq_spinlock_lock(&threads_lock, false);
     95        if (!thread_exists(thread))
    9396                goto out;
    94 
     97       
    9598grab_locks:
    96         spinlock_lock(&t->lock);
    97         if ((wq = t->sleep_queue)) {            /* assignment */
    98                 if (!spinlock_trylock(&wq->lock)) {
    99                         spinlock_unlock(&t->lock);
     99        irq_spinlock_lock(&thread->lock, false);
     100       
     101        waitq_t *wq;
     102        if ((wq = thread->sleep_queue)) {  /* Assignment */
     103                if (!irq_spinlock_trylock(&wq->lock)) {
     104                        irq_spinlock_unlock(&thread->lock, false);
    100105                        DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
    101                         goto grab_locks;        /* avoid deadlock */
    102                 }
    103 
    104                 list_remove(&t->wq_link);
    105                 t->saved_context = t->sleep_timeout_context;
     106                        /* Avoid deadlock */
     107                        goto grab_locks;
     108                }
     109               
     110                list_remove(&thread->wq_link);
     111                thread->saved_context = thread->sleep_timeout_context;
    106112                do_wakeup = true;
    107                 t->sleep_queue = NULL;
    108                 spinlock_unlock(&wq->lock);
    109         }
    110        
    111         t->timeout_pending = false;
    112         spinlock_unlock(&t->lock);
     113                thread->sleep_queue = NULL;
     114                irq_spinlock_unlock(&wq->lock, false);
     115        }
     116       
     117        thread->timeout_pending = false;
     118        irq_spinlock_unlock(&thread->lock, false);
    113119       
    114120        if (do_wakeup)
    115                 thread_ready(t);
    116 
     121                thread_ready(thread);
     122       
    117123out:
    118         spinlock_unlock(&threads_lock);
     124        irq_spinlock_unlock(&threads_lock, false);
    119125}
    120126
    121127/** Interrupt sleeping thread.
    122128 *
    123  * This routine attempts to interrupt a thread from its sleep in a waitqueue.
    124  * If the thread is not found sleeping, no action is taken.
    125  *
    126  * @param t             Thread to be interrupted.
    127  */
    128 void waitq_interrupt_sleep(thread_t *t)
    129 {
     129 * This routine attempts to interrupt a thread from its sleep in
     130 * a waitqueue. If the thread is not found sleeping, no action
     131 * is taken.
     132 *
     133 * The threads_lock must be already held and interrupts must be
     134 * disabled upon calling this function.
     135 *
     136 * @param thread Thread to be interrupted.
     137 *
     138 */
     139void waitq_interrupt_sleep(thread_t *thread)
     140{
     141        bool do_wakeup = false;
     142        DEADLOCK_PROBE_INIT(p_wqlock);
     143       
     144        /*
     145         * The thread is quaranteed to exist because
     146         * threads_lock is held.
     147         */
     148       
     149grab_locks:
     150        irq_spinlock_lock(&thread->lock, false);
     151       
    130152        waitq_t *wq;
    131         bool do_wakeup = false;
    132         ipl_t ipl;
    133         DEADLOCK_PROBE_INIT(p_wqlock);
    134 
    135         ipl = interrupts_disable();
    136         spinlock_lock(&threads_lock);
    137         if (!thread_exists(t))
    138                 goto out;
    139 
    140 grab_locks:
    141         spinlock_lock(&t->lock);
    142         if ((wq = t->sleep_queue)) {            /* assignment */
    143                 if (!(t->sleep_interruptible)) {
     153        if ((wq = thread->sleep_queue)) {  /* Assignment */
     154                if (!(thread->sleep_interruptible)) {
    144155                        /*
    145156                         * The sleep cannot be interrupted.
    146157                         */
    147                         spinlock_unlock(&t->lock);
    148                         goto out;
    149                 }
    150                        
    151                 if (!spinlock_trylock(&wq->lock)) {
    152                         spinlock_unlock(&t->lock);
     158                        irq_spinlock_unlock(&thread->lock, false);
     159                        return;
     160                }
     161               
     162                if (!irq_spinlock_trylock(&wq->lock)) {
     163                        /* Avoid deadlock */
     164                        irq_spinlock_unlock(&thread->lock, false);
    153165                        DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
    154                         goto grab_locks;        /* avoid deadlock */
    155                 }
    156 
    157                 if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
    158                         t->timeout_pending = false;
    159 
    160                 list_remove(&t->wq_link);
    161                 t->saved_context = t->sleep_interruption_context;
     166                        goto grab_locks;
     167                }
     168               
     169                if ((thread->timeout_pending) &&
     170                    (timeout_unregister(&thread->sleep_timeout)))
     171                        thread->timeout_pending = false;
     172               
     173                list_remove(&thread->wq_link);
     174                thread->saved_context = thread->sleep_interruption_context;
    162175                do_wakeup = true;
    163                 t->sleep_queue = NULL;
    164                 spinlock_unlock(&wq->lock);
    165         }
    166         spinlock_unlock(&t->lock);
    167 
     176                thread->sleep_queue = NULL;
     177                irq_spinlock_unlock(&wq->lock, false);
     178        }
     179       
     180        irq_spinlock_unlock(&thread->lock, false);
     181       
    168182        if (do_wakeup)
    169                 thread_ready(t);
    170 
    171 out:
    172         spinlock_unlock(&threads_lock);
    173         interrupts_restore(ipl);
     183                thread_ready(thread);
    174184}
    175185
     
    179189 * is sleeping interruptibly.
    180190 *
    181  * @param wq            Pointer to wait queue.
     191 * @param wq Pointer to wait queue.
     192 *
    182193 */
    183194void waitq_unsleep(waitq_t *wq)
    184195{
    185         ipl_t ipl;
    186 
    187         ipl = interrupts_disable();
    188         spinlock_lock(&wq->lock);
    189 
     196        irq_spinlock_lock(&wq->lock, true);
     197       
    190198        if (!list_empty(&wq->head)) {
    191                 thread_t *t;
    192                
    193                 t = list_get_instance(wq->head.next, thread_t, wq_link);
    194                 spinlock_lock(&t->lock);
    195                 ASSERT(t->sleep_interruptible);
    196                 if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
    197                         t->timeout_pending = false;
    198                 list_remove(&t->wq_link);
    199                 t->saved_context = t->sleep_interruption_context;
    200                 t->sleep_queue = NULL;
    201                 spinlock_unlock(&t->lock);
    202                 thread_ready(t);
    203         }
    204 
    205         spinlock_unlock(&wq->lock);
    206         interrupts_restore(ipl);
    207 }
     199                thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link);
     200               
     201                irq_spinlock_lock(&thread->lock, false);
     202               
     203                ASSERT(thread->sleep_interruptible);
     204               
     205                if ((thread->timeout_pending) &&
     206                    (timeout_unregister(&thread->sleep_timeout)))
     207                        thread->timeout_pending = false;
     208               
     209                list_remove(&thread->wq_link);
     210                thread->saved_context = thread->sleep_interruption_context;
     211                thread->sleep_queue = NULL;
     212               
     213                irq_spinlock_unlock(&thread->lock, false);
     214                thread_ready(thread);
     215        }
     216       
     217        irq_spinlock_unlock(&wq->lock, true);
     218}
     219
     220#define PARAM_NON_BLOCKING(flags, usec) \
     221        (((flags) & SYNCH_FLAGS_NON_BLOCKING) && ((usec) == 0))
    208222
    209223/** Sleep until either wakeup, timeout or interruption occurs
     
    217231 * and all the *_timeout() functions use it.
    218232 *
    219  * @param wq            Pointer to wait queue.
    220  * @param usec          Timeout in microseconds.
    221  * @param flags         Specify mode of the sleep.
     233 * @param wq    Pointer to wait queue.
     234 * @param usec  Timeout in microseconds.
     235 * @param flags Specify mode of the sleep.
    222236 *
    223237 * The sleep can be interrupted only if the
    224238 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
    225  * 
     239 *
    226240 * If usec is greater than zero, regardless of the value of the
    227241 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either
    228  * timeout, interruption or wakeup comes. 
     242 * timeout, interruption or wakeup comes.
    229243 *
    230244 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags,
     
    234248 * call will immediately return, reporting either success or failure.
    235249 *
    236  * @return              Returns one of ESYNCH_WOULD_BLOCK, ESYNCH_TIMEOUT,
    237  *                      ESYNCH_INTERRUPTED, ESYNCH_OK_ATOMIC and
    238  *                      ESYNCH_OK_BLOCKED.
    239  *
    240  * @li  ESYNCH_WOULD_BLOCK means that the sleep failed because at the time of
    241  *      the call there was no pending wakeup.
    242  *
    243  * @li  ESYNCH_TIMEOUT means that the sleep timed out.
    244  *
    245  * @li  ESYNCH_INTERRUPTED means that somebody interrupted the sleeping thread.
    246  *
    247  * @li  ESYNCH_OK_ATOMIC means that the sleep succeeded and that there was
    248  *      a pending wakeup at the time of the call. The caller was not put
    249  *      asleep at all.
    250  *
    251  * @li  ESYNCH_OK_BLOCKED means that the sleep succeeded; the full sleep was
    252  *      attempted.
    253  */
    254 int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, int flags)
    255 {
    256         ipl_t ipl;
    257         int rc;
    258        
    259         ipl = waitq_sleep_prepare(wq);
    260         rc = waitq_sleep_timeout_unsafe(wq, usec, flags);
     250 * @return ESYNCH_WOULD_BLOCK, meaning that the sleep failed because at the
     251 *         time of the call there was no pending wakeup
     252 * @return ESYNCH_TIMEOUT, meaning that the sleep timed out.
     253 * @return ESYNCH_INTERRUPTED, meaning that somebody interrupted the sleeping
     254 *         thread.
     255 * @return ESYNCH_OK_ATOMIC, meaning that the sleep succeeded and that there
     256 *         was a pending wakeup at the time of the call. The caller was not put
     257 *         asleep at all.
     258 * @return ESYNCH_OK_BLOCKED, meaning that the sleep succeeded; the full sleep
     259 *         was attempted.
     260 *
     261 */
     262int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, unsigned int flags)
     263{
     264        ASSERT((!PREEMPTION_DISABLED) || (PARAM_NON_BLOCKING(flags, usec)));
     265       
     266        ipl_t ipl = waitq_sleep_prepare(wq);
     267        int rc = waitq_sleep_timeout_unsafe(wq, usec, flags);
    261268        waitq_sleep_finish(wq, rc, ipl);
    262269        return rc;
     
    268275 * and interrupts disabled.
    269276 *
    270  * @param wq            Wait queue.
    271  *
    272  * @return              Interrupt level as it existed on entry to this function.
     277 * @param wq Wait queue.
     278 *
     279 * @return Interrupt level as it existed on entry to this function.
     280 *
    273281 */
    274282ipl_t waitq_sleep_prepare(waitq_t *wq)
     
    278286restart:
    279287        ipl = interrupts_disable();
    280 
    281         if (THREAD) {   /* needed during system initiailzation */
     288       
     289        if (THREAD) {  /* Needed during system initiailzation */
    282290                /*
    283291                 * Busy waiting for a delayed timeout.
     
    286294                 * Simply, the thread is not allowed to go to sleep if
    287295                 * there are timeouts in progress.
     296                 *
    288297                 */
    289                 spinlock_lock(&THREAD->lock);
     298                irq_spinlock_lock(&THREAD->lock, false);
     299               
    290300                if (THREAD->timeout_pending) {
    291                         spinlock_unlock(&THREAD->lock);
     301                        irq_spinlock_unlock(&THREAD->lock, false);
    292302                        interrupts_restore(ipl);
    293303                        goto restart;
    294304                }
    295                 spinlock_unlock(&THREAD->lock);
    296         }
    297                                                                                                        
    298         spinlock_lock(&wq->lock);
     305               
     306                irq_spinlock_unlock(&THREAD->lock, false);
     307        }
     308       
     309        irq_spinlock_lock(&wq->lock, false);
    299310        return ipl;
    300311}
     
    306317 * lock is released.
    307318 *
    308  * @param wq            Wait queue.
    309  * @param rc            Return code of waitq_sleep_timeout_unsafe().
    310  * @param ipl           Interrupt level returned by waitq_sleep_prepare().
     319 * @param wq  Wait queue.
     320 * @param rc  Return code of waitq_sleep_timeout_unsafe().
     321 * @param ipl Interrupt level returned by waitq_sleep_prepare().
     322 *
    311323 */
    312324void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl)
     
    315327        case ESYNCH_WOULD_BLOCK:
    316328        case ESYNCH_OK_ATOMIC:
    317                 spinlock_unlock(&wq->lock);
     329                irq_spinlock_unlock(&wq->lock, false);
    318330                break;
    319331        default:
    320332                break;
    321333        }
     334       
    322335        interrupts_restore(ipl);
    323336}
     
    329342 * and followed by a call to waitq_sleep_finish().
    330343 *
    331  * @param wq            See waitq_sleep_timeout().
    332  * @param usec          See waitq_sleep_timeout().
    333  * @param flags         See waitq_sleep_timeout().
    334  *
    335  * @return              See waitq_sleep_timeout().
    336  */
    337 int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, int flags)
    338 {
    339         /* checks whether to go to sleep at all */
     344 * @param wq    See waitq_sleep_timeout().
     345 * @param usec  See waitq_sleep_timeout().
     346 * @param flags See waitq_sleep_timeout().
     347 *
     348 * @return See waitq_sleep_timeout().
     349 *
     350 */
     351int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags)
     352{
     353        /* Checks whether to go to sleep at all */
    340354        if (wq->missed_wakeups) {
    341355                wq->missed_wakeups--;
    342356                return ESYNCH_OK_ATOMIC;
    343         }
    344         else {
    345                 if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) {
    346                         /* return immediatelly instead of going to sleep */
     357        } else {
     358                if (PARAM_NON_BLOCKING(flags, usec)) {
     359                        /* Return immediatelly instead of going to sleep */
    347360                        return ESYNCH_WOULD_BLOCK;
    348361                }
     
    351364        /*
    352365         * Now we are firmly decided to go to sleep.
     366         *
    353367         */
    354         spinlock_lock(&THREAD->lock);
    355 
     368        irq_spinlock_lock(&THREAD->lock, false);
     369       
    356370        if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
    357 
    358371                /*
    359372                 * If the thread was already interrupted,
     
    361374                 */
    362375                if (THREAD->interrupted) {
    363                         spinlock_unlock(&THREAD->lock);
    364                         spinlock_unlock(&wq->lock);
     376                        irq_spinlock_unlock(&THREAD->lock, false);
     377                        irq_spinlock_unlock(&wq->lock, false);
    365378                        return ESYNCH_INTERRUPTED;
    366379                }
    367 
     380               
    368381                /*
    369382                 * Set context that will be restored if the sleep
     
    373386                if (!context_save(&THREAD->sleep_interruption_context)) {
    374387                        /* Short emulation of scheduler() return code. */
    375                         spinlock_unlock(&THREAD->lock);
     388                        THREAD->last_cycle = get_cycle();
     389                        irq_spinlock_unlock(&THREAD->lock, false);
    376390                        return ESYNCH_INTERRUPTED;
    377391                }
    378 
    379         } else {
     392        } else
    380393                THREAD->sleep_interruptible = false;
    381         }
    382 
     394       
    383395        if (usec) {
    384396                /* We use the timeout variant. */
    385397                if (!context_save(&THREAD->sleep_timeout_context)) {
    386398                        /* Short emulation of scheduler() return code. */
    387                         spinlock_unlock(&THREAD->lock);
     399                        THREAD->last_cycle = get_cycle();
     400                        irq_spinlock_unlock(&THREAD->lock, false);
    388401                        return ESYNCH_TIMEOUT;
    389402                }
     403               
    390404                THREAD->timeout_pending = true;
    391405                timeout_register(&THREAD->sleep_timeout, (uint64_t) usec,
    392406                    waitq_sleep_timed_out, THREAD);
    393407        }
    394 
     408       
    395409        list_append(&THREAD->wq_link, &wq->head);
    396 
     410       
    397411        /*
    398412         * Suspend execution.
     413         *
    399414         */
    400415        THREAD->state = Sleeping;
    401416        THREAD->sleep_queue = wq;
    402 
    403         spinlock_unlock(&THREAD->lock);
    404 
     417       
     418        irq_spinlock_unlock(&THREAD->lock, false);
     419       
    405420        /* wq->lock is released in scheduler_separated_stack() */
    406         scheduler(); 
     421        scheduler();
    407422       
    408423        return ESYNCH_OK_BLOCKED;
    409424}
    410 
    411425
    412426/** Wake up first thread sleeping in a wait queue
     
    418432 * timeout.
    419433 *
    420  * @param wq            Pointer to wait queue.
    421  * @param mode          Wakeup mode.
     434 * @param wq   Pointer to wait queue.
     435 * @param mode Wakeup mode.
     436 *
    422437 */
    423438void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode)
    424439{
    425         ipl_t ipl;
    426 
    427         ipl = interrupts_disable();
    428         spinlock_lock(&wq->lock);
    429 
     440        irq_spinlock_lock(&wq->lock, true);
    430441        _waitq_wakeup_unsafe(wq, mode);
    431 
    432         spinlock_unlock(&wq->lock);
    433         interrupts_restore(ipl);
     442        irq_spinlock_unlock(&wq->lock, true);
    434443}
    435444
     
    439448 * assumes wq->lock is already locked and interrupts are already disabled.
    440449 *
    441  * @param wq            Pointer to wait queue.
    442  * @param mode          If mode is WAKEUP_FIRST, then the longest waiting
    443  *                      thread, if any, is woken up. If mode is WAKEUP_ALL, then
    444  *                      all waiting threads, if any, are woken up. If there are
    445  *                      no waiting threads to be woken up, the missed wakeup is
    446  *                      recorded in the wait queue.
     450 * @param wq   Pointer to wait queue.
     451 * @param mode If mode is WAKEUP_FIRST, then the longest waiting
     452 *             thread, if any, is woken up. If mode is WAKEUP_ALL, then
     453 *             all waiting threads, if any, are woken up. If there are
     454 *             no waiting threads to be woken up, the missed wakeup is
     455 *             recorded in the wait queue.
     456 *
    447457 */
    448458void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode)
    449459{
    450         thread_t *t;
    451460        size_t count = 0;
    452461
    453 loop:   
     462        ASSERT(interrupts_disabled());
     463        ASSERT(irq_spinlock_locked(&wq->lock));
     464       
     465loop:
    454466        if (list_empty(&wq->head)) {
    455467                wq->missed_wakeups++;
    456                 if (count && mode == WAKEUP_ALL)
     468                if ((count) && (mode == WAKEUP_ALL))
    457469                        wq->missed_wakeups--;
     470               
    458471                return;
    459472        }
    460 
     473       
    461474        count++;
    462         t = list_get_instance(wq->head.next, thread_t, wq_link);
     475        thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link);
    463476       
    464477        /*
     
    472485         * invariant must hold:
    473486         *
    474          * t->sleep_queue != NULL <=> t sleeps in a wait queue
     487         * thread->sleep_queue != NULL <=> thread sleeps in a wait queue
    475488         *
    476489         * For an observer who locks the thread, the invariant
    477490         * holds only when the lock is held prior to removing
    478491         * it from the wait queue.
     492         *
    479493         */
    480         spinlock_lock(&t->lock);
    481         list_remove(&t->wq_link);
    482        
    483         if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
    484                 t->timeout_pending = false;
    485         t->sleep_queue = NULL;
    486         spinlock_unlock(&t->lock);
    487 
    488         thread_ready(t);
    489 
     494        irq_spinlock_lock(&thread->lock, false);
     495        list_remove(&thread->wq_link);
     496       
     497        if ((thread->timeout_pending) &&
     498            (timeout_unregister(&thread->sleep_timeout)))
     499                thread->timeout_pending = false;
     500       
     501        thread->sleep_queue = NULL;
     502        irq_spinlock_unlock(&thread->lock, false);
     503       
     504        thread_ready(thread);
     505       
    490506        if (mode == WAKEUP_ALL)
    491507                goto loop;
    492508}
    493509
     510/** Get the missed wakeups count.
     511 *
     512 * @param wq    Pointer to wait queue.
     513 * @return      The wait queue's missed_wakeups count.
     514 */
     515int waitq_count_get(waitq_t *wq)
     516{
     517        int cnt;
     518
     519        irq_spinlock_lock(&wq->lock, true);
     520        cnt = wq->missed_wakeups;
     521        irq_spinlock_unlock(&wq->lock, true);
     522
     523        return cnt;
     524}
     525
     526/** Set the missed wakeups count.
     527 *
     528 * @param wq    Pointer to wait queue.
     529 * @param val   New value of the missed_wakeups count.
     530 */
     531void waitq_count_set(waitq_t *wq, int val)
     532{
     533        irq_spinlock_lock(&wq->lock, true);
     534        wq->missed_wakeups = val;
     535        irq_spinlock_unlock(&wq->lock, true);
     536}
     537
    494538/** @}
    495539 */
Note: See TracChangeset for help on using the changeset viewer.