Ignore:
File:
1 edited

Legend:

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

    r6ec34bb r1d432f9  
    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
     
    5657#include <arch/cycle.h>
    5758
    58 static void waitq_sleep_timed_out(void *data);
     59static void waitq_sleep_timed_out(void *);
    5960
    6061/** Initialize wait queue
     
    6263 * Initialize wait queue.
    6364 *
    64  * @param wq            Pointer to wait queue to be initialized.
     65 * @param wq Pointer to wait queue to be initialized.
     66 *
    6567 */
    6668void waitq_initialize(waitq_t *wq)
    6769{
    68         spinlock_initialize(&wq->lock, "waitq_lock");
     70        irq_spinlock_initialize(&wq->lock, "wq.lock");
    6971        list_initialize(&wq->head);
    7072        wq->missed_wakeups = 0;
     
    8183 * timeout at all.
    8284 *
    83  * @param data          Pointer to the thread that called waitq_sleep_timeout().
     85 * @param data Pointer to the thread that called waitq_sleep_timeout().
     86 *
    8487 */
    8588void waitq_sleep_timed_out(void *data)
    8689{
    87         thread_t *t = (thread_t *) data;
    88         waitq_t *wq;
     90        thread_t *thread = (thread_t *) data;
    8991        bool do_wakeup = false;
    9092        DEADLOCK_PROBE_INIT(p_wqlock);
    91 
    92         spinlock_lock(&threads_lock);
    93         if (!thread_exists(t))
     93       
     94        irq_spinlock_lock(&threads_lock, false);
     95        if (!thread_exists(thread))
    9496                goto out;
    95 
     97       
    9698grab_locks:
    97         spinlock_lock(&t->lock);
    98         if ((wq = t->sleep_queue)) {            /* assignment */
    99                 if (!spinlock_trylock(&wq->lock)) {
    100                         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);
    101105                        DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
    102                         goto grab_locks;        /* avoid deadlock */
    103                 }
    104 
    105                 list_remove(&t->wq_link);
    106                 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;
    107112                do_wakeup = true;
    108                 t->sleep_queue = NULL;
    109                 spinlock_unlock(&wq->lock);
    110         }
    111        
    112         t->timeout_pending = false;
    113         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);
    114119       
    115120        if (do_wakeup)
    116                 thread_ready(t);
    117 
     121                thread_ready(thread);
     122       
    118123out:
    119         spinlock_unlock(&threads_lock);
     124        irq_spinlock_unlock(&threads_lock, false);
    120125}
    121126
     
    125130 * If the thread is not found sleeping, no action is taken.
    126131 *
    127  * @param t             Thread to be interrupted.
    128  */
    129 void waitq_interrupt_sleep(thread_t *t)
    130 {
     132 * @param thread Thread to be interrupted.
     133 *
     134 */
     135void waitq_interrupt_sleep(thread_t *thread)
     136{
     137        bool do_wakeup = false;
     138        DEADLOCK_PROBE_INIT(p_wqlock);
     139       
     140        irq_spinlock_lock(&threads_lock, true);
     141        if (!thread_exists(thread))
     142                goto out;
     143       
     144grab_locks:
     145        irq_spinlock_lock(&thread->lock, false);
     146       
    131147        waitq_t *wq;
    132         bool do_wakeup = false;
    133         ipl_t ipl;
    134         DEADLOCK_PROBE_INIT(p_wqlock);
    135 
    136         ipl = interrupts_disable();
    137         spinlock_lock(&threads_lock);
    138         if (!thread_exists(t))
    139                 goto out;
    140 
    141 grab_locks:
    142         spinlock_lock(&t->lock);
    143         if ((wq = t->sleep_queue)) {            /* assignment */
    144                 if (!(t->sleep_interruptible)) {
     148        if ((wq = thread->sleep_queue)) {  /* Assignment */
     149                if (!(thread->sleep_interruptible)) {
    145150                        /*
    146151                         * The sleep cannot be interrupted.
     152                         *
    147153                         */
    148                         spinlock_unlock(&t->lock);
     154                        irq_spinlock_unlock(&thread->lock, false);
    149155                        goto out;
    150156                }
    151                        
    152                 if (!spinlock_trylock(&wq->lock)) {
    153                         spinlock_unlock(&t->lock);
     157               
     158                if (!irq_spinlock_trylock(&wq->lock)) {
     159                        irq_spinlock_unlock(&thread->lock, false);
    154160                        DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
    155                         goto grab_locks;        /* avoid deadlock */
    156                 }
    157 
    158                 if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
    159                         t->timeout_pending = false;
    160 
    161                 list_remove(&t->wq_link);
    162                 t->saved_context = t->sleep_interruption_context;
     161                        /* Avoid deadlock */
     162                        goto grab_locks;
     163                }
     164               
     165                if ((thread->timeout_pending) &&
     166                    (timeout_unregister(&thread->sleep_timeout)))
     167                        thread->timeout_pending = false;
     168               
     169                list_remove(&thread->wq_link);
     170                thread->saved_context = thread->sleep_interruption_context;
    163171                do_wakeup = true;
    164                 t->sleep_queue = NULL;
    165                 spinlock_unlock(&wq->lock);
    166         }
    167         spinlock_unlock(&t->lock);
    168 
     172                thread->sleep_queue = NULL;
     173                irq_spinlock_unlock(&wq->lock, false);
     174        }
     175        irq_spinlock_unlock(&thread->lock, false);
     176       
    169177        if (do_wakeup)
    170                 thread_ready(t);
    171 
     178                thread_ready(thread);
     179       
    172180out:
    173         spinlock_unlock(&threads_lock);
    174         interrupts_restore(ipl);
     181        irq_spinlock_unlock(&threads_lock, true);
    175182}
    176183
     
    180187 * is sleeping interruptibly.
    181188 *
    182  * @param wq            Pointer to wait queue.
     189 * @param wq Pointer to wait queue.
     190 *
    183191 */
    184192void waitq_unsleep(waitq_t *wq)
    185193{
    186         ipl_t ipl;
    187 
    188         ipl = interrupts_disable();
    189         spinlock_lock(&wq->lock);
    190 
     194        irq_spinlock_lock(&wq->lock, true);
     195       
    191196        if (!list_empty(&wq->head)) {
    192                 thread_t *t;
    193                
    194                 t = list_get_instance(wq->head.next, thread_t, wq_link);
    195                 spinlock_lock(&t->lock);
    196                 ASSERT(t->sleep_interruptible);
    197                 if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
    198                         t->timeout_pending = false;
    199                 list_remove(&t->wq_link);
    200                 t->saved_context = t->sleep_interruption_context;
    201                 t->sleep_queue = NULL;
    202                 spinlock_unlock(&t->lock);
    203                 thread_ready(t);
    204         }
    205 
    206         spinlock_unlock(&wq->lock);
    207         interrupts_restore(ipl);
    208 }
     197                thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link);
     198               
     199                irq_spinlock_lock(&thread->lock, false);
     200               
     201                ASSERT(thread->sleep_interruptible);
     202               
     203                if ((thread->timeout_pending) &&
     204                    (timeout_unregister(&thread->sleep_timeout)))
     205                        thread->timeout_pending = false;
     206               
     207                list_remove(&thread->wq_link);
     208                thread->saved_context = thread->sleep_interruption_context;
     209                thread->sleep_queue = NULL;
     210               
     211                irq_spinlock_unlock(&thread->lock, false);
     212                thread_ready(thread);
     213        }
     214       
     215        irq_spinlock_unlock(&wq->lock, true);
     216}
     217
     218#define PARAM_NON_BLOCKING(flags, usec) \
     219        (((flags) & SYNCH_FLAGS_NON_BLOCKING) && ((usec) == 0))
    209220
    210221/** Sleep until either wakeup, timeout or interruption occurs
     
    218229 * and all the *_timeout() functions use it.
    219230 *
    220  * @param wq            Pointer to wait queue.
    221  * @param usec          Timeout in microseconds.
    222  * @param flags         Specify mode of the sleep.
     231 * @param wq    Pointer to wait queue.
     232 * @param usec  Timeout in microseconds.
     233 * @param flags Specify mode of the sleep.
    223234 *
    224235 * The sleep can be interrupted only if the
    225236 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
    226  * 
     237 *
    227238 * If usec is greater than zero, regardless of the value of the
    228239 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either
    229  * timeout, interruption or wakeup comes. 
     240 * timeout, interruption or wakeup comes.
    230241 *
    231242 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags,
     
    235246 * call will immediately return, reporting either success or failure.
    236247 *
    237  * @return              Returns one of ESYNCH_WOULD_BLOCK, ESYNCH_TIMEOUT,
    238  *                      ESYNCH_INTERRUPTED, ESYNCH_OK_ATOMIC and
    239  *                      ESYNCH_OK_BLOCKED.
    240  *
    241  * @li  ESYNCH_WOULD_BLOCK means that the sleep failed because at the time of
    242  *      the call there was no pending wakeup.
    243  *
    244  * @li  ESYNCH_TIMEOUT means that the sleep timed out.
    245  *
    246  * @li  ESYNCH_INTERRUPTED means that somebody interrupted the sleeping thread.
    247  *
    248  * @li  ESYNCH_OK_ATOMIC means that the sleep succeeded and that there was
    249  *      a pending wakeup at the time of the call. The caller was not put
    250  *      asleep at all.
    251  *
    252  * @li  ESYNCH_OK_BLOCKED means that the sleep succeeded; the full sleep was
    253  *      attempted.
    254  */
    255 int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, int flags)
    256 {
    257         ipl_t ipl;
    258         int rc;
    259        
    260         ipl = waitq_sleep_prepare(wq);
    261         rc = waitq_sleep_timeout_unsafe(wq, usec, flags);
     248 * @return ESYNCH_WOULD_BLOCK, meaning that the sleep failed because at the
     249 *         time of the call there was no pending wakeup
     250 * @return ESYNCH_TIMEOUT, meaning that the sleep timed out.
     251 * @return ESYNCH_INTERRUPTED, meaning that somebody interrupted the sleeping
     252 *         thread.
     253 * @return ESYNCH_OK_ATOMIC, meaning that the sleep succeeded and that there
     254 *         was a pending wakeup at the time of the call. The caller was not put
     255 *         asleep at all.
     256 * @return ESYNCH_OK_BLOCKED, meaning that the sleep succeeded; the full sleep
     257 *         was attempted.
     258 *
     259 */
     260int waitq_sleep_timeout(waitq_t *wq, uint32_t usec, unsigned int flags)
     261{
     262        ASSERT((!PREEMPTION_DISABLED) || (PARAM_NON_BLOCKING(flags, usec)));
     263       
     264        ipl_t ipl = waitq_sleep_prepare(wq);
     265        int rc = waitq_sleep_timeout_unsafe(wq, usec, flags);
    262266        waitq_sleep_finish(wq, rc, ipl);
    263267        return rc;
     
    269273 * and interrupts disabled.
    270274 *
    271  * @param wq            Wait queue.
    272  *
    273  * @return              Interrupt level as it existed on entry to this function.
     275 * @param wq Wait queue.
     276 *
     277 * @return Interrupt level as it existed on entry to this function.
     278 *
    274279 */
    275280ipl_t waitq_sleep_prepare(waitq_t *wq)
     
    279284restart:
    280285        ipl = interrupts_disable();
    281 
    282         if (THREAD) {   /* needed during system initiailzation */
     286       
     287        if (THREAD) {  /* Needed during system initiailzation */
    283288                /*
    284289                 * Busy waiting for a delayed timeout.
     
    287292                 * Simply, the thread is not allowed to go to sleep if
    288293                 * there are timeouts in progress.
     294                 *
    289295                 */
    290                 spinlock_lock(&THREAD->lock);
     296                irq_spinlock_lock(&THREAD->lock, false);
     297               
    291298                if (THREAD->timeout_pending) {
    292                         spinlock_unlock(&THREAD->lock);
     299                        irq_spinlock_unlock(&THREAD->lock, false);
    293300                        interrupts_restore(ipl);
    294301                        goto restart;
    295302                }
    296                 spinlock_unlock(&THREAD->lock);
    297         }
    298                                                                                                        
    299         spinlock_lock(&wq->lock);
     303               
     304                irq_spinlock_unlock(&THREAD->lock, false);
     305        }
     306       
     307        irq_spinlock_lock(&wq->lock, false);
    300308        return ipl;
    301309}
     
    307315 * lock is released.
    308316 *
    309  * @param wq            Wait queue.
    310  * @param rc            Return code of waitq_sleep_timeout_unsafe().
    311  * @param ipl           Interrupt level returned by waitq_sleep_prepare().
     317 * @param wq  Wait queue.
     318 * @param rc  Return code of waitq_sleep_timeout_unsafe().
     319 * @param ipl Interrupt level returned by waitq_sleep_prepare().
     320 *
    312321 */
    313322void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl)
     
    316325        case ESYNCH_WOULD_BLOCK:
    317326        case ESYNCH_OK_ATOMIC:
    318                 spinlock_unlock(&wq->lock);
     327                irq_spinlock_unlock(&wq->lock, false);
    319328                break;
    320329        default:
    321330                break;
    322331        }
     332       
    323333        interrupts_restore(ipl);
    324334}
     
    330340 * and followed by a call to waitq_sleep_finish().
    331341 *
    332  * @param wq            See waitq_sleep_timeout().
    333  * @param usec          See waitq_sleep_timeout().
    334  * @param flags         See waitq_sleep_timeout().
    335  *
    336  * @return              See waitq_sleep_timeout().
    337  */
    338 int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, int flags)
    339 {
    340         /* checks whether to go to sleep at all */
     342 * @param wq    See waitq_sleep_timeout().
     343 * @param usec  See waitq_sleep_timeout().
     344 * @param flags See waitq_sleep_timeout().
     345 *
     346 * @return See waitq_sleep_timeout().
     347 *
     348 */
     349int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags)
     350{
     351        /* Checks whether to go to sleep at all */
    341352        if (wq->missed_wakeups) {
    342353                wq->missed_wakeups--;
    343354                return ESYNCH_OK_ATOMIC;
    344         }
    345         else {
    346                 if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) {
    347                         /* return immediatelly instead of going to sleep */
     355        } else {
     356                if (PARAM_NON_BLOCKING(flags, usec)) {
     357                        /* Return immediatelly instead of going to sleep */
    348358                        return ESYNCH_WOULD_BLOCK;
    349359                }
     
    352362        /*
    353363         * Now we are firmly decided to go to sleep.
     364         *
    354365         */
    355         spinlock_lock(&THREAD->lock);
    356 
     366        irq_spinlock_lock(&THREAD->lock, false);
     367       
    357368        if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
    358 
    359369                /*
    360370                 * If the thread was already interrupted,
    361371                 * don't go to sleep at all.
     372                 *
    362373                 */
    363374                if (THREAD->interrupted) {
    364                         spinlock_unlock(&THREAD->lock);
    365                         spinlock_unlock(&wq->lock);
     375                        irq_spinlock_unlock(&THREAD->lock, false);
     376                        irq_spinlock_unlock(&wq->lock, false);
    366377                        return ESYNCH_INTERRUPTED;
    367378                }
    368 
     379               
    369380                /*
    370381                 * Set context that will be restored if the sleep
    371382                 * of this thread is ever interrupted.
     383                 *
    372384                 */
    373385                THREAD->sleep_interruptible = true;
     
    375387                        /* Short emulation of scheduler() return code. */
    376388                        THREAD->last_cycle = get_cycle();
    377                         spinlock_unlock(&THREAD->lock);
     389                        irq_spinlock_unlock(&THREAD->lock, false);
    378390                        return ESYNCH_INTERRUPTED;
    379391                }
    380 
    381         } else {
     392        } else
    382393                THREAD->sleep_interruptible = false;
    383         }
    384 
     394       
    385395        if (usec) {
    386396                /* We use the timeout variant. */
     
    388398                        /* Short emulation of scheduler() return code. */
    389399                        THREAD->last_cycle = get_cycle();
    390                         spinlock_unlock(&THREAD->lock);
     400                        irq_spinlock_unlock(&THREAD->lock, false);
    391401                        return ESYNCH_TIMEOUT;
    392402                }
     403               
    393404                THREAD->timeout_pending = true;
    394405                timeout_register(&THREAD->sleep_timeout, (uint64_t) usec,
    395406                    waitq_sleep_timed_out, THREAD);
    396407        }
    397 
     408       
    398409        list_append(&THREAD->wq_link, &wq->head);
    399 
     410       
    400411        /*
    401412         * Suspend execution.
     413         *
    402414         */
    403415        THREAD->state = Sleeping;
    404416        THREAD->sleep_queue = wq;
    405 
    406         spinlock_unlock(&THREAD->lock);
    407 
     417       
     418        irq_spinlock_unlock(&THREAD->lock, false);
     419       
    408420        /* wq->lock is released in scheduler_separated_stack() */
    409         scheduler(); 
     421        scheduler();
    410422       
    411423        return ESYNCH_OK_BLOCKED;
    412424}
    413 
    414425
    415426/** Wake up first thread sleeping in a wait queue
     
    421432 * timeout.
    422433 *
    423  * @param wq            Pointer to wait queue.
    424  * @param mode          Wakeup mode.
     434 * @param wq   Pointer to wait queue.
     435 * @param mode Wakeup mode.
     436 *
    425437 */
    426438void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode)
    427439{
    428         ipl_t ipl;
    429 
    430         ipl = interrupts_disable();
    431         spinlock_lock(&wq->lock);
    432 
     440        irq_spinlock_lock(&wq->lock, true);
    433441        _waitq_wakeup_unsafe(wq, mode);
    434 
    435         spinlock_unlock(&wq->lock);
    436         interrupts_restore(ipl);
     442        irq_spinlock_unlock(&wq->lock, true);
    437443}
    438444
     
    442448 * assumes wq->lock is already locked and interrupts are already disabled.
    443449 *
    444  * @param wq            Pointer to wait queue.
    445  * @param mode          If mode is WAKEUP_FIRST, then the longest waiting
    446  *                      thread, if any, is woken up. If mode is WAKEUP_ALL, then
    447  *                      all waiting threads, if any, are woken up. If there are
    448  *                      no waiting threads to be woken up, the missed wakeup is
    449  *                      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 *
    450457 */
    451458void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode)
    452459{
    453         thread_t *t;
    454460        size_t count = 0;
    455461
    456 loop:   
     462        ASSERT(interrupts_disabled());
     463        ASSERT(irq_spinlock_locked(&wq->lock));
     464       
     465loop:
    457466        if (list_empty(&wq->head)) {
    458467                wq->missed_wakeups++;
    459                 if (count && mode == WAKEUP_ALL)
     468                if ((count) && (mode == WAKEUP_ALL))
    460469                        wq->missed_wakeups--;
     470               
    461471                return;
    462472        }
    463 
     473       
    464474        count++;
    465         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);
    466476       
    467477        /*
     
    475485         * invariant must hold:
    476486         *
    477          * t->sleep_queue != NULL <=> t sleeps in a wait queue
     487         * thread->sleep_queue != NULL <=> thread sleeps in a wait queue
    478488         *
    479489         * For an observer who locks the thread, the invariant
    480490         * holds only when the lock is held prior to removing
    481491         * it from the wait queue.
     492         *
    482493         */
    483         spinlock_lock(&t->lock);
    484         list_remove(&t->wq_link);
    485        
    486         if (t->timeout_pending && timeout_unregister(&t->sleep_timeout))
    487                 t->timeout_pending = false;
    488         t->sleep_queue = NULL;
    489         spinlock_unlock(&t->lock);
    490 
    491         thread_ready(t);
    492 
     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       
    493506        if (mode == WAKEUP_ALL)
    494507                goto loop;
    495508}
    496509
     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
    497538/** @}
    498539 */
Note: See TracChangeset for help on using the changeset viewer.