1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/md/os2/os2cv.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,330 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * os2cv.c -- OS/2 Machine-Dependent Code for Condition Variables 1.11 + * 1.12 + * We implement our own condition variable wait queue. Each thread 1.13 + * has a semaphore object (thread->md.blocked_sema) to block on while 1.14 + * waiting on a condition variable. 1.15 + * 1.16 + * We use a deferred condition notify algorithm. When PR_NotifyCondVar 1.17 + * or PR_NotifyAllCondVar is called, the condition notifies are simply 1.18 + * recorded in the _MDLock structure. We defer the condition notifies 1.19 + * until right after we unlock the lock. This way the awakened threads 1.20 + * have a better chance to reaquire the lock. 1.21 + */ 1.22 + 1.23 +#include "primpl.h" 1.24 + 1.25 +/* 1.26 + * AddThreadToCVWaitQueueInternal -- 1.27 + * 1.28 + * Add the thread to the end of the condition variable's wait queue. 1.29 + * The CV's lock must be locked when this function is called. 1.30 + */ 1.31 + 1.32 +static void 1.33 +AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv) 1.34 +{ 1.35 + PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) 1.36 + || (cv->waitTail == NULL && cv->waitHead == NULL)); 1.37 + cv->nwait += 1; 1.38 + thred->md.inCVWaitQueue = PR_TRUE; 1.39 + thred->md.next = NULL; 1.40 + thred->md.prev = cv->waitTail; 1.41 + if (cv->waitHead == NULL) { 1.42 + cv->waitHead = thred; 1.43 + } else { 1.44 + cv->waitTail->md.next = thred; 1.45 + } 1.46 + cv->waitTail = thred; 1.47 +} 1.48 + 1.49 +/* 1.50 + * md_UnlockAndPostNotifies -- 1.51 + * 1.52 + * Unlock the lock, and then do the deferred condition notifies. 1.53 + * If waitThred and waitCV are not NULL, waitThred is added to 1.54 + * the wait queue of waitCV before the lock is unlocked. 1.55 + * 1.56 + * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK, 1.57 + * the two places where a lock is unlocked. 1.58 + */ 1.59 +void 1.60 +md_UnlockAndPostNotifies( 1.61 + _MDLock *lock, 1.62 + PRThread *waitThred, 1.63 + _MDCVar *waitCV) 1.64 +{ 1.65 + PRIntn index; 1.66 + _MDNotified post; 1.67 + _MDNotified *notified, *prev = NULL; 1.68 + 1.69 + /* 1.70 + * Time to actually notify any conditions that were affected 1.71 + * while the lock was held. Get a copy of the list that's in 1.72 + * the lock structure and then zero the original. If it's 1.73 + * linked to other such structures, we own that storage. 1.74 + */ 1.75 + post = lock->notified; /* a safe copy; we own the lock */ 1.76 + 1.77 +#if defined(DEBUG) 1.78 + memset(&lock->notified, 0, sizeof(_MDNotified)); /* reset */ 1.79 +#else 1.80 + lock->notified.length = 0; /* these are really sufficient */ 1.81 + lock->notified.link = NULL; 1.82 +#endif 1.83 + 1.84 + /* 1.85 + * Figure out how many threads we need to wake up. 1.86 + */ 1.87 + notified = &post; /* this is where we start */ 1.88 + do { 1.89 + for (index = 0; index < notified->length; ++index) { 1.90 + _MDCVar *cv = notified->cv[index].cv; 1.91 + PRThread *thred; 1.92 + int i; 1.93 + 1.94 + /* Fast special case: no waiting threads */ 1.95 + if (cv->waitHead == NULL) { 1.96 + notified->cv[index].notifyHead = NULL; 1.97 + continue; 1.98 + } 1.99 + 1.100 + /* General case */ 1.101 + if (-1 == notified->cv[index].times) { 1.102 + /* broadcast */ 1.103 + thred = cv->waitHead; 1.104 + while (thred != NULL) { 1.105 + thred->md.inCVWaitQueue = PR_FALSE; 1.106 + thred = thred->md.next; 1.107 + } 1.108 + notified->cv[index].notifyHead = cv->waitHead; 1.109 + cv->waitHead = cv->waitTail = NULL; 1.110 + cv->nwait = 0; 1.111 + } else { 1.112 + thred = cv->waitHead; 1.113 + i = notified->cv[index].times; 1.114 + while (thred != NULL && i > 0) { 1.115 + thred->md.inCVWaitQueue = PR_FALSE; 1.116 + thred = thred->md.next; 1.117 + i--; 1.118 + } 1.119 + notified->cv[index].notifyHead = cv->waitHead; 1.120 + cv->waitHead = thred; 1.121 + if (cv->waitHead == NULL) { 1.122 + cv->waitTail = NULL; 1.123 + } else { 1.124 + if (cv->waitHead->md.prev != NULL) { 1.125 + cv->waitHead->md.prev->md.next = NULL; 1.126 + cv->waitHead->md.prev = NULL; 1.127 + } 1.128 + } 1.129 + cv->nwait -= notified->cv[index].times - i; 1.130 + } 1.131 + } 1.132 + notified = notified->link; 1.133 + } while (NULL != notified); 1.134 + 1.135 + if (waitThred) { 1.136 + AddThreadToCVWaitQueueInternal(waitThred, waitCV); 1.137 + } 1.138 + 1.139 + /* Release the lock before notifying */ 1.140 + DosReleaseMutexSem(lock->mutex); 1.141 + 1.142 + notified = &post; /* this is where we start */ 1.143 + do { 1.144 + for (index = 0; index < notified->length; ++index) { 1.145 + PRThread *thred; 1.146 + PRThread *next; 1.147 + 1.148 + thred = notified->cv[index].notifyHead; 1.149 + while (thred != NULL) { 1.150 + BOOL rv; 1.151 + 1.152 + next = thred->md.next; 1.153 + thred->md.prev = thred->md.next = NULL; 1.154 + rv = DosPostEventSem(thred->md.blocked_sema); 1.155 + PR_ASSERT(rv == NO_ERROR); 1.156 + thred = next; 1.157 + } 1.158 + } 1.159 + prev = notified; 1.160 + notified = notified->link; 1.161 + if (&post != prev) PR_DELETE(prev); 1.162 + } while (NULL != notified); 1.163 +} 1.164 + 1.165 +/* 1.166 + * Notifies just get posted to the protecting mutex. The 1.167 + * actual notification is done when the lock is released so that 1.168 + * MP systems don't contend for a lock that they can't have. 1.169 + */ 1.170 +static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock, 1.171 + PRBool broadcast) 1.172 +{ 1.173 + PRIntn index = 0; 1.174 + _MDNotified *notified = &lock->notified; 1.175 + 1.176 + while (1) { 1.177 + for (index = 0; index < notified->length; ++index) { 1.178 + if (notified->cv[index].cv == cvar) { 1.179 + if (broadcast) { 1.180 + notified->cv[index].times = -1; 1.181 + } else if (-1 != notified->cv[index].times) { 1.182 + notified->cv[index].times += 1; 1.183 + } 1.184 + return; 1.185 + } 1.186 + } 1.187 + /* if not full, enter new CV in this array */ 1.188 + if (notified->length < _MD_CV_NOTIFIED_LENGTH) break; 1.189 + 1.190 + /* if there's no link, create an empty array and link it */ 1.191 + if (NULL == notified->link) { 1.192 + notified->link = PR_NEWZAP(_MDNotified); 1.193 + } 1.194 + 1.195 + notified = notified->link; 1.196 + } 1.197 + 1.198 + /* A brand new entry in the array */ 1.199 + notified->cv[index].times = (broadcast) ? -1 : 1; 1.200 + notified->cv[index].cv = cvar; 1.201 + notified->length += 1; 1.202 +} 1.203 + 1.204 +/* 1.205 + * _PR_MD_NEW_CV() -- Creating new condition variable 1.206 + * ... Solaris uses cond_init() in similar function. 1.207 + * 1.208 + * returns: -1 on failure 1.209 + * 0 when it succeeds. 1.210 + * 1.211 + */ 1.212 +PRInt32 1.213 +_PR_MD_NEW_CV(_MDCVar *cv) 1.214 +{ 1.215 + cv->magic = _MD_MAGIC_CV; 1.216 + /* 1.217 + * The waitHead, waitTail, and nwait fields are zeroed 1.218 + * when the PRCondVar structure is created. 1.219 + */ 1.220 + return 0; 1.221 +} 1.222 + 1.223 +void _PR_MD_FREE_CV(_MDCVar *cv) 1.224 +{ 1.225 + cv->magic = (PRUint32)-1; 1.226 + return; 1.227 +} 1.228 + 1.229 +/* 1.230 + * _PR_MD_WAIT_CV() -- Wait on condition variable 1.231 + */ 1.232 +void 1.233 +_PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout ) 1.234 +{ 1.235 + PRThread *thred = _PR_MD_CURRENT_THREAD(); 1.236 + ULONG rv, count; 1.237 + ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ? 1.238 + SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(timeout); 1.239 + 1.240 + /* 1.241 + * If we have pending notifies, post them now. 1.242 + */ 1.243 + if (0 != lock->notified.length) { 1.244 + md_UnlockAndPostNotifies(lock, thred, cv); 1.245 + } else { 1.246 + AddThreadToCVWaitQueueInternal(thred, cv); 1.247 + DosReleaseMutexSem(lock->mutex); 1.248 + } 1.249 + 1.250 + /* Wait for notification or timeout; don't really care which */ 1.251 + rv = DosWaitEventSem(thred->md.blocked_sema, msecs); 1.252 + if (rv == NO_ERROR) { 1.253 + DosResetEventSem(thred->md.blocked_sema, &count); 1.254 + } 1.255 + 1.256 + DosRequestMutexSem((lock->mutex), SEM_INDEFINITE_WAIT); 1.257 + 1.258 + PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT); 1.259 + 1.260 + if(rv == ERROR_TIMEOUT) 1.261 + { 1.262 + if (thred->md.inCVWaitQueue) { 1.263 + PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL) 1.264 + || (cv->waitTail == NULL && cv->waitHead == NULL)); 1.265 + cv->nwait -= 1; 1.266 + thred->md.inCVWaitQueue = PR_FALSE; 1.267 + if (cv->waitHead == thred) { 1.268 + cv->waitHead = thred->md.next; 1.269 + if (cv->waitHead == NULL) { 1.270 + cv->waitTail = NULL; 1.271 + } else { 1.272 + cv->waitHead->md.prev = NULL; 1.273 + } 1.274 + } else { 1.275 + PR_ASSERT(thred->md.prev != NULL); 1.276 + thred->md.prev->md.next = thred->md.next; 1.277 + if (thred->md.next != NULL) { 1.278 + thred->md.next->md.prev = thred->md.prev; 1.279 + } else { 1.280 + PR_ASSERT(cv->waitTail == thred); 1.281 + cv->waitTail = thred->md.prev; 1.282 + } 1.283 + } 1.284 + thred->md.next = thred->md.prev = NULL; 1.285 + } else { 1.286 + /* 1.287 + * This thread must have been notified, but the 1.288 + * SemRelease call happens after SemRequest 1.289 + * times out. Wait on the semaphore again to make it 1.290 + * non-signaled. We assume this wait won't take long. 1.291 + */ 1.292 + rv = DosWaitEventSem(thred->md.blocked_sema, SEM_INDEFINITE_WAIT); 1.293 + if (rv == NO_ERROR) { 1.294 + DosResetEventSem(thred->md.blocked_sema, &count); 1.295 + } 1.296 + PR_ASSERT(rv == NO_ERROR); 1.297 + } 1.298 + } 1.299 + PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE); 1.300 + return; 1.301 +} /* --- end _PR_MD_WAIT_CV() --- */ 1.302 + 1.303 +void 1.304 +_PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock) 1.305 +{ 1.306 + md_PostNotifyToCvar(cv, lock, PR_FALSE); 1.307 + return; 1.308 +} 1.309 + 1.310 +PRStatus 1.311 +_PR_MD_NEW_LOCK(_MDLock *lock) 1.312 +{ 1.313 + DosCreateMutexSem(0, &(lock->mutex), 0, 0); 1.314 + (lock)->notified.length=0; 1.315 + (lock)->notified.link=NULL; 1.316 + return PR_SUCCESS; 1.317 +} 1.318 + 1.319 +void 1.320 +_PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock) 1.321 +{ 1.322 + md_PostNotifyToCvar(cv, lock, PR_TRUE); 1.323 + return; 1.324 +} 1.325 + 1.326 +void _PR_MD_UNLOCK(_MDLock *lock) 1.327 +{ 1.328 + if (0 != lock->notified.length) { 1.329 + md_UnlockAndPostNotifies(lock, NULL, NULL); 1.330 + } else { 1.331 + DosReleaseMutexSem(lock->mutex); 1.332 + } 1.333 +}