Ignore:
File:
1 edited

Legend:

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

    r1d432f9 r6ec34bb  
    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  *
    4443 */
    4544
     
    5756#include <arch/cycle.h>
    5857
    59 static void waitq_sleep_timed_out(void *);
     58static void waitq_sleep_timed_out(void *data);
    6059
    6160/** Initialize wait queue
     
    6362 * Initialize wait queue.
    6463 *
    65  * @param wq Pointer to wait queue to be initialized.
    66  *
     64 * @param wq            Pointer to wait queue to be initialized.
    6765 */
    6866void waitq_initialize(waitq_t *wq)
    6967{
    70         irq_spinlock_initialize(&wq->lock, "wq.lock");
     68        spinlock_initialize(&wq->lock, "waitq_lock");
    7169        list_initialize(&wq->head);
    7270        wq->missed_wakeups = 0;
     
    8381 * timeout at all.
    8482 *
    85  * @param data Pointer to the thread that called waitq_sleep_timeout().
    86  *
     83 * @param data          Pointer to the thread that called waitq_sleep_timeout().
    8784 */
    8885void waitq_sleep_timed_out(void *data)
    8986{
    90         thread_t *thread = (thread_t *) data;
     87        thread_t *t = (thread_t *) data;
     88        waitq_t *wq;
    9189        bool do_wakeup = false;
    9290        DEADLOCK_PROBE_INIT(p_wqlock);
    93        
    94         irq_spinlock_lock(&threads_lock, false);
    95         if (!thread_exists(thread))
     91
     92        spinlock_lock(&threads_lock);
     93        if (!thread_exists(t))
    9694                goto out;
    97        
     95
    9896grab_locks:
    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);
     97        spinlock_lock(&t->lock);
     98        if ((wq = t->sleep_queue)) {            /* assignment */
     99                if (!spinlock_trylock(&wq->lock)) {
     100                        spinlock_unlock(&t->lock);
    105101                        DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
    106                         /* Avoid deadlock */
    107                         goto grab_locks;
    108                 }
    109                
    110                 list_remove(&thread->wq_link);
    111                 thread->saved_context = thread->sleep_timeout_context;
     102                        goto grab_locks;        /* avoid deadlock */
     103                }
     104
     105                list_remove(&t->wq_link);
     106                t->saved_context = t->sleep_timeout_context;
    112107                do_wakeup = true;
    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);
     108                t->sleep_queue = NULL;
     109                spinlock_unlock(&wq->lock);
     110        }
     111       
     112        t->timeout_pending = false;
     113        spinlock_unlock(&t->lock);
    119114       
    120115        if (do_wakeup)
    121                 thread_ready(thread);
    122        
     116                thread_ready(t);
     117
    123118out:
    124         irq_spinlock_unlock(&threads_lock, false);
     119        spinlock_unlock(&threads_lock);
    125120}
    126121
     
    130125 * If the thread is not found sleeping, no action is taken.
    131126 *
    132  * @param thread Thread to be interrupted.
    133  *
    134  */
    135 void waitq_interrupt_sleep(thread_t *thread)
    136 {
     127 * @param t             Thread to be interrupted.
     128 */
     129void waitq_interrupt_sleep(thread_t *t)
     130{
     131        waitq_t *wq;
    137132        bool do_wakeup = false;
     133        ipl_t ipl;
    138134        DEADLOCK_PROBE_INIT(p_wqlock);
    139        
    140         irq_spinlock_lock(&threads_lock, true);
    141         if (!thread_exists(thread))
     135
     136        ipl = interrupts_disable();
     137        spinlock_lock(&threads_lock);
     138        if (!thread_exists(t))
    142139                goto out;
    143        
     140
    144141grab_locks:
    145         irq_spinlock_lock(&thread->lock, false);
    146        
    147         waitq_t *wq;
    148         if ((wq = thread->sleep_queue)) {  /* Assignment */
    149                 if (!(thread->sleep_interruptible)) {
     142        spinlock_lock(&t->lock);
     143        if ((wq = t->sleep_queue)) {            /* assignment */
     144                if (!(t->sleep_interruptible)) {
    150145                        /*
    151146                         * The sleep cannot be interrupted.
    152                          *
    153147                         */
    154                         irq_spinlock_unlock(&thread->lock, false);
     148                        spinlock_unlock(&t->lock);
    155149                        goto out;
    156150                }
    157                
    158                 if (!irq_spinlock_trylock(&wq->lock)) {
    159                         irq_spinlock_unlock(&thread->lock, false);
     151                       
     152                if (!spinlock_trylock(&wq->lock)) {
     153                        spinlock_unlock(&t->lock);
    160154                        DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
    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;
     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;
    171163                do_wakeup = true;
    172                 thread->sleep_queue = NULL;
    173                 irq_spinlock_unlock(&wq->lock, false);
    174         }
    175         irq_spinlock_unlock(&thread->lock, false);
    176        
     164                t->sleep_queue = NULL;
     165                spinlock_unlock(&wq->lock);
     166        }
     167        spinlock_unlock(&t->lock);
     168
    177169        if (do_wakeup)
    178                 thread_ready(thread);
    179        
     170                thread_ready(t);
     171
    180172out:
    181         irq_spinlock_unlock(&threads_lock, true);
     173        spinlock_unlock(&threads_lock);
     174        interrupts_restore(ipl);
    182175}
    183176
     
    187180 * is sleeping interruptibly.
    188181 *
    189  * @param wq Pointer to wait queue.
    190  *
     182 * @param wq            Pointer to wait queue.
    191183 */
    192184void waitq_unsleep(waitq_t *wq)
    193185{
    194         irq_spinlock_lock(&wq->lock, true);
    195        
     186        ipl_t ipl;
     187
     188        ipl = interrupts_disable();
     189        spinlock_lock(&wq->lock);
     190
    196191        if (!list_empty(&wq->head)) {
    197                 thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link);
     192                thread_t *t;
    198193               
    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))
     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}
    220209
    221210/** Sleep until either wakeup, timeout or interruption occurs
     
    229218 * and all the *_timeout() functions use it.
    230219 *
    231  * @param wq    Pointer to wait queue.
    232  * @param usec  Timeout in microseconds.
    233  * @param flags Specify mode of the sleep.
     220 * @param wq            Pointer to wait queue.
     221 * @param usec          Timeout in microseconds.
     222 * @param flags         Specify mode of the sleep.
    234223 *
    235224 * The sleep can be interrupted only if the
    236225 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
    237  *
     226 * 
    238227 * If usec is greater than zero, regardless of the value of the
    239228 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either
    240  * timeout, interruption or wakeup comes.
     229 * timeout, interruption or wakeup comes. 
    241230 *
    242231 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags,
     
    246235 * call will immediately return, reporting either success or failure.
    247236 *
    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  */
    260 int 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);
     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 */
     255int 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);
    266262        waitq_sleep_finish(wq, rc, ipl);
    267263        return rc;
     
    273269 * and interrupts disabled.
    274270 *
    275  * @param wq Wait queue.
    276  *
    277  * @return Interrupt level as it existed on entry to this function.
    278  *
     271 * @param wq            Wait queue.
     272 *
     273 * @return              Interrupt level as it existed on entry to this function.
    279274 */
    280275ipl_t waitq_sleep_prepare(waitq_t *wq)
     
    284279restart:
    285280        ipl = interrupts_disable();
    286        
    287         if (THREAD) {  /* Needed during system initiailzation */
     281
     282        if (THREAD) {   /* needed during system initiailzation */
    288283                /*
    289284                 * Busy waiting for a delayed timeout.
     
    292287                 * Simply, the thread is not allowed to go to sleep if
    293288                 * there are timeouts in progress.
    294                  *
    295289                 */
    296                 irq_spinlock_lock(&THREAD->lock, false);
    297                
     290                spinlock_lock(&THREAD->lock);
    298291                if (THREAD->timeout_pending) {
    299                         irq_spinlock_unlock(&THREAD->lock, false);
     292                        spinlock_unlock(&THREAD->lock);
    300293                        interrupts_restore(ipl);
    301294                        goto restart;
    302295                }
    303                
    304                 irq_spinlock_unlock(&THREAD->lock, false);
    305         }
    306        
    307         irq_spinlock_lock(&wq->lock, false);
     296                spinlock_unlock(&THREAD->lock);
     297        }
     298                                                                                                       
     299        spinlock_lock(&wq->lock);
    308300        return ipl;
    309301}
     
    315307 * lock is released.
    316308 *
    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  *
     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().
    321312 */
    322313void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl)
     
    325316        case ESYNCH_WOULD_BLOCK:
    326317        case ESYNCH_OK_ATOMIC:
    327                 irq_spinlock_unlock(&wq->lock, false);
     318                spinlock_unlock(&wq->lock);
    328319                break;
    329320        default:
    330321                break;
    331322        }
    332        
    333323        interrupts_restore(ipl);
    334324}
     
    340330 * and followed by a call to waitq_sleep_finish().
    341331 *
    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  */
    349 int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags)
    350 {
    351         /* Checks whether to go to sleep at all */
     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 */
     338int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, int flags)
     339{
     340        /* checks whether to go to sleep at all */
    352341        if (wq->missed_wakeups) {
    353342                wq->missed_wakeups--;
    354343                return ESYNCH_OK_ATOMIC;
    355         } else {
    356                 if (PARAM_NON_BLOCKING(flags, usec)) {
    357                         /* Return immediatelly instead of going to sleep */
     344        }
     345        else {
     346                if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) {
     347                        /* return immediatelly instead of going to sleep */
    358348                        return ESYNCH_WOULD_BLOCK;
    359349                }
     
    362352        /*
    363353         * Now we are firmly decided to go to sleep.
    364          *
    365354         */
    366         irq_spinlock_lock(&THREAD->lock, false);
    367        
     355        spinlock_lock(&THREAD->lock);
     356
    368357        if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
     358
    369359                /*
    370360                 * If the thread was already interrupted,
    371361                 * don't go to sleep at all.
    372                  *
    373362                 */
    374363                if (THREAD->interrupted) {
    375                         irq_spinlock_unlock(&THREAD->lock, false);
    376                         irq_spinlock_unlock(&wq->lock, false);
     364                        spinlock_unlock(&THREAD->lock);
     365                        spinlock_unlock(&wq->lock);
    377366                        return ESYNCH_INTERRUPTED;
    378367                }
    379                
     368
    380369                /*
    381370                 * Set context that will be restored if the sleep
    382371                 * of this thread is ever interrupted.
    383                  *
    384372                 */
    385373                THREAD->sleep_interruptible = true;
     
    387375                        /* Short emulation of scheduler() return code. */
    388376                        THREAD->last_cycle = get_cycle();
    389                         irq_spinlock_unlock(&THREAD->lock, false);
     377                        spinlock_unlock(&THREAD->lock);
    390378                        return ESYNCH_INTERRUPTED;
    391379                }
    392         } else
     380
     381        } else {
    393382                THREAD->sleep_interruptible = false;
    394        
     383        }
     384
    395385        if (usec) {
    396386                /* We use the timeout variant. */
     
    398388                        /* Short emulation of scheduler() return code. */
    399389                        THREAD->last_cycle = get_cycle();
    400                         irq_spinlock_unlock(&THREAD->lock, false);
     390                        spinlock_unlock(&THREAD->lock);
    401391                        return ESYNCH_TIMEOUT;
    402392                }
    403                
    404393                THREAD->timeout_pending = true;
    405394                timeout_register(&THREAD->sleep_timeout, (uint64_t) usec,
    406395                    waitq_sleep_timed_out, THREAD);
    407396        }
    408        
     397
    409398        list_append(&THREAD->wq_link, &wq->head);
    410        
     399
    411400        /*
    412401         * Suspend execution.
    413          *
    414402         */
    415403        THREAD->state = Sleeping;
    416404        THREAD->sleep_queue = wq;
    417        
    418         irq_spinlock_unlock(&THREAD->lock, false);
    419        
     405
     406        spinlock_unlock(&THREAD->lock);
     407
    420408        /* wq->lock is released in scheduler_separated_stack() */
    421         scheduler();
     409        scheduler(); 
    422410       
    423411        return ESYNCH_OK_BLOCKED;
    424412}
     413
    425414
    426415/** Wake up first thread sleeping in a wait queue
     
    432421 * timeout.
    433422 *
    434  * @param wq   Pointer to wait queue.
    435  * @param mode Wakeup mode.
    436  *
     423 * @param wq            Pointer to wait queue.
     424 * @param mode          Wakeup mode.
    437425 */
    438426void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode)
    439427{
    440         irq_spinlock_lock(&wq->lock, true);
     428        ipl_t ipl;
     429
     430        ipl = interrupts_disable();
     431        spinlock_lock(&wq->lock);
     432
    441433        _waitq_wakeup_unsafe(wq, mode);
    442         irq_spinlock_unlock(&wq->lock, true);
     434
     435        spinlock_unlock(&wq->lock);
     436        interrupts_restore(ipl);
    443437}
    444438
     
    448442 * assumes wq->lock is already locked and interrupts are already disabled.
    449443 *
    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  *
     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.
    457450 */
    458451void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode)
    459452{
     453        thread_t *t;
    460454        size_t count = 0;
    461455
    462         ASSERT(interrupts_disabled());
    463         ASSERT(irq_spinlock_locked(&wq->lock));
    464        
    465 loop:
     456loop:   
    466457        if (list_empty(&wq->head)) {
    467458                wq->missed_wakeups++;
    468                 if ((count) && (mode == WAKEUP_ALL))
     459                if (count && mode == WAKEUP_ALL)
    469460                        wq->missed_wakeups--;
    470                
    471461                return;
    472462        }
    473        
     463
    474464        count++;
    475         thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link);
     465        t = list_get_instance(wq->head.next, thread_t, wq_link);
    476466       
    477467        /*
     
    485475         * invariant must hold:
    486476         *
    487          * thread->sleep_queue != NULL <=> thread sleeps in a wait queue
     477         * t->sleep_queue != NULL <=> t sleeps in a wait queue
    488478         *
    489479         * For an observer who locks the thread, the invariant
    490480         * holds only when the lock is held prior to removing
    491481         * it from the wait queue.
    492          *
    493482         */
    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        
     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
    506493        if (mode == WAKEUP_ALL)
    507494                goto loop;
    508495}
    509496
    510 /** Get the missed wakeups count.
    511  *
    512  * @param wq    Pointer to wait queue.
    513  * @return      The wait queue's missed_wakeups count.
    514  */
    515 int 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  */
    531 void 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 
    538497/** @}
    539498 */
Note: See TracChangeset for help on using the changeset viewer.