Subversion Repositories HelenOS-historic

Rev

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

  1. /*
  2.  * Copyright (C) 2001-2004 Jakub Jermar
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * - Redistributions of source code must retain the above copyright
  10.  *   notice, this list of conditions and the following disclaimer.
  11.  * - Redistributions in binary form must reproduce the above copyright
  12.  *   notice, this list of conditions and the following disclaimer in the
  13.  *   documentation and/or other materials provided with the distribution.
  14.  * - The name of the author may not be used to endorse or promote products
  15.  *   derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. /**
  30.  * @file    rwlock.c
  31.  * @brief   Reader/Writer locks.
  32.  *
  33.  * A reader/writer lock can be held by multiple readers at a time.
  34.  * Or it can be exclusively held by a sole writer at a time.
  35.  *
  36.  * These locks are not recursive.
  37.  * Because technique called direct hand-off is used, neither readers
  38.  * nor writers will suffer starvation.
  39.  *
  40.  * If there is a writer followed by a reader waiting for the rwlock
  41.  * and the writer times out, all leading readers are automatically woken up
  42.  * and allowed in.
  43.  */
  44.  
  45. /*
  46.  * NOTE ON rwlock_holder_type
  47.  * This field is set on an attempt to acquire the exclusive mutex
  48.  * to the respective value depending whether the caller is a reader
  49.  * or a writer. The field is examined only if the thread had been
  50.  * previously blocked on the exclusive mutex. Thus it is save
  51.  * to store the rwlock type in the thread structure, because
  52.  * each thread can block on only one rwlock at a time.
  53.  */
  54.  
  55. #include <synch/rwlock.h>
  56. #include <synch/spinlock.h>
  57. #include <synch/mutex.h>
  58. #include <synch/waitq.h>
  59. #include <synch/synch.h>
  60. #include <adt/list.h>
  61. #include <typedefs.h>
  62. #include <arch/asm.h>
  63. #include <arch.h>
  64. #include <proc/thread.h>
  65. #include <panic.h>
  66.  
  67. #define ALLOW_ALL       0
  68. #define ALLOW_READERS_ONLY  1
  69.  
  70. static void let_others_in(rwlock_t *rwl, int readers_only);
  71. static void release_spinlock(void *arg);
  72.  
  73. /** Initialize reader/writer lock
  74.  *
  75.  * Initialize reader/writer lock.
  76.  *
  77.  * @param rwl Reader/Writer lock.
  78.  */
  79. void rwlock_initialize(rwlock_t *rwl) {
  80.     spinlock_initialize(&rwl->lock, "rwlock_t");
  81.     mutex_initialize(&rwl->exclusive);
  82.     rwl->readers_in = 0;
  83. }
  84.  
  85. /** Acquire reader/writer lock for reading
  86.  *
  87.  * Acquire reader/writer lock for reading.
  88.  * Timeout and willingness to block may be specified.
  89.  *
  90.  * @param rwl Reader/Writer lock.
  91.  * @param usec Timeout in microseconds.
  92.  * @param flags Specify mode of operation.
  93.  *
  94.  * For exact description of possible combinations of
  95.  * usec and flags, see comment for waitq_sleep_timeout().
  96.  *
  97.  * @return See comment for waitq_sleep_timeout().
  98.  */
  99. int _rwlock_write_lock_timeout(rwlock_t *rwl, __u32 usec, int flags)
  100. {
  101.     ipl_t ipl;
  102.     int rc;
  103.    
  104.     ipl = interrupts_disable();
  105.     spinlock_lock(&THREAD->lock);
  106.     THREAD->rwlock_holder_type = RWLOCK_WRITER;
  107.     spinlock_unlock(&THREAD->lock);
  108.     interrupts_restore(ipl);
  109.  
  110.     /*
  111.      * Writers take the easy part.
  112.      * They just need to acquire the exclusive mutex.
  113.      */
  114.     rc = _mutex_lock_timeout(&rwl->exclusive, usec, flags);
  115.     if (SYNCH_FAILED(rc)) {
  116.  
  117.         /*
  118.          * Lock operation timed out or was interrupted.
  119.          * The state of rwl is UNKNOWN at this point.
  120.          * No claims about its holder can be made.
  121.          */
  122.          
  123.         ipl = interrupts_disable();
  124.         spinlock_lock(&rwl->lock);
  125.         /*
  126.          * Now when rwl is locked, we can inspect it again.
  127.          * If it is held by some readers already, we can let
  128.          * readers from the head of the wait queue in.
  129.          */
  130.         if (rwl->readers_in)
  131.             let_others_in(rwl, ALLOW_READERS_ONLY);
  132.         spinlock_unlock(&rwl->lock);
  133.         interrupts_restore(ipl);
  134.     }
  135.    
  136.     return rc;
  137. }
  138.  
  139. /** Acquire reader/writer lock for writing
  140.  *
  141.  * Acquire reader/writer lock for writing.
  142.  * Timeout and willingness to block may be specified.
  143.  *
  144.  * @param rwl Reader/Writer lock.
  145.  * @param usec Timeout in microseconds.
  146.  * @param flags Select mode of operation.
  147.  *
  148.  * For exact description of possible combinations of
  149.  * usec and flags, see comment for waitq_sleep_timeout().
  150.  *
  151.  * @return See comment for waitq_sleep_timeout().
  152.  */
  153. int _rwlock_read_lock_timeout(rwlock_t *rwl, __u32 usec, int flags)
  154. {
  155.     int rc;
  156.     ipl_t ipl;
  157.    
  158.     ipl = interrupts_disable();
  159.     spinlock_lock(&THREAD->lock);
  160.     THREAD->rwlock_holder_type = RWLOCK_READER;
  161.     spinlock_unlock(&THREAD->lock);
  162.  
  163.     spinlock_lock(&rwl->lock);
  164.  
  165.     /*
  166.      * Find out whether we can get what we want without blocking.
  167.      */
  168.     rc = mutex_trylock(&rwl->exclusive);
  169.     if (SYNCH_FAILED(rc)) {
  170.  
  171.         /*
  172.          * 'exclusive' mutex is being held by someone else.
  173.          * If the holder is a reader and there is no one
  174.          * else waiting for it, we can enter the critical
  175.          * section.
  176.          */
  177.  
  178.         if (rwl->readers_in) {
  179.             spinlock_lock(&rwl->exclusive.sem.wq.lock);
  180.             if (list_empty(&rwl->exclusive.sem.wq.head)) {
  181.                 /*
  182.                  * We can enter.
  183.                  */
  184.                 spinlock_unlock(&rwl->exclusive.sem.wq.lock);
  185.                 goto shortcut;
  186.             }
  187.             spinlock_unlock(&rwl->exclusive.sem.wq.lock);
  188.         }
  189.  
  190.         /*
  191.          * In order to prevent a race condition when a reader
  192.          * could block another reader at the head of the waitq,
  193.          * we register a function to unlock rwl->lock
  194.          * after this thread is put asleep.
  195.          */
  196.         #ifdef CONFIG_SMP
  197.         thread_register_call_me(release_spinlock, &rwl->lock);
  198.         #else
  199.         thread_register_call_me(release_spinlock, NULL);
  200.         #endif
  201.                  
  202.         rc = _mutex_lock_timeout(&rwl->exclusive, usec, flags);
  203.         switch (rc) {
  204.             case ESYNCH_WOULD_BLOCK:
  205.                 /*
  206.                  * release_spinlock() wasn't called
  207.                  */
  208.                 thread_register_call_me(NULL, NULL);
  209.                 spinlock_unlock(&rwl->lock);
  210.             case ESYNCH_TIMEOUT:
  211.             case ESYNCH_INTERRUPTED:
  212.                 /*
  213.                  * The sleep timed out.
  214.                  * We just restore interrupt priority level.
  215.                  */
  216.             case ESYNCH_OK_BLOCKED:    
  217.                 /*
  218.                  * We were woken with rwl->readers_in already incremented.
  219.                  * Note that this arrangement avoids race condition between
  220.                  * two concurrent readers. (Race is avoided if 'exclusive' is
  221.                  * locked at the same time as 'readers_in' is incremented.
  222.                  * Same time means both events happen atomically when
  223.                  * rwl->lock is held.)
  224.                  */
  225.                 interrupts_restore(ipl);
  226.                 break;
  227.             case ESYNCH_OK_ATOMIC:
  228.                 panic("_mutex_lock_timeout()==ESYNCH_OK_ATOMIC\n");
  229.                 break;
  230.             default:
  231.                 panic("invalid ESYNCH\n");
  232.                 break;
  233.         }
  234.         return rc;
  235.     }
  236.  
  237. shortcut:
  238.  
  239.     /*
  240.      * We can increment readers_in only if we didn't go to sleep.
  241.      * For sleepers, rwlock_let_others_in() will do the job.
  242.      */
  243.     rwl->readers_in++;
  244.    
  245.     spinlock_unlock(&rwl->lock);
  246.     interrupts_restore(ipl);
  247.  
  248.     return ESYNCH_OK_ATOMIC;
  249. }
  250.  
  251. /** Release reader/writer lock held by writer
  252.  *
  253.  * Release reader/writer lock held by writer.
  254.  * Handoff reader/writer lock ownership directly
  255.  * to waiting readers or a writer.
  256.  *
  257.  * @param rwl Reader/Writer lock.
  258.  */
  259. void rwlock_write_unlock(rwlock_t *rwl)
  260. {
  261.     ipl_t ipl;
  262.    
  263.     ipl = interrupts_disable();
  264.     spinlock_lock(&rwl->lock);
  265.     let_others_in(rwl, ALLOW_ALL);
  266.     spinlock_unlock(&rwl->lock);
  267.     interrupts_restore(ipl);
  268.    
  269. }
  270.  
  271. /** Release reader/writer lock held by reader
  272.  *
  273.  * Release reader/writer lock held by reader.
  274.  * Handoff reader/writer lock ownership directly
  275.  * to a waiting writer or don't do anything if more
  276.  * readers poses the lock.
  277.  *
  278.  * @param rwl Reader/Writer lock.
  279.  */
  280. void rwlock_read_unlock(rwlock_t *rwl)
  281. {
  282.     ipl_t ipl;
  283.  
  284.     ipl = interrupts_disable();
  285.     spinlock_lock(&rwl->lock);
  286.     if (!--rwl->readers_in)
  287.         let_others_in(rwl, ALLOW_ALL);
  288.     spinlock_unlock(&rwl->lock);
  289.     interrupts_restore(ipl);
  290. }
  291.  
  292.  
  293. /** Direct handoff of reader/writer lock ownership.
  294.  *
  295.  * Direct handoff of reader/writer lock ownership
  296.  * to waiting readers or a writer.
  297.  *
  298.  * Must be called with rwl->lock locked.
  299.  * Must be called with interrupts_disable()'d.
  300.  *
  301.  * @param rwl Reader/Writer lock.
  302.  * @param readers_only See the description below.
  303.  *
  304.  * If readers_only is false: (unlock scenario)
  305.  * Let the first sleeper on 'exclusive' mutex in, no matter
  306.  * whether it is a reader or a writer. If there are more leading
  307.  * readers in line, let each of them in.
  308.  *
  309.  * Otherwise: (timeout scenario)
  310.  * Let all leading readers in.
  311.  */
  312. void let_others_in(rwlock_t *rwl, int readers_only)
  313. {
  314.     rwlock_type_t type = RWLOCK_NONE;
  315.     thread_t *t = NULL;
  316.     bool one_more = true;
  317.    
  318.     spinlock_lock(&rwl->exclusive.sem.wq.lock);
  319.  
  320.     if (!list_empty(&rwl->exclusive.sem.wq.head))
  321.         t = list_get_instance(rwl->exclusive.sem.wq.head.next, thread_t, wq_link);
  322.     do {
  323.         if (t) {
  324.             spinlock_lock(&t->lock);
  325.             type = t->rwlock_holder_type;
  326.             spinlock_unlock(&t->lock);         
  327.         }
  328.    
  329.         /*
  330.          * If readers_only is true, we wake all leading readers
  331.          * if and only if rwl is locked by another reader.
  332.          * Assumption: readers_only ==> rwl->readers_in
  333.          */
  334.         if (readers_only && (type != RWLOCK_READER))
  335.             break;
  336.  
  337.  
  338.         if (type == RWLOCK_READER) {
  339.             /*
  340.              * Waking up a reader.
  341.              * We are responsible for incrementing rwl->readers_in for it.
  342.              */
  343.              rwl->readers_in++;
  344.         }
  345.  
  346.         /*
  347.          * Only the last iteration through this loop can increment
  348.          * rwl->exclusive.sem.wq.missed_wakeup's. All preceeding
  349.          * iterations will wake up a thread.
  350.          */
  351.         /* We call the internal version of waitq_wakeup, which
  352.          * relies on the fact that the waitq is already locked.
  353.          */
  354.         _waitq_wakeup_unsafe(&rwl->exclusive.sem.wq, WAKEUP_FIRST);
  355.        
  356.         t = NULL;
  357.         if (!list_empty(&rwl->exclusive.sem.wq.head)) {
  358.             t = list_get_instance(rwl->exclusive.sem.wq.head.next, thread_t, wq_link);
  359.             if (t) {
  360.                 spinlock_lock(&t->lock);
  361.                 if (t->rwlock_holder_type != RWLOCK_READER)
  362.                     one_more = false;
  363.                 spinlock_unlock(&t->lock); 
  364.             }
  365.         }
  366.     } while ((type == RWLOCK_READER) && t && one_more);
  367.  
  368.     spinlock_unlock(&rwl->exclusive.sem.wq.lock);
  369. }
  370.  
  371. /** Release spinlock callback
  372.  *
  373.  * This is a callback function invoked from the scheduler.
  374.  * The callback is registered in _rwlock_read_lock_timeout().
  375.  *
  376.  * @param arg Spinlock.
  377.  */
  378. void release_spinlock(void *arg)
  379. {
  380.     spinlock_unlock((spinlock_t *) arg);
  381. }
  382.