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