tor-browser

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

prmon.c (9105B)


      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 /************************************************************************/
      9 
     10 /*
     11 * Notifies just get posted to the monitor. The actual notification is done
     12 * when the monitor is fully exited so that MP systems don't contend for a
     13 * monitor that they can't enter.
     14 */
     15 static void _PR_PostNotifyToMonitor(PRMonitor* mon, PRBool broadcast) {
     16  PR_ASSERT(mon != NULL);
     17  PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mon);
     18 
     19  /* mon->notifyTimes is protected by the monitor, so we don't need to
     20   * acquire mon->lock.
     21   */
     22  if (broadcast) {
     23    mon->notifyTimes = -1;
     24  } else if (mon->notifyTimes != -1) {
     25    mon->notifyTimes += 1;
     26  }
     27 }
     28 
     29 static void _PR_PostNotifiesFromMonitor(PRCondVar* cv, PRIntn times) {
     30  PRStatus rv;
     31 
     32  /*
     33   * Time to actually notify any waits that were affected while the monitor
     34   * was entered.
     35   */
     36  PR_ASSERT(cv != NULL);
     37  PR_ASSERT(times != 0);
     38  if (times == -1) {
     39    rv = PR_NotifyAllCondVar(cv);
     40    PR_ASSERT(rv == PR_SUCCESS);
     41  } else {
     42    while (times-- > 0) {
     43      rv = PR_NotifyCondVar(cv);
     44      PR_ASSERT(rv == PR_SUCCESS);
     45    }
     46  }
     47 }
     48 
     49 /*
     50 ** Create a new monitor.
     51 */
     52 PR_IMPLEMENT(PRMonitor*) PR_NewMonitor() {
     53  PRMonitor* mon;
     54  PRStatus rv;
     55 
     56  if (!_pr_initialized) {
     57    _PR_ImplicitInitialization();
     58  }
     59 
     60  mon = PR_NEWZAP(PRMonitor);
     61  if (mon == NULL) {
     62    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
     63    return NULL;
     64  }
     65 
     66  rv = _PR_InitLock(&mon->lock);
     67  PR_ASSERT(rv == PR_SUCCESS);
     68  if (rv != PR_SUCCESS) {
     69    goto error1;
     70  }
     71 
     72  mon->owner = NULL;
     73 
     74  rv = _PR_InitCondVar(&mon->entryCV, &mon->lock);
     75  PR_ASSERT(rv == PR_SUCCESS);
     76  if (rv != PR_SUCCESS) {
     77    goto error2;
     78  }
     79 
     80  rv = _PR_InitCondVar(&mon->waitCV, &mon->lock);
     81  PR_ASSERT(rv == PR_SUCCESS);
     82  if (rv != PR_SUCCESS) {
     83    goto error3;
     84  }
     85 
     86  mon->notifyTimes = 0;
     87  mon->entryCount = 0;
     88  mon->name = NULL;
     89  return mon;
     90 
     91 error3:
     92  _PR_FreeCondVar(&mon->entryCV);
     93 error2:
     94  _PR_FreeLock(&mon->lock);
     95 error1:
     96  PR_Free(mon);
     97  return NULL;
     98 }
     99 
    100 PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) {
    101  PRMonitor* mon = PR_NewMonitor();
    102  if (mon) {
    103    mon->name = name;
    104  }
    105  return mon;
    106 }
    107 
    108 /*
    109 ** Destroy a monitor. There must be no thread waiting on the monitor's
    110 ** condition variable. The caller is responsible for guaranteeing that the
    111 ** monitor is no longer in use.
    112 */
    113 PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor* mon) {
    114  PR_ASSERT(mon != NULL);
    115  _PR_FreeCondVar(&mon->waitCV);
    116  _PR_FreeCondVar(&mon->entryCV);
    117  _PR_FreeLock(&mon->lock);
    118 #if defined(DEBUG)
    119  memset(mon, 0xaf, sizeof(PRMonitor));
    120 #endif
    121  PR_Free(mon);
    122 }
    123 
    124 /*
    125 ** Enter the lock associated with the monitor.
    126 */
    127 PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor* mon) {
    128  PRThread* me = _PR_MD_CURRENT_THREAD();
    129  PRStatus rv;
    130 
    131  PR_ASSERT(mon != NULL);
    132  PR_Lock(&mon->lock);
    133  if (mon->entryCount != 0) {
    134    if (mon->owner == me) {
    135      goto done;
    136    }
    137    while (mon->entryCount != 0) {
    138      rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT);
    139      PR_ASSERT(rv == PR_SUCCESS);
    140    }
    141  }
    142  /* and now I have the monitor */
    143  PR_ASSERT(mon->notifyTimes == 0);
    144  PR_ASSERT(mon->owner == NULL);
    145  mon->owner = me;
    146 
    147 done:
    148  mon->entryCount += 1;
    149  rv = PR_Unlock(&mon->lock);
    150  PR_ASSERT(rv == PR_SUCCESS);
    151 }
    152 
    153 /*
    154 ** Test and then enter the lock associated with the monitor if it's not
    155 ** already entered by some other thread. Return PR_FALSE if some other
    156 ** thread owned the lock at the time of the call.
    157 */
    158 PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor* mon) {
    159  PRThread* me = _PR_MD_CURRENT_THREAD();
    160  PRStatus rv;
    161 
    162  PR_ASSERT(mon != NULL);
    163  PR_Lock(&mon->lock);
    164  if (mon->entryCount != 0) {
    165    if (mon->owner == me) {
    166      goto done;
    167    }
    168    rv = PR_Unlock(&mon->lock);
    169    PR_ASSERT(rv == PR_SUCCESS);
    170    return PR_FALSE;
    171  }
    172  /* and now I have the monitor */
    173  PR_ASSERT(mon->notifyTimes == 0);
    174  PR_ASSERT(mon->owner == NULL);
    175  mon->owner = me;
    176 
    177 done:
    178  mon->entryCount += 1;
    179  rv = PR_Unlock(&mon->lock);
    180  PR_ASSERT(rv == PR_SUCCESS);
    181  return PR_TRUE;
    182 }
    183 
    184 /*
    185 ** Exit the lock associated with the monitor once.
    186 */
    187 PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor* mon) {
    188  PRThread* me = _PR_MD_CURRENT_THREAD();
    189  PRStatus rv;
    190 
    191  PR_ASSERT(mon != NULL);
    192  PR_Lock(&mon->lock);
    193  /* the entries should be > 0 and we'd better be the owner */
    194  PR_ASSERT(mon->entryCount > 0);
    195  PR_ASSERT(mon->owner == me);
    196  if (mon->entryCount == 0 || mon->owner != me) {
    197    rv = PR_Unlock(&mon->lock);
    198    PR_ASSERT(rv == PR_SUCCESS);
    199    return PR_FAILURE;
    200  }
    201 
    202  mon->entryCount -= 1; /* reduce by one */
    203  if (mon->entryCount == 0) {
    204    /* and if it transitioned to zero - notify an entry waiter */
    205    /* make the owner unknown */
    206    mon->owner = NULL;
    207    if (mon->notifyTimes != 0) {
    208      _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
    209      mon->notifyTimes = 0;
    210    }
    211    rv = PR_NotifyCondVar(&mon->entryCV);
    212    PR_ASSERT(rv == PR_SUCCESS);
    213  }
    214  rv = PR_Unlock(&mon->lock);
    215  PR_ASSERT(rv == PR_SUCCESS);
    216  return PR_SUCCESS;
    217 }
    218 
    219 /*
    220 ** Return the number of times that the current thread has entered the
    221 ** lock. Returns zero if the current thread has not entered the lock.
    222 */
    223 PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor* mon) {
    224  PRThread* me = _PR_MD_CURRENT_THREAD();
    225  PRStatus rv;
    226  PRIntn count = 0;
    227 
    228  PR_Lock(&mon->lock);
    229  if (mon->owner == me) {
    230    count = mon->entryCount;
    231  }
    232  rv = PR_Unlock(&mon->lock);
    233  PR_ASSERT(rv == PR_SUCCESS);
    234  return count;
    235 }
    236 
    237 PR_IMPLEMENT(void) PR_AssertCurrentThreadInMonitor(PRMonitor* mon) {
    238 #if defined(DEBUG) || defined(FORCE_PR_ASSERT)
    239  PRStatus rv;
    240 
    241  PR_Lock(&mon->lock);
    242  PR_ASSERT(mon->entryCount != 0 && mon->owner == _PR_MD_CURRENT_THREAD());
    243  rv = PR_Unlock(&mon->lock);
    244  PR_ASSERT(rv == PR_SUCCESS);
    245 #endif
    246 }
    247 
    248 /*
    249 ** Wait for a notify on the condition variable. Sleep for "ticks" amount
    250 ** of time (if "tick" is 0 then the sleep is indefinite). While
    251 ** the thread is waiting it exits the monitors lock (as if it called
    252 ** PR_ExitMonitor as many times as it had called PR_EnterMonitor).  When
    253 ** the wait has finished the thread regains control of the monitors lock
    254 ** with the same entry count as before the wait began.
    255 **
    256 ** The thread waiting on the monitor will be resumed when the monitor is
    257 ** notified (assuming the thread is the next in line to receive the
    258 ** notify) or when the "ticks" elapses.
    259 **
    260 ** Returns PR_FAILURE if the caller has not locked the lock associated
    261 ** with the condition variable.
    262 ** This routine can return PR_PENDING_INTERRUPT_ERROR if the waiting thread
    263 ** has been interrupted.
    264 */
    265 PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor* mon, PRIntervalTime ticks) {
    266  PRStatus rv;
    267  PRUint32 saved_entries;
    268  PRThread* saved_owner;
    269 
    270  PR_ASSERT(mon != NULL);
    271  PR_Lock(&mon->lock);
    272  /* the entries better be positive */
    273  PR_ASSERT(mon->entryCount > 0);
    274  /* and it better be owned by us */
    275  PR_ASSERT(mon->owner == _PR_MD_CURRENT_THREAD()); /* XXX return failure */
    276 
    277  /* tuck these away 'till later */
    278  saved_entries = mon->entryCount;
    279  mon->entryCount = 0;
    280  saved_owner = mon->owner;
    281  mon->owner = NULL;
    282  /* If we have pending notifies, post them now. */
    283  if (mon->notifyTimes != 0) {
    284    _PR_PostNotifiesFromMonitor(&mon->waitCV, mon->notifyTimes);
    285    mon->notifyTimes = 0;
    286  }
    287  rv = PR_NotifyCondVar(&mon->entryCV);
    288  PR_ASSERT(rv == PR_SUCCESS);
    289 
    290  rv = PR_WaitCondVar(&mon->waitCV, ticks);
    291  PR_ASSERT(rv == PR_SUCCESS);
    292 
    293  while (mon->entryCount != 0) {
    294    rv = PR_WaitCondVar(&mon->entryCV, PR_INTERVAL_NO_TIMEOUT);
    295    PR_ASSERT(rv == PR_SUCCESS);
    296  }
    297  PR_ASSERT(mon->notifyTimes == 0);
    298  /* reinstate the interesting information */
    299  mon->entryCount = saved_entries;
    300  mon->owner = saved_owner;
    301 
    302  rv = PR_Unlock(&mon->lock);
    303  PR_ASSERT(rv == PR_SUCCESS);
    304  return rv;
    305 }
    306 
    307 /*
    308 ** Notify the highest priority thread waiting on the condition
    309 ** variable. If a thread is waiting on the condition variable (using
    310 ** PR_Wait) then it is awakened and begins waiting on the monitor's lock.
    311 */
    312 PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor* mon) {
    313  _PR_PostNotifyToMonitor(mon, PR_FALSE);
    314  return PR_SUCCESS;
    315 }
    316 
    317 /*
    318 ** Notify all of the threads waiting on the condition variable. All of
    319 ** threads are notified in turn. The highest priority thread will
    320 ** probably acquire the monitor first when the monitor is exited.
    321 */
    322 PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor* mon) {
    323  _PR_PostNotifyToMonitor(mon, PR_TRUE);
    324  return PR_SUCCESS;
    325 }
    326 
    327 /************************************************************************/
    328 
    329 PRUint32 _PR_MonitorToString(PRMonitor* mon, char* buf, PRUint32 buflen) {
    330  PRUint32 nb;
    331 
    332  if (mon->owner) {
    333    nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld", mon,
    334                     mon->owner->id, mon->owner, mon->entryCount);
    335  } else {
    336    nb = PR_snprintf(buf, buflen, "[%p]", mon);
    337  }
    338  return nb;
    339 }