tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prrwlock.c (11790B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "primpl.h"
      7 
      8 #include <string.h>
      9 
     10 #if defined(SOLARIS) && \
     11    (defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY))
     12 
     13 #  include <synch.h>
     14 #  define HAVE_UI_RWLOCK
     15 #  define RWLOCK_T rwlock_t
     16 #  define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL)
     17 #  define RWLOCK_DESTROY(lock) rwlock_destroy(lock)
     18 #  define RWLOCK_RDLOCK(lock) rw_rdlock(lock)
     19 #  define RWLOCK_WRLOCK(lock) rw_wrlock(lock)
     20 #  define RWLOCK_UNLOCK(lock) rw_unlock(lock)
     21 
     22 #endif
     23 
     24 /*
     25 * Reader-writer lock
     26 */
     27 struct PRRWLock {
     28  char* rw_name;    /* lock name                    */
     29  PRUint32 rw_rank; /* rank of the lock             */
     30 
     31 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
     32  RWLOCK_T rw_lock;
     33 #else
     34  PRLock* rw_lock;
     35  PRInt32 rw_lock_cnt; /* ==  0, if unlocked           */
     36  /* == -1, if write-locked       */
     37  /* > 0  , # of read locks       */
     38  PRUint32 rw_reader_cnt;     /* number of waiting readers    */
     39  PRUint32 rw_writer_cnt;     /* number of waiting writers    */
     40  PRCondVar* rw_reader_waitq; /* cvar for readers             */
     41  PRCondVar* rw_writer_waitq; /* cvar for writers             */
     42 #  ifdef DEBUG
     43  PRThread* rw_owner; /* lock owner for write-lock    */
     44 #  endif
     45 #endif
     46 };
     47 
     48 #ifdef DEBUG
     49 #  define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using \
     50                                         rank-order for locks            \
     51                                      */
     52 #endif
     53 
     54 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
     55 
     56 static PRUintn pr_thread_rwlock_key; /* TPD key for lock stack */
     57 static PRUintn pr_thread_rwlock_alloc_failed;
     58 
     59 #  define _PR_RWLOCK_RANK_ORDER_LIMIT 10
     60 
     61 typedef struct thread_rwlock_stack {
     62  PRInt32 trs_index;                                /* top of stack */
     63  PRRWLock* trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock
     64                                                       pointers */
     65 
     66 } thread_rwlock_stack;
     67 
     68 static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock* rwlock);
     69 static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void);
     70 static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock* rwlock);
     71 static void _PR_RELEASE_LOCK_STACK(void* lock_stack);
     72 
     73 #endif
     74 
     75 /*
     76 * Reader/Writer Locks
     77 */
     78 
     79 /*
     80 * PR_NewRWLock
     81 *      Create a reader-writer lock, with the given lock rank and lock name
     82 *
     83 */
     84 
     85 PR_IMPLEMENT(PRRWLock*)
     86 PR_NewRWLock(PRUint32 lock_rank, const char* lock_name) {
     87  PRRWLock* rwlock;
     88 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
     89  int err;
     90 #endif
     91 
     92  if (!_pr_initialized) {
     93    _PR_ImplicitInitialization();
     94  }
     95 
     96  rwlock = PR_NEWZAP(PRRWLock);
     97  if (rwlock == NULL) {
     98    return NULL;
     99  }
    100 
    101  rwlock->rw_rank = lock_rank;
    102  if (lock_name != NULL) {
    103    rwlock->rw_name = (char*)PR_Malloc(strlen(lock_name) + 1);
    104    if (rwlock->rw_name == NULL) {
    105      PR_DELETE(rwlock);
    106      return (NULL);
    107    }
    108    strcpy(rwlock->rw_name, lock_name);
    109  } else {
    110    rwlock->rw_name = NULL;
    111  }
    112 
    113 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    114  err = RWLOCK_INIT(&rwlock->rw_lock);
    115  if (err != 0) {
    116    PR_SetError(PR_UNKNOWN_ERROR, err);
    117    PR_Free(rwlock->rw_name);
    118    PR_DELETE(rwlock);
    119    return NULL;
    120  }
    121  return rwlock;
    122 #else
    123  rwlock->rw_lock = PR_NewLock();
    124  if (rwlock->rw_lock == NULL) {
    125    goto failed;
    126  }
    127  rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
    128  if (rwlock->rw_reader_waitq == NULL) {
    129    goto failed;
    130  }
    131  rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
    132  if (rwlock->rw_writer_waitq == NULL) {
    133    goto failed;
    134  }
    135  rwlock->rw_reader_cnt = 0;
    136  rwlock->rw_writer_cnt = 0;
    137  rwlock->rw_lock_cnt = 0;
    138  return rwlock;
    139 
    140 failed:
    141  if (rwlock->rw_reader_waitq != NULL) {
    142    PR_DestroyCondVar(rwlock->rw_reader_waitq);
    143  }
    144  if (rwlock->rw_lock != NULL) {
    145    PR_DestroyLock(rwlock->rw_lock);
    146  }
    147  PR_Free(rwlock->rw_name);
    148  PR_DELETE(rwlock);
    149  return NULL;
    150 #endif
    151 }
    152 
    153 /*
    154 ** Destroy the given RWLock "lock".
    155 */
    156 PR_IMPLEMENT(void)
    157 PR_DestroyRWLock(PRRWLock* rwlock) {
    158 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    159  int err;
    160  err = RWLOCK_DESTROY(&rwlock->rw_lock);
    161  PR_ASSERT(err == 0);
    162 #else
    163  PR_ASSERT(rwlock->rw_reader_cnt == 0);
    164  PR_DestroyCondVar(rwlock->rw_reader_waitq);
    165  PR_DestroyCondVar(rwlock->rw_writer_waitq);
    166  PR_DestroyLock(rwlock->rw_lock);
    167 #endif
    168  if (rwlock->rw_name != NULL) {
    169    PR_Free(rwlock->rw_name);
    170  }
    171  PR_DELETE(rwlock);
    172 }
    173 
    174 /*
    175 ** Read-lock the RWLock.
    176 */
    177 PR_IMPLEMENT(void)
    178 PR_RWLock_Rlock(PRRWLock* rwlock) {
    179 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    180  int err;
    181 #endif
    182 
    183 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
    184  /*
    185   * assert that rank ordering is not violated; the rank of 'rwlock' should
    186   * be equal to or greater than the highest rank of all the locks held by
    187   * the thread.
    188   */
    189  PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
    190            (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
    191 #endif
    192 
    193 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    194  err = RWLOCK_RDLOCK(&rwlock->rw_lock);
    195  PR_ASSERT(err == 0);
    196 #else
    197  PR_Lock(rwlock->rw_lock);
    198  /*
    199   * wait if write-locked or if a writer is waiting; preference for writers
    200   */
    201  while ((rwlock->rw_lock_cnt < 0) || (rwlock->rw_writer_cnt > 0)) {
    202    rwlock->rw_reader_cnt++;
    203    PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
    204    rwlock->rw_reader_cnt--;
    205  }
    206  /*
    207   * Increment read-lock count
    208   */
    209  rwlock->rw_lock_cnt++;
    210 
    211  PR_Unlock(rwlock->rw_lock);
    212 #endif
    213 
    214 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
    215  /*
    216   * update thread's lock rank
    217   */
    218  if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
    219    _PR_SET_THREAD_RWLOCK_RANK(rwlock);
    220  }
    221 #endif
    222 }
    223 
    224 /*
    225 ** Write-lock the RWLock.
    226 */
    227 PR_IMPLEMENT(void)
    228 PR_RWLock_Wlock(PRRWLock* rwlock) {
    229 #if defined(DEBUG)
    230  PRThread* me = PR_GetCurrentThread();
    231 #endif
    232 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    233  int err;
    234 #endif
    235 
    236 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
    237  /*
    238   * assert that rank ordering is not violated; the rank of 'rwlock' should
    239   * be equal to or greater than the highest rank of all the locks held by
    240   * the thread.
    241   */
    242  PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) ||
    243            (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
    244 #endif
    245 
    246 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    247  err = RWLOCK_WRLOCK(&rwlock->rw_lock);
    248  PR_ASSERT(err == 0);
    249 #else
    250  PR_Lock(rwlock->rw_lock);
    251  /*
    252   * wait if read locked
    253   */
    254  while (rwlock->rw_lock_cnt != 0) {
    255    rwlock->rw_writer_cnt++;
    256    PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
    257    rwlock->rw_writer_cnt--;
    258  }
    259  /*
    260   * apply write lock
    261   */
    262  rwlock->rw_lock_cnt--;
    263  PR_ASSERT(rwlock->rw_lock_cnt == -1);
    264 #  ifdef DEBUG
    265  PR_ASSERT(me != NULL);
    266  rwlock->rw_owner = me;
    267 #  endif
    268  PR_Unlock(rwlock->rw_lock);
    269 #endif
    270 
    271 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
    272  /*
    273   * update thread's lock rank
    274   */
    275  if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
    276    _PR_SET_THREAD_RWLOCK_RANK(rwlock);
    277  }
    278 #endif
    279 }
    280 
    281 /*
    282 ** Unlock the RW lock.
    283 */
    284 PR_IMPLEMENT(void)
    285 PR_RWLock_Unlock(PRRWLock* rwlock) {
    286 #if defined(DEBUG)
    287  PRThread* me = PR_GetCurrentThread();
    288 #endif
    289 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    290  int err;
    291 #endif
    292 
    293 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    294  err = RWLOCK_UNLOCK(&rwlock->rw_lock);
    295  PR_ASSERT(err == 0);
    296 #else
    297  PR_Lock(rwlock->rw_lock);
    298  /*
    299   * lock must be read or write-locked
    300   */
    301  PR_ASSERT(rwlock->rw_lock_cnt != 0);
    302  if (rwlock->rw_lock_cnt > 0) {
    303    /*
    304     * decrement read-lock count
    305     */
    306    rwlock->rw_lock_cnt--;
    307    if (rwlock->rw_lock_cnt == 0) {
    308      /*
    309       * lock is not read-locked anymore; wakeup a waiting writer
    310       */
    311      if (rwlock->rw_writer_cnt > 0) {
    312        PR_NotifyCondVar(rwlock->rw_writer_waitq);
    313      }
    314    }
    315  } else {
    316    PR_ASSERT(rwlock->rw_lock_cnt == -1);
    317 
    318    rwlock->rw_lock_cnt = 0;
    319 #  ifdef DEBUG
    320    PR_ASSERT(rwlock->rw_owner == me);
    321    rwlock->rw_owner = NULL;
    322 #  endif
    323    /*
    324     * wakeup a writer, if present; preference for writers
    325     */
    326    if (rwlock->rw_writer_cnt > 0) {
    327      PR_NotifyCondVar(rwlock->rw_writer_waitq);
    328    }
    329    /*
    330     * else, wakeup all readers, if any
    331     */
    332    else if (rwlock->rw_reader_cnt > 0) {
    333      PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
    334    }
    335  }
    336  PR_Unlock(rwlock->rw_lock);
    337 #endif
    338 
    339 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
    340  /*
    341   * update thread's lock rank
    342   */
    343  if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE) {
    344    _PR_UNSET_THREAD_RWLOCK_RANK(rwlock);
    345  }
    346 #endif
    347  return;
    348 }
    349 
    350 #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
    351 
    352 void _PR_InitRWLocks(void) {}
    353 
    354 #else
    355 
    356 void _PR_InitRWLocks(void) {
    357  /*
    358   * allocated thread-private-data index for rwlock list
    359   */
    360  if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key, _PR_RELEASE_LOCK_STACK) ==
    361      PR_FAILURE) {
    362    pr_thread_rwlock_alloc_failed = 1;
    363    return;
    364  }
    365 }
    366 
    367 /*
    368 * _PR_SET_THREAD_RWLOCK_RANK
    369 *      Set a thread's lock rank, which is the highest of the ranks of all
    370 *      the locks held by the thread. Pointers to the locks are added to a
    371 *      per-thread list, which is anchored off a thread-private data key.
    372 */
    373 
    374 static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock* rwlock) {
    375  thread_rwlock_stack* lock_stack;
    376  PRStatus rv;
    377 
    378  /*
    379   * allocate a lock stack
    380   */
    381  if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) {
    382    lock_stack =
    383        (thread_rwlock_stack*)PR_CALLOC(1 * sizeof(thread_rwlock_stack));
    384    if (lock_stack) {
    385      rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack);
    386      if (rv == PR_FAILURE) {
    387        PR_DELETE(lock_stack);
    388        pr_thread_rwlock_alloc_failed = 1;
    389        return;
    390      }
    391    } else {
    392      pr_thread_rwlock_alloc_failed = 1;
    393      return;
    394    }
    395  }
    396  /*
    397   * add rwlock to lock stack, if limit is not exceeded
    398   */
    399  if (lock_stack) {
    400    if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) {
    401      lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
    402    }
    403  }
    404 }
    405 
    406 static void _PR_RELEASE_LOCK_STACK(void* lock_stack) {
    407  PR_ASSERT(lock_stack);
    408  PR_DELETE(lock_stack);
    409 }
    410 
    411 /*
    412 * _PR_GET_THREAD_RWLOCK_RANK
    413 *
    414 *      return thread's lock rank. If thread-private-data for the lock
    415 *      stack is not allocated, return PR_RWLOCK_RANK_NONE.
    416 */
    417 
    418 static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void) {
    419  thread_rwlock_stack* lock_stack;
    420 
    421  lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
    422  if (lock_stack == NULL || lock_stack->trs_index == 0) {
    423    return (PR_RWLOCK_RANK_NONE);
    424  } else {
    425    return (lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
    426  }
    427 }
    428 
    429 /*
    430 * _PR_UNSET_THREAD_RWLOCK_RANK
    431 *
    432 *      remove the rwlock from the lock stack. Since locks may not be
    433 *      unlocked in a FIFO order, the entire lock stack is searched.
    434 */
    435 
    436 static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock* rwlock) {
    437  thread_rwlock_stack* lock_stack;
    438  int new_index = 0, index, done = 0;
    439 
    440  lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
    441 
    442  PR_ASSERT(lock_stack != NULL);
    443 
    444  for (index = lock_stack->trs_index - 1; index >= 0; index--) {
    445    if (!done && (lock_stack->trs_stack[index] == rwlock)) {
    446      /*
    447       * reset the slot for rwlock
    448       */
    449      lock_stack->trs_stack[index] = NULL;
    450      done = 1;
    451    }
    452    /*
    453     * search for the lowest-numbered empty slot, above which there are
    454     * no non-empty slots
    455     */
    456    if (!new_index && (lock_stack->trs_stack[index] != NULL)) {
    457      new_index = index + 1;
    458    }
    459    if (done && new_index) {
    460      break;
    461    }
    462  }
    463  /*
    464   * set top of stack to highest numbered empty slot
    465   */
    466  lock_stack->trs_index = new_index;
    467 }
    468 
    469 #endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */