nsprpub/pr/src/threads/prmon.c

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

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

mercurial