nsprpub/pr/src/md/os2/os2cv.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 /*
michael@0 7 * os2cv.c -- OS/2 Machine-Dependent Code for Condition Variables
michael@0 8 *
michael@0 9 * We implement our own condition variable wait queue. Each thread
michael@0 10 * has a semaphore object (thread->md.blocked_sema) to block on while
michael@0 11 * waiting on a condition variable.
michael@0 12 *
michael@0 13 * We use a deferred condition notify algorithm. When PR_NotifyCondVar
michael@0 14 * or PR_NotifyAllCondVar is called, the condition notifies are simply
michael@0 15 * recorded in the _MDLock structure. We defer the condition notifies
michael@0 16 * until right after we unlock the lock. This way the awakened threads
michael@0 17 * have a better chance to reaquire the lock.
michael@0 18 */
michael@0 19
michael@0 20 #include "primpl.h"
michael@0 21
michael@0 22 /*
michael@0 23 * AddThreadToCVWaitQueueInternal --
michael@0 24 *
michael@0 25 * Add the thread to the end of the condition variable's wait queue.
michael@0 26 * The CV's lock must be locked when this function is called.
michael@0 27 */
michael@0 28
michael@0 29 static void
michael@0 30 AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
michael@0 31 {
michael@0 32 PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
michael@0 33 || (cv->waitTail == NULL && cv->waitHead == NULL));
michael@0 34 cv->nwait += 1;
michael@0 35 thred->md.inCVWaitQueue = PR_TRUE;
michael@0 36 thred->md.next = NULL;
michael@0 37 thred->md.prev = cv->waitTail;
michael@0 38 if (cv->waitHead == NULL) {
michael@0 39 cv->waitHead = thred;
michael@0 40 } else {
michael@0 41 cv->waitTail->md.next = thred;
michael@0 42 }
michael@0 43 cv->waitTail = thred;
michael@0 44 }
michael@0 45
michael@0 46 /*
michael@0 47 * md_UnlockAndPostNotifies --
michael@0 48 *
michael@0 49 * Unlock the lock, and then do the deferred condition notifies.
michael@0 50 * If waitThred and waitCV are not NULL, waitThred is added to
michael@0 51 * the wait queue of waitCV before the lock is unlocked.
michael@0 52 *
michael@0 53 * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
michael@0 54 * the two places where a lock is unlocked.
michael@0 55 */
michael@0 56 void
michael@0 57 md_UnlockAndPostNotifies(
michael@0 58 _MDLock *lock,
michael@0 59 PRThread *waitThred,
michael@0 60 _MDCVar *waitCV)
michael@0 61 {
michael@0 62 PRIntn index;
michael@0 63 _MDNotified post;
michael@0 64 _MDNotified *notified, *prev = NULL;
michael@0 65
michael@0 66 /*
michael@0 67 * Time to actually notify any conditions that were affected
michael@0 68 * while the lock was held. Get a copy of the list that's in
michael@0 69 * the lock structure and then zero the original. If it's
michael@0 70 * linked to other such structures, we own that storage.
michael@0 71 */
michael@0 72 post = lock->notified; /* a safe copy; we own the lock */
michael@0 73
michael@0 74 #if defined(DEBUG)
michael@0 75 memset(&lock->notified, 0, sizeof(_MDNotified)); /* reset */
michael@0 76 #else
michael@0 77 lock->notified.length = 0; /* these are really sufficient */
michael@0 78 lock->notified.link = NULL;
michael@0 79 #endif
michael@0 80
michael@0 81 /*
michael@0 82 * Figure out how many threads we need to wake up.
michael@0 83 */
michael@0 84 notified = &post; /* this is where we start */
michael@0 85 do {
michael@0 86 for (index = 0; index < notified->length; ++index) {
michael@0 87 _MDCVar *cv = notified->cv[index].cv;
michael@0 88 PRThread *thred;
michael@0 89 int i;
michael@0 90
michael@0 91 /* Fast special case: no waiting threads */
michael@0 92 if (cv->waitHead == NULL) {
michael@0 93 notified->cv[index].notifyHead = NULL;
michael@0 94 continue;
michael@0 95 }
michael@0 96
michael@0 97 /* General case */
michael@0 98 if (-1 == notified->cv[index].times) {
michael@0 99 /* broadcast */
michael@0 100 thred = cv->waitHead;
michael@0 101 while (thred != NULL) {
michael@0 102 thred->md.inCVWaitQueue = PR_FALSE;
michael@0 103 thred = thred->md.next;
michael@0 104 }
michael@0 105 notified->cv[index].notifyHead = cv->waitHead;
michael@0 106 cv->waitHead = cv->waitTail = NULL;
michael@0 107 cv->nwait = 0;
michael@0 108 } else {
michael@0 109 thred = cv->waitHead;
michael@0 110 i = notified->cv[index].times;
michael@0 111 while (thred != NULL && i > 0) {
michael@0 112 thred->md.inCVWaitQueue = PR_FALSE;
michael@0 113 thred = thred->md.next;
michael@0 114 i--;
michael@0 115 }
michael@0 116 notified->cv[index].notifyHead = cv->waitHead;
michael@0 117 cv->waitHead = thred;
michael@0 118 if (cv->waitHead == NULL) {
michael@0 119 cv->waitTail = NULL;
michael@0 120 } else {
michael@0 121 if (cv->waitHead->md.prev != NULL) {
michael@0 122 cv->waitHead->md.prev->md.next = NULL;
michael@0 123 cv->waitHead->md.prev = NULL;
michael@0 124 }
michael@0 125 }
michael@0 126 cv->nwait -= notified->cv[index].times - i;
michael@0 127 }
michael@0 128 }
michael@0 129 notified = notified->link;
michael@0 130 } while (NULL != notified);
michael@0 131
michael@0 132 if (waitThred) {
michael@0 133 AddThreadToCVWaitQueueInternal(waitThred, waitCV);
michael@0 134 }
michael@0 135
michael@0 136 /* Release the lock before notifying */
michael@0 137 DosReleaseMutexSem(lock->mutex);
michael@0 138
michael@0 139 notified = &post; /* this is where we start */
michael@0 140 do {
michael@0 141 for (index = 0; index < notified->length; ++index) {
michael@0 142 PRThread *thred;
michael@0 143 PRThread *next;
michael@0 144
michael@0 145 thred = notified->cv[index].notifyHead;
michael@0 146 while (thred != NULL) {
michael@0 147 BOOL rv;
michael@0 148
michael@0 149 next = thred->md.next;
michael@0 150 thred->md.prev = thred->md.next = NULL;
michael@0 151 rv = DosPostEventSem(thred->md.blocked_sema);
michael@0 152 PR_ASSERT(rv == NO_ERROR);
michael@0 153 thred = next;
michael@0 154 }
michael@0 155 }
michael@0 156 prev = notified;
michael@0 157 notified = notified->link;
michael@0 158 if (&post != prev) PR_DELETE(prev);
michael@0 159 } while (NULL != notified);
michael@0 160 }
michael@0 161
michael@0 162 /*
michael@0 163 * Notifies just get posted to the protecting mutex. The
michael@0 164 * actual notification is done when the lock is released so that
michael@0 165 * MP systems don't contend for a lock that they can't have.
michael@0 166 */
michael@0 167 static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
michael@0 168 PRBool broadcast)
michael@0 169 {
michael@0 170 PRIntn index = 0;
michael@0 171 _MDNotified *notified = &lock->notified;
michael@0 172
michael@0 173 while (1) {
michael@0 174 for (index = 0; index < notified->length; ++index) {
michael@0 175 if (notified->cv[index].cv == cvar) {
michael@0 176 if (broadcast) {
michael@0 177 notified->cv[index].times = -1;
michael@0 178 } else if (-1 != notified->cv[index].times) {
michael@0 179 notified->cv[index].times += 1;
michael@0 180 }
michael@0 181 return;
michael@0 182 }
michael@0 183 }
michael@0 184 /* if not full, enter new CV in this array */
michael@0 185 if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
michael@0 186
michael@0 187 /* if there's no link, create an empty array and link it */
michael@0 188 if (NULL == notified->link) {
michael@0 189 notified->link = PR_NEWZAP(_MDNotified);
michael@0 190 }
michael@0 191
michael@0 192 notified = notified->link;
michael@0 193 }
michael@0 194
michael@0 195 /* A brand new entry in the array */
michael@0 196 notified->cv[index].times = (broadcast) ? -1 : 1;
michael@0 197 notified->cv[index].cv = cvar;
michael@0 198 notified->length += 1;
michael@0 199 }
michael@0 200
michael@0 201 /*
michael@0 202 * _PR_MD_NEW_CV() -- Creating new condition variable
michael@0 203 * ... Solaris uses cond_init() in similar function.
michael@0 204 *
michael@0 205 * returns: -1 on failure
michael@0 206 * 0 when it succeeds.
michael@0 207 *
michael@0 208 */
michael@0 209 PRInt32
michael@0 210 _PR_MD_NEW_CV(_MDCVar *cv)
michael@0 211 {
michael@0 212 cv->magic = _MD_MAGIC_CV;
michael@0 213 /*
michael@0 214 * The waitHead, waitTail, and nwait fields are zeroed
michael@0 215 * when the PRCondVar structure is created.
michael@0 216 */
michael@0 217 return 0;
michael@0 218 }
michael@0 219
michael@0 220 void _PR_MD_FREE_CV(_MDCVar *cv)
michael@0 221 {
michael@0 222 cv->magic = (PRUint32)-1;
michael@0 223 return;
michael@0 224 }
michael@0 225
michael@0 226 /*
michael@0 227 * _PR_MD_WAIT_CV() -- Wait on condition variable
michael@0 228 */
michael@0 229 void
michael@0 230 _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
michael@0 231 {
michael@0 232 PRThread *thred = _PR_MD_CURRENT_THREAD();
michael@0 233 ULONG rv, count;
michael@0 234 ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
michael@0 235 SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(timeout);
michael@0 236
michael@0 237 /*
michael@0 238 * If we have pending notifies, post them now.
michael@0 239 */
michael@0 240 if (0 != lock->notified.length) {
michael@0 241 md_UnlockAndPostNotifies(lock, thred, cv);
michael@0 242 } else {
michael@0 243 AddThreadToCVWaitQueueInternal(thred, cv);
michael@0 244 DosReleaseMutexSem(lock->mutex);
michael@0 245 }
michael@0 246
michael@0 247 /* Wait for notification or timeout; don't really care which */
michael@0 248 rv = DosWaitEventSem(thred->md.blocked_sema, msecs);
michael@0 249 if (rv == NO_ERROR) {
michael@0 250 DosResetEventSem(thred->md.blocked_sema, &count);
michael@0 251 }
michael@0 252
michael@0 253 DosRequestMutexSem((lock->mutex), SEM_INDEFINITE_WAIT);
michael@0 254
michael@0 255 PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT);
michael@0 256
michael@0 257 if(rv == ERROR_TIMEOUT)
michael@0 258 {
michael@0 259 if (thred->md.inCVWaitQueue) {
michael@0 260 PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
michael@0 261 || (cv->waitTail == NULL && cv->waitHead == NULL));
michael@0 262 cv->nwait -= 1;
michael@0 263 thred->md.inCVWaitQueue = PR_FALSE;
michael@0 264 if (cv->waitHead == thred) {
michael@0 265 cv->waitHead = thred->md.next;
michael@0 266 if (cv->waitHead == NULL) {
michael@0 267 cv->waitTail = NULL;
michael@0 268 } else {
michael@0 269 cv->waitHead->md.prev = NULL;
michael@0 270 }
michael@0 271 } else {
michael@0 272 PR_ASSERT(thred->md.prev != NULL);
michael@0 273 thred->md.prev->md.next = thred->md.next;
michael@0 274 if (thred->md.next != NULL) {
michael@0 275 thred->md.next->md.prev = thred->md.prev;
michael@0 276 } else {
michael@0 277 PR_ASSERT(cv->waitTail == thred);
michael@0 278 cv->waitTail = thred->md.prev;
michael@0 279 }
michael@0 280 }
michael@0 281 thred->md.next = thred->md.prev = NULL;
michael@0 282 } else {
michael@0 283 /*
michael@0 284 * This thread must have been notified, but the
michael@0 285 * SemRelease call happens after SemRequest
michael@0 286 * times out. Wait on the semaphore again to make it
michael@0 287 * non-signaled. We assume this wait won't take long.
michael@0 288 */
michael@0 289 rv = DosWaitEventSem(thred->md.blocked_sema, SEM_INDEFINITE_WAIT);
michael@0 290 if (rv == NO_ERROR) {
michael@0 291 DosResetEventSem(thred->md.blocked_sema, &count);
michael@0 292 }
michael@0 293 PR_ASSERT(rv == NO_ERROR);
michael@0 294 }
michael@0 295 }
michael@0 296 PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
michael@0 297 return;
michael@0 298 } /* --- end _PR_MD_WAIT_CV() --- */
michael@0 299
michael@0 300 void
michael@0 301 _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
michael@0 302 {
michael@0 303 md_PostNotifyToCvar(cv, lock, PR_FALSE);
michael@0 304 return;
michael@0 305 }
michael@0 306
michael@0 307 PRStatus
michael@0 308 _PR_MD_NEW_LOCK(_MDLock *lock)
michael@0 309 {
michael@0 310 DosCreateMutexSem(0, &(lock->mutex), 0, 0);
michael@0 311 (lock)->notified.length=0;
michael@0 312 (lock)->notified.link=NULL;
michael@0 313 return PR_SUCCESS;
michael@0 314 }
michael@0 315
michael@0 316 void
michael@0 317 _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
michael@0 318 {
michael@0 319 md_PostNotifyToCvar(cv, lock, PR_TRUE);
michael@0 320 return;
michael@0 321 }
michael@0 322
michael@0 323 void _PR_MD_UNLOCK(_MDLock *lock)
michael@0 324 {
michael@0 325 if (0 != lock->notified.length) {
michael@0 326 md_UnlockAndPostNotifies(lock, NULL, NULL);
michael@0 327 } else {
michael@0 328 DosReleaseMutexSem(lock->mutex);
michael@0 329 }
michael@0 330 }

mercurial