tor-browser

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

prulock.c (11778B)


      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 #if defined(WIN95)
      9 /*
     10 ** Some local variables report warnings on Win95 because the code paths
     11 ** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
     12 ** The pragma suppresses the warning.
     13 **
     14 */
     15 #  pragma warning(disable : 4101)
     16 #endif
     17 
     18 void _PR_InitLocks(void) { _PR_MD_INIT_LOCKS(); }
     19 
     20 /*
     21 ** Deal with delayed interrupts/requested reschedule during interrupt
     22 ** re-enables.
     23 */
     24 void _PR_IntsOn(_PRCPU* cpu) {
     25  PRUintn missed, pri, i;
     26  _PRInterruptTable* it;
     27  PRThread* me;
     28 
     29  PR_ASSERT(cpu); /* Global threads don't have CPUs */
     30  PR_ASSERT(_PR_MD_GET_INTSOFF() > 0);
     31  me = _PR_MD_CURRENT_THREAD();
     32  PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
     33 
     34  /*
     35  ** Process delayed interrupts. This logic is kinda scary because we
     36  ** need to avoid losing an interrupt (it's ok to delay an interrupt
     37  ** until later).
     38  **
     39  ** There are two missed state words. _pr_ints.where indicates to the
     40  ** interrupt handler which state word is currently safe for
     41  ** modification.
     42  **
     43  ** This code scans both interrupt state words, using the where flag
     44  ** to indicate to the interrupt which state word is safe for writing.
     45  ** If an interrupt comes in during a scan the other word will be
     46  ** modified. This modification will be noticed during the next
     47  ** iteration of the loop or during the next call to this routine.
     48  */
     49  for (i = 0; i < 2; i++) {
     50    cpu->where = (1 - i);
     51    missed = cpu->u.missed[i];
     52    if (missed != 0) {
     53      cpu->u.missed[i] = 0;
     54      for (it = _pr_interruptTable; it->name; it++) {
     55        if (missed & it->missed_bit) {
     56          PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("IntsOn[0]: %s intr", it->name));
     57          (*it->handler)();
     58        }
     59      }
     60    }
     61  }
     62 
     63  if (cpu->u.missed[3] != 0) {
     64    _PRCPU* cpu;
     65 
     66    _PR_THREAD_LOCK(me);
     67    me->state = _PR_RUNNABLE;
     68    pri = me->priority;
     69 
     70    cpu = me->cpu;
     71    _PR_RUNQ_LOCK(cpu);
     72    _PR_ADD_RUNQ(me, cpu, pri);
     73    _PR_RUNQ_UNLOCK(cpu);
     74    _PR_THREAD_UNLOCK(me);
     75    _PR_MD_SWITCH_CONTEXT(me);
     76  }
     77 }
     78 
     79 /*
     80 ** Unblock the first runnable waiting thread. Skip over
     81 ** threads that are trying to be suspended
     82 ** Note: Caller must hold _PR_LOCK_LOCK()
     83 */
     84 void _PR_UnblockLockWaiter(PRLock* lock) {
     85  PRThread* t = NULL;
     86  PRThread* me;
     87  PRCList* q;
     88 
     89  q = lock->waitQ.next;
     90  PR_ASSERT(q != &lock->waitQ);
     91  while (q != &lock->waitQ) {
     92    /* Unblock first waiter */
     93    t = _PR_THREAD_CONDQ_PTR(q);
     94 
     95    /*
     96    ** We are about to change the thread's state to runnable and for local
     97    ** threads, we are going to assign a cpu to it.  So, protect thread's
     98    ** data structure.
     99    */
    100    _PR_THREAD_LOCK(t);
    101 
    102    if (t->flags & _PR_SUSPENDING) {
    103      q = q->next;
    104      _PR_THREAD_UNLOCK(t);
    105      continue;
    106    }
    107 
    108    /* Found a runnable thread */
    109    PR_ASSERT(t->state == _PR_LOCK_WAIT);
    110    PR_ASSERT(t->wait.lock == lock);
    111    t->wait.lock = 0;
    112    PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */
    113 
    114    /*
    115    ** If this is a native thread, nothing else to do except to wake it
    116    ** up by calling the machine dependent wakeup routine.
    117    **
    118    ** If this is a local thread, we need to assign it a cpu and
    119    ** put the thread on that cpu's run queue.  There are two cases to
    120    ** take care of.  If the currently running thread is also a local
    121    ** thread, we just assign our own cpu to that thread and put it on
    122    ** the cpu's run queue.  If the the currently running thread is a
    123    ** native thread, we assign the primordial cpu to it (on NT,
    124    ** MD_WAKEUP handles the cpu assignment).
    125    */
    126 
    127    if (!_PR_IS_NATIVE_THREAD(t)) {
    128      t->state = _PR_RUNNABLE;
    129 
    130      me = _PR_MD_CURRENT_THREAD();
    131 
    132      _PR_AddThreadToRunQ(me, t);
    133      _PR_THREAD_UNLOCK(t);
    134    } else {
    135      t->state = _PR_RUNNING;
    136      _PR_THREAD_UNLOCK(t);
    137    }
    138    _PR_MD_WAKEUP_WAITER(t);
    139    break;
    140  }
    141  return;
    142 }
    143 
    144 /************************************************************************/
    145 
    146 PR_IMPLEMENT(PRLock*) PR_NewLock(void) {
    147  PRLock* lock;
    148 
    149  if (!_pr_initialized) {
    150    _PR_ImplicitInitialization();
    151  }
    152 
    153  lock = PR_NEWZAP(PRLock);
    154  if (lock) {
    155    if (_PR_InitLock(lock) != PR_SUCCESS) {
    156      PR_DELETE(lock);
    157      return NULL;
    158    }
    159  }
    160  return lock;
    161 }
    162 
    163 PRStatus _PR_InitLock(PRLock* lock) {
    164  if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) {
    165    return PR_FAILURE;
    166  }
    167  PR_INIT_CLIST(&lock->links);
    168  PR_INIT_CLIST(&lock->waitQ);
    169  return PR_SUCCESS;
    170 }
    171 
    172 /*
    173 ** Destroy the given lock "lock". There is no point in making this race
    174 ** free because if some other thread has the pointer to this lock all
    175 ** bets are off.
    176 */
    177 PR_IMPLEMENT(void) PR_DestroyLock(PRLock* lock) {
    178  _PR_FreeLock(lock);
    179  PR_DELETE(lock);
    180 }
    181 
    182 void _PR_FreeLock(PRLock* lock) {
    183  PR_ASSERT(lock->owner == 0);
    184  _PR_MD_FREE_LOCK(&lock->ilock);
    185 }
    186 
    187 extern PRThread* suspendAllThread;
    188 /*
    189 ** Lock the lock.
    190 */
    191 PR_IMPLEMENT(void) PR_Lock(PRLock* lock) {
    192  PRThread* me = _PR_MD_CURRENT_THREAD();
    193  PRIntn is;
    194  PRThread* t;
    195  PRCList* q;
    196 
    197  PR_ASSERT(me != suspendAllThread);
    198  PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
    199  PR_ASSERT(lock != NULL);
    200 #ifdef _PR_GLOBAL_THREADS_ONLY
    201  _PR_MD_LOCK(&lock->ilock);
    202  PR_ASSERT(lock->owner == 0);
    203  lock->owner = me;
    204  return;
    205 #else /* _PR_GLOBAL_THREADS_ONLY */
    206 
    207  if (_native_threads_only) {
    208    _PR_MD_LOCK(&lock->ilock);
    209    PR_ASSERT(lock->owner == 0);
    210    lock->owner = me;
    211    return;
    212  }
    213 
    214  if (!_PR_IS_NATIVE_THREAD(me)) {
    215    _PR_INTSOFF(is);
    216  }
    217 
    218  PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
    219 
    220 retry:
    221  _PR_LOCK_LOCK(lock);
    222  if (lock->owner == 0) {
    223    /* Just got the lock */
    224    lock->owner = me;
    225    lock->priority = me->priority;
    226    /* Add the granted lock to this owning thread's lock list */
    227    PR_APPEND_LINK(&lock->links, &me->lockList);
    228    _PR_LOCK_UNLOCK(lock);
    229    if (!_PR_IS_NATIVE_THREAD(me)) {
    230      _PR_FAST_INTSON(is);
    231    }
    232    return;
    233  }
    234 
    235  /* If this thread already owns this lock, then it is a deadlock */
    236  PR_ASSERT(lock->owner != me);
    237 
    238  PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
    239 
    240 #  if 0
    241    if (me->priority > lock->owner->priority) {
    242        /*
    243        ** Give the lock owner a priority boost until we get the
    244        ** lock. Record the priority we boosted it to.
    245        */
    246        lock->boostPriority = me->priority;
    247        _PR_SetThreadPriority(lock->owner, me->priority);
    248    }
    249 #  endif
    250 
    251  /*
    252  Add this thread to the asked for lock's list of waiting threads.  We
    253  add this thread thread in the right priority order so when the unlock
    254  occurs, the thread with the higher priority will get the lock.
    255  */
    256  q = lock->waitQ.next;
    257  if (q == &lock->waitQ ||
    258      _PR_THREAD_CONDQ_PTR(q)->priority ==
    259          _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) {
    260    /*
    261     * If all the threads in the lock waitQ have the same priority,
    262     * then avoid scanning the list:  insert the element at the end.
    263     */
    264    q = &lock->waitQ;
    265  } else {
    266    /* Sort thread into lock's waitQ at appropriate point */
    267    /* Now scan the list for where to insert this entry */
    268    while (q != &lock->waitQ) {
    269      t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
    270      if (me->priority > t->priority) {
    271        /* Found a lower priority thread to insert in front of */
    272        break;
    273      }
    274      q = q->next;
    275    }
    276  }
    277  PR_INSERT_BEFORE(&me->waitQLinks, q);
    278 
    279  /*
    280  Now grab the threadLock since we are about to change the state.  We have
    281  to do this since a PR_Suspend or PR_SetThreadPriority type call that takes
    282  a PRThread* as an argument could be changing the state of this thread from
    283  a thread running on a different cpu.
    284  */
    285 
    286  _PR_THREAD_LOCK(me);
    287  me->state = _PR_LOCK_WAIT;
    288  me->wait.lock = lock;
    289  _PR_THREAD_UNLOCK(me);
    290 
    291  _PR_LOCK_UNLOCK(lock);
    292 
    293  _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
    294  goto retry;
    295 
    296 #endif /* _PR_GLOBAL_THREADS_ONLY */
    297 }
    298 
    299 /*
    300 ** Unlock the lock.
    301 */
    302 PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock* lock) {
    303  PRCList* q;
    304  PRThreadPriority pri, boost;
    305  PRIntn is;
    306  PRThread* me = _PR_MD_CURRENT_THREAD();
    307 
    308  PR_ASSERT(lock != NULL);
    309  PR_ASSERT(lock->owner == me);
    310  PR_ASSERT(me != suspendAllThread);
    311  PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
    312  if (lock->owner != me) {
    313    return PR_FAILURE;
    314  }
    315 
    316 #ifdef _PR_GLOBAL_THREADS_ONLY
    317  lock->owner = 0;
    318  _PR_MD_UNLOCK(&lock->ilock);
    319  return PR_SUCCESS;
    320 #else  /* _PR_GLOBAL_THREADS_ONLY */
    321 
    322  if (_native_threads_only) {
    323    lock->owner = 0;
    324    _PR_MD_UNLOCK(&lock->ilock);
    325    return PR_SUCCESS;
    326  }
    327 
    328  if (!_PR_IS_NATIVE_THREAD(me)) {
    329    _PR_INTSOFF(is);
    330  }
    331  _PR_LOCK_LOCK(lock);
    332 
    333  /* Remove the lock from the owning thread's lock list */
    334  PR_REMOVE_LINK(&lock->links);
    335  pri = lock->priority;
    336  boost = lock->boostPriority;
    337  if (boost > pri) {
    338    /*
    339    ** We received a priority boost during the time we held the lock.
    340    ** We need to figure out what priority to move to by scanning
    341    ** down our list of lock's that we are still holding and using
    342    ** the highest boosted priority found.
    343    */
    344    q = me->lockList.next;
    345    while (q != &me->lockList) {
    346      PRLock* ll = _PR_LOCK_PTR(q);
    347      if (ll->boostPriority > pri) {
    348        pri = ll->boostPriority;
    349      }
    350      q = q->next;
    351    }
    352    if (pri != me->priority) {
    353      _PR_SetThreadPriority(me, pri);
    354    }
    355  }
    356 
    357  /* Unblock the first waiting thread */
    358  q = lock->waitQ.next;
    359  if (q != &lock->waitQ) {
    360    _PR_UnblockLockWaiter(lock);
    361  }
    362  lock->boostPriority = PR_PRIORITY_LOW;
    363  lock->owner = 0;
    364  _PR_LOCK_UNLOCK(lock);
    365  if (!_PR_IS_NATIVE_THREAD(me)) {
    366    _PR_INTSON(is);
    367  }
    368  return PR_SUCCESS;
    369 #endif /* _PR_GLOBAL_THREADS_ONLY */
    370 }
    371 
    372 /*
    373 **  If the current thread owns |lock|, this assertion is guaranteed to
    374 **  succeed.  Otherwise, the behavior of this function is undefined.
    375 */
    376 PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock* lock) {
    377  PRThread* me = _PR_MD_CURRENT_THREAD();
    378  PR_ASSERT(lock->owner == me);
    379 }
    380 
    381 /*
    382 ** Test and then lock the lock if it's not already locked by some other
    383 ** thread. Return PR_FALSE if some other thread owned the lock at the
    384 ** time of the call.
    385 */
    386 PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock* lock) {
    387  PRThread* me = _PR_MD_CURRENT_THREAD();
    388  PRBool rv = PR_FALSE;
    389  PRIntn is;
    390 
    391 #ifdef _PR_GLOBAL_THREADS_ONLY
    392  is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
    393  if (is == 0) {
    394    lock->owner = me;
    395    return PR_TRUE;
    396  }
    397  return PR_FALSE;
    398 #else /* _PR_GLOBAL_THREADS_ONLY */
    399 
    400 #  ifndef _PR_LOCAL_THREADS_ONLY
    401  if (_native_threads_only) {
    402    is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
    403    if (is == 0) {
    404      lock->owner = me;
    405      return PR_TRUE;
    406    }
    407    return PR_FALSE;
    408  }
    409 #  endif
    410 
    411  if (!_PR_IS_NATIVE_THREAD(me)) {
    412    _PR_INTSOFF(is);
    413  }
    414 
    415  _PR_LOCK_LOCK(lock);
    416  if (lock->owner == 0) {
    417    /* Just got the lock */
    418    lock->owner = me;
    419    lock->priority = me->priority;
    420    /* Add the granted lock to this owning thread's lock list */
    421    PR_APPEND_LINK(&lock->links, &me->lockList);
    422    rv = PR_TRUE;
    423  }
    424  _PR_LOCK_UNLOCK(lock);
    425 
    426  if (!_PR_IS_NATIVE_THREAD(me)) {
    427    _PR_INTSON(is);
    428  }
    429  return rv;
    430 #endif /* _PR_GLOBAL_THREADS_ONLY */
    431 }
    432 
    433 /************************************************************************/
    434 /************************************************************************/
    435 /***********************ROUTINES FOR DCE EMULATION***********************/
    436 /************************************************************************/
    437 /************************************************************************/
    438 PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock* lock) {
    439  return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE;
    440 }