Ignore:
File:
1 edited

Legend:

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

    rdf58e44 r6c4a56f  
    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
     
    5049#include <proc/scheduler.h>
    5150#include <arch/asm.h>
    52 #include <typedefs.h>
     51#include <arch/types.h>
    5352#include <time/timeout.h>
    5453#include <arch.h>
    5554#include <context.h>
    5655#include <adt/list.h>
    57 #include <arch/cycle.h>
    58 
    59 static void waitq_sleep_timed_out(void *);
     56
     57static void waitq_sleep_timed_out(void *data);
    6058
    6159/** Initialize wait queue
     
    6361 * Initialize wait queue.
    6462 *
    65  * @param wq Pointer to wait queue to be initialized.
    66  *
     63 * @param wq            Pointer to wait queue to be initialized.
    6764 */
    6865void waitq_initialize(waitq_t *wq)
    6966{
    70         irq_spinlock_initialize(&wq->lock, "wq.lock");
     67        spinlock_initialize(&wq->lock, "waitq_lock");
    7168        list_initialize(&wq->head);
    7269        wq->missed_wakeups = 0;
     
    8380 * timeout at all.
    8481 *
    85  * @param data Pointer to the thread that called waitq_sleep_timeout().
    86  *
     82 * @param data          Pointer to the thread that called waitq_sleep_timeout().
    8783 */
    8884void waitq_sleep_timed_out(void *data)
    8985{
    90         thread_t *thread = (thread_t *) data;
     86        thread_t *t = (thread_t *) data;
     87        waitq_t *wq;
    9188        bool do_wakeup = false;
    9289        DEADLOCK_PROBE_INIT(p_wqlock);
    93        
    94         irq_spinlock_lock(&threads_lock, false);
    95         if (!thread_exists(thread))
     90
     91        spinlock_lock(&threads_lock);
     92        if (!thread_exists(t))
    9693                goto out;
    97        
     94
    9895grab_locks:
    99         irq_spinlock_lock(&thread->lock, false);
    100        
     96        spinlock_lock(&t->lock);
     97        if ((wq = t->sleep_queue)) {            /* assignment */
     98                if (!spinlock_trylock(&wq->lock)) {
     99                        spinlock_unlock(&t->lock);
     100                        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                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       
     114        if (do_wakeup)
     115                thread_ready(t);
     116
     117out:
     118        spinlock_unlock(&threads_lock);
     119}
     120
     121/** Interrupt sleeping thread.
     122 *
     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 */
     128void waitq_interrupt_sleep(thread_t *t)
     129{
    101130        waitq_t *wq;
    102         if ((wq = thread->sleep_queue)) {  /* Assignment */
    103                 if (!irq_spinlock_trylock(&wq->lock)) {
    104                         irq_spinlock_unlock(&thread->lock, false);
    105                         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;
    112                 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);
    119        
    120         if (do_wakeup)
    121                 thread_ready(thread);
    122        
    123 out:
    124         irq_spinlock_unlock(&threads_lock, false);
    125 }
    126 
    127 /** Interrupt sleeping thread.
    128  *
    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  */
    139 void waitq_interrupt_sleep(thread_t *thread)
    140 {
    141131        bool do_wakeup = false;
     132        ipl_t ipl;
    142133        DEADLOCK_PROBE_INIT(p_wqlock);
    143        
    144         /*
    145          * The thread is quaranteed to exist because
    146          * threads_lock is held.
    147          */
    148        
     134
     135        ipl = interrupts_disable();
     136        spinlock_lock(&threads_lock);
     137        if (!thread_exists(t))
     138                goto out;
     139
    149140grab_locks:
    150         irq_spinlock_lock(&thread->lock, false);
    151        
    152         waitq_t *wq;
    153         if ((wq = thread->sleep_queue)) {  /* Assignment */
    154                 if (!(thread->sleep_interruptible)) {
     141        spinlock_lock(&t->lock);
     142        if ((wq = t->sleep_queue)) {            /* assignment */
     143                if (!(t->sleep_interruptible)) {
    155144                        /*
    156145                         * The sleep cannot be interrupted.
    157146                         */
    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);
     147                        spinlock_unlock(&t->lock);
     148                        goto out;
     149                }
     150                       
     151                if (!spinlock_trylock(&wq->lock)) {
     152                        spinlock_unlock(&t->lock);
    165153                        DEADLOCK_PROBE(p_wqlock, DEADLOCK_THRESHOLD);
    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;
     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;
    175162                do_wakeup = true;
    176                 thread->sleep_queue = NULL;
    177                 irq_spinlock_unlock(&wq->lock, false);
    178         }
    179        
    180         irq_spinlock_unlock(&thread->lock, false);
    181        
     163                t->sleep_queue = NULL;
     164                spinlock_unlock(&wq->lock);
     165        }
     166        spinlock_unlock(&t->lock);
     167
    182168        if (do_wakeup)
    183                 thread_ready(thread);
     169                thread_ready(t);
     170
     171out:
     172        spinlock_unlock(&threads_lock);
     173        interrupts_restore(ipl);
    184174}
    185175
     
    189179 * is sleeping interruptibly.
    190180 *
    191  * @param wq Pointer to wait queue.
    192  *
     181 * @param wq            Pointer to wait queue.
    193182 */
    194183void waitq_unsleep(waitq_t *wq)
    195184{
    196         irq_spinlock_lock(&wq->lock, true);
    197        
     185        ipl_t ipl;
     186
     187        ipl = interrupts_disable();
     188        spinlock_lock(&wq->lock);
     189
    198190        if (!list_empty(&wq->head)) {
    199                 thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link);
     191                thread_t *t;
    200192               
    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))
     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}
    222208
    223209/** Sleep until either wakeup, timeout or interruption occurs
     
    231217 * and all the *_timeout() functions use it.
    232218 *
    233  * @param wq    Pointer to wait queue.
    234  * @param usec  Timeout in microseconds.
    235  * @param flags Specify mode of the sleep.
     219 * @param wq            Pointer to wait queue.
     220 * @param usec          Timeout in microseconds.
     221 * @param flags         Specify mode of the sleep.
    236222 *
    237223 * The sleep can be interrupted only if the
    238224 * SYNCH_FLAGS_INTERRUPTIBLE bit is specified in flags.
    239  *
     225 * 
    240226 * If usec is greater than zero, regardless of the value of the
    241227 * SYNCH_FLAGS_NON_BLOCKING bit in flags, the call will not return until either
    242  * timeout, interruption or wakeup comes.
     228 * timeout, interruption or wakeup comes. 
    243229 *
    244230 * If usec is zero and the SYNCH_FLAGS_NON_BLOCKING bit is not set in flags,
     
    248234 * call will immediately return, reporting either success or failure.
    249235 *
    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  */
    262 int 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);
     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 */
     254int 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);
    268261        waitq_sleep_finish(wq, rc, ipl);
    269262        return rc;
     
    275268 * and interrupts disabled.
    276269 *
    277  * @param wq Wait queue.
    278  *
    279  * @return Interrupt level as it existed on entry to this function.
    280  *
     270 * @param wq            Wait queue.
     271 *
     272 * @return              Interrupt level as it existed on entry to this function.
    281273 */
    282274ipl_t waitq_sleep_prepare(waitq_t *wq)
     
    286278restart:
    287279        ipl = interrupts_disable();
    288        
    289         if (THREAD) {  /* Needed during system initiailzation */
     280
     281        if (THREAD) {   /* needed during system initiailzation */
    290282                /*
    291283                 * Busy waiting for a delayed timeout.
     
    294286                 * Simply, the thread is not allowed to go to sleep if
    295287                 * there are timeouts in progress.
    296                  *
    297288                 */
    298                 irq_spinlock_lock(&THREAD->lock, false);
    299                
     289                spinlock_lock(&THREAD->lock);
    300290                if (THREAD->timeout_pending) {
    301                         irq_spinlock_unlock(&THREAD->lock, false);
     291                        spinlock_unlock(&THREAD->lock);
    302292                        interrupts_restore(ipl);
    303293                        goto restart;
    304294                }
    305                
    306                 irq_spinlock_unlock(&THREAD->lock, false);
    307         }
    308        
    309         irq_spinlock_lock(&wq->lock, false);
     295                spinlock_unlock(&THREAD->lock);
     296        }
     297                                                                                                       
     298        spinlock_lock(&wq->lock);
    310299        return ipl;
    311300}
     
    317306 * lock is released.
    318307 *
    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  *
     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().
    323311 */
    324312void waitq_sleep_finish(waitq_t *wq, int rc, ipl_t ipl)
     
    327315        case ESYNCH_WOULD_BLOCK:
    328316        case ESYNCH_OK_ATOMIC:
    329                 irq_spinlock_unlock(&wq->lock, false);
     317                spinlock_unlock(&wq->lock);
    330318                break;
    331319        default:
    332320                break;
    333321        }
    334        
    335322        interrupts_restore(ipl);
    336323}
     
    342329 * and followed by a call to waitq_sleep_finish().
    343330 *
    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  */
    351 int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, unsigned int flags)
    352 {
    353         /* Checks whether to go to sleep at all */
     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 */
     337int waitq_sleep_timeout_unsafe(waitq_t *wq, uint32_t usec, int flags)
     338{
     339        /* checks whether to go to sleep at all */
    354340        if (wq->missed_wakeups) {
    355341                wq->missed_wakeups--;
    356342                return ESYNCH_OK_ATOMIC;
    357         } else {
    358                 if (PARAM_NON_BLOCKING(flags, usec)) {
    359                         /* Return immediatelly instead of going to sleep */
     343        }
     344        else {
     345                if ((flags & SYNCH_FLAGS_NON_BLOCKING) && (usec == 0)) {
     346                        /* return immediatelly instead of going to sleep */
    360347                        return ESYNCH_WOULD_BLOCK;
    361348                }
     
    364351        /*
    365352         * Now we are firmly decided to go to sleep.
    366          *
    367353         */
    368         irq_spinlock_lock(&THREAD->lock, false);
    369        
     354        spinlock_lock(&THREAD->lock);
     355
    370356        if (flags & SYNCH_FLAGS_INTERRUPTIBLE) {
     357
    371358                /*
    372359                 * If the thread was already interrupted,
     
    374361                 */
    375362                if (THREAD->interrupted) {
    376                         irq_spinlock_unlock(&THREAD->lock, false);
    377                         irq_spinlock_unlock(&wq->lock, false);
     363                        spinlock_unlock(&THREAD->lock);
     364                        spinlock_unlock(&wq->lock);
    378365                        return ESYNCH_INTERRUPTED;
    379366                }
    380                
     367
    381368                /*
    382369                 * Set context that will be restored if the sleep
     
    386373                if (!context_save(&THREAD->sleep_interruption_context)) {
    387374                        /* Short emulation of scheduler() return code. */
    388                         THREAD->last_cycle = get_cycle();
    389                         irq_spinlock_unlock(&THREAD->lock, false);
     375                        spinlock_unlock(&THREAD->lock);
    390376                        return ESYNCH_INTERRUPTED;
    391377                }
    392         } else
     378
     379        } else {
    393380                THREAD->sleep_interruptible = false;
    394        
     381        }
     382
    395383        if (usec) {
    396384                /* We use the timeout variant. */
    397385                if (!context_save(&THREAD->sleep_timeout_context)) {
    398386                        /* Short emulation of scheduler() return code. */
    399                         THREAD->last_cycle = get_cycle();
    400                         irq_spinlock_unlock(&THREAD->lock, false);
     387                        spinlock_unlock(&THREAD->lock);
    401388                        return ESYNCH_TIMEOUT;
    402389                }
    403                
    404390                THREAD->timeout_pending = true;
    405391                timeout_register(&THREAD->sleep_timeout, (uint64_t) usec,
    406392                    waitq_sleep_timed_out, THREAD);
    407393        }
    408        
     394
    409395        list_append(&THREAD->wq_link, &wq->head);
    410        
     396
    411397        /*
    412398         * Suspend execution.
    413          *
    414399         */
    415400        THREAD->state = Sleeping;
    416401        THREAD->sleep_queue = wq;
    417        
    418         irq_spinlock_unlock(&THREAD->lock, false);
    419        
     402
     403        spinlock_unlock(&THREAD->lock);
     404
    420405        /* wq->lock is released in scheduler_separated_stack() */
    421         scheduler();
     406        scheduler(); 
    422407       
    423408        return ESYNCH_OK_BLOCKED;
    424409}
     410
    425411
    426412/** Wake up first thread sleeping in a wait queue
     
    432418 * timeout.
    433419 *
    434  * @param wq   Pointer to wait queue.
    435  * @param mode Wakeup mode.
    436  *
     420 * @param wq            Pointer to wait queue.
     421 * @param mode          Wakeup mode.
    437422 */
    438423void waitq_wakeup(waitq_t *wq, wakeup_mode_t mode)
    439424{
    440         irq_spinlock_lock(&wq->lock, true);
     425        ipl_t ipl;
     426
     427        ipl = interrupts_disable();
     428        spinlock_lock(&wq->lock);
     429
    441430        _waitq_wakeup_unsafe(wq, mode);
    442         irq_spinlock_unlock(&wq->lock, true);
     431
     432        spinlock_unlock(&wq->lock);
     433        interrupts_restore(ipl);
    443434}
    444435
     
    448439 * assumes wq->lock is already locked and interrupts are already disabled.
    449440 *
    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  *
     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.
    457447 */
    458448void _waitq_wakeup_unsafe(waitq_t *wq, wakeup_mode_t mode)
    459449{
     450        thread_t *t;
    460451        size_t count = 0;
    461452
    462         ASSERT(interrupts_disabled());
    463         ASSERT(irq_spinlock_locked(&wq->lock));
    464        
    465 loop:
     453loop:   
    466454        if (list_empty(&wq->head)) {
    467455                wq->missed_wakeups++;
    468                 if ((count) && (mode == WAKEUP_ALL))
     456                if (count && mode == WAKEUP_ALL)
    469457                        wq->missed_wakeups--;
    470                
    471458                return;
    472459        }
    473        
     460
    474461        count++;
    475         thread_t *thread = list_get_instance(wq->head.next, thread_t, wq_link);
     462        t = list_get_instance(wq->head.next, thread_t, wq_link);
    476463       
    477464        /*
     
    485472         * invariant must hold:
    486473         *
    487          * thread->sleep_queue != NULL <=> thread sleeps in a wait queue
     474         * t->sleep_queue != NULL <=> t sleeps in a wait queue
    488475         *
    489476         * For an observer who locks the thread, the invariant
    490477         * holds only when the lock is held prior to removing
    491478         * it from the wait queue.
    492          *
    493479         */
    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        
     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
    506490        if (mode == WAKEUP_ALL)
    507491                goto loop;
    508492}
    509493
    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 
    538494/** @}
    539495 */
Note: See TracChangeset for help on using the changeset viewer.