| OLD | NEW |
| (Empty) |
| 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 #if defined(WIN95) | |
| 9 /* | |
| 10 ** Some local variables report warnings on Win95 because the code paths | |
| 11 ** using them are conditioned on HAVE_CUSTOME_USER_THREADS. | |
| 12 ** The pragma suppresses the warning. | |
| 13 ** | |
| 14 */ | |
| 15 #pragma warning(disable : 4101) | |
| 16 #endif | |
| 17 | |
| 18 | |
| 19 void _PR_InitLocks(void) | |
| 20 { | |
| 21 _PR_MD_INIT_LOCKS(); | |
| 22 } | |
| 23 | |
| 24 /* | |
| 25 ** Deal with delayed interrupts/requested reschedule during interrupt | |
| 26 ** re-enables. | |
| 27 */ | |
| 28 void _PR_IntsOn(_PRCPU *cpu) | |
| 29 { | |
| 30 PRUintn missed, pri, i; | |
| 31 _PRInterruptTable *it; | |
| 32 PRThread *me; | |
| 33 | |
| 34 PR_ASSERT(cpu); /* Global threads don't have CPUs */ | |
| 35 PR_ASSERT(_PR_MD_GET_INTSOFF() > 0); | |
| 36 me = _PR_MD_CURRENT_THREAD(); | |
| 37 PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); | |
| 38 | |
| 39 /* | |
| 40 ** Process delayed interrupts. This logic is kinda scary because we | |
| 41 ** need to avoid losing an interrupt (it's ok to delay an interrupt | |
| 42 ** until later). | |
| 43 ** | |
| 44 ** There are two missed state words. _pr_ints.where indicates to the | |
| 45 ** interrupt handler which state word is currently safe for | |
| 46 ** modification. | |
| 47 ** | |
| 48 ** This code scans both interrupt state words, using the where flag | |
| 49 ** to indicate to the interrupt which state word is safe for writing. | |
| 50 ** If an interrupt comes in during a scan the other word will be | |
| 51 ** modified. This modification will be noticed during the next | |
| 52 ** iteration of the loop or during the next call to this routine. | |
| 53 */ | |
| 54 for (i = 0; i < 2; i++) { | |
| 55 cpu->where = (1 - i); | |
| 56 missed = cpu->u.missed[i]; | |
| 57 if (missed != 0) { | |
| 58 cpu->u.missed[i] = 0; | |
| 59 for (it = _pr_interruptTable; it->name; it++) { | |
| 60 if (missed & it->missed_bit) { | |
| 61 PR_LOG(_pr_sched_lm, PR_LOG_MIN, | |
| 62 ("IntsOn[0]: %s intr", it->name)); | |
| 63 (*it->handler)(); | |
| 64 } | |
| 65 } | |
| 66 } | |
| 67 } | |
| 68 | |
| 69 if (cpu->u.missed[3] != 0) { | |
| 70 _PRCPU *cpu; | |
| 71 | |
| 72 _PR_THREAD_LOCK(me); | |
| 73 me->state = _PR_RUNNABLE; | |
| 74 pri = me->priority; | |
| 75 | |
| 76 cpu = me->cpu; | |
| 77 _PR_RUNQ_LOCK(cpu); | |
| 78 _PR_ADD_RUNQ(me, cpu, pri); | |
| 79 _PR_RUNQ_UNLOCK(cpu); | |
| 80 _PR_THREAD_UNLOCK(me); | |
| 81 _PR_MD_SWITCH_CONTEXT(me); | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 /* | |
| 86 ** Unblock the first runnable waiting thread. Skip over | |
| 87 ** threads that are trying to be suspended | |
| 88 ** Note: Caller must hold _PR_LOCK_LOCK() | |
| 89 */ | |
| 90 void _PR_UnblockLockWaiter(PRLock *lock) | |
| 91 { | |
| 92 PRThread *t = NULL; | |
| 93 PRThread *me; | |
| 94 PRCList *q; | |
| 95 | |
| 96 q = lock->waitQ.next; | |
| 97 PR_ASSERT(q != &lock->waitQ); | |
| 98 while (q != &lock->waitQ) { | |
| 99 /* Unblock first waiter */ | |
| 100 t = _PR_THREAD_CONDQ_PTR(q); | |
| 101 | |
| 102 /* | |
| 103 ** We are about to change the thread's state to runnable and for
local | |
| 104 ** threads, we are going to assign a cpu to it. So, protect thr
ead's | |
| 105 ** data structure. | |
| 106 */ | |
| 107 _PR_THREAD_LOCK(t); | |
| 108 | |
| 109 if (t->flags & _PR_SUSPENDING) { | |
| 110 q = q->next; | |
| 111 _PR_THREAD_UNLOCK(t); | |
| 112 continue; | |
| 113 } | |
| 114 | |
| 115 /* Found a runnable thread */ | |
| 116 PR_ASSERT(t->state == _PR_LOCK_WAIT); | |
| 117 PR_ASSERT(t->wait.lock == lock); | |
| 118 t->wait.lock = 0; | |
| 119 PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */ | |
| 120 | |
| 121 /* | |
| 122 ** If this is a native thread, nothing else to do except to wake
it | |
| 123 ** up by calling the machine dependent wakeup routine. | |
| 124 ** | |
| 125 ** If this is a local thread, we need to assign it a cpu and | |
| 126 ** put the thread on that cpu's run queue. There are two cases
to | |
| 127 ** take care of. If the currently running thread is also a loca
l | |
| 128 ** thread, we just assign our own cpu to that thread and put it
on | |
| 129 ** the cpu's run queue. If the the currently running thread is
a | |
| 130 ** native thread, we assign the primordial cpu to it (on NT, | |
| 131 ** MD_WAKEUP handles the cpu assignment). | |
| 132 */ | |
| 133 | |
| 134 if ( !_PR_IS_NATIVE_THREAD(t) ) { | |
| 135 | |
| 136 t->state = _PR_RUNNABLE; | |
| 137 | |
| 138 me = _PR_MD_CURRENT_THREAD(); | |
| 139 | |
| 140 _PR_AddThreadToRunQ(me, t); | |
| 141 _PR_THREAD_UNLOCK(t); | |
| 142 } else { | |
| 143 t->state = _PR_RUNNING; | |
| 144 _PR_THREAD_UNLOCK(t); | |
| 145 } | |
| 146 _PR_MD_WAKEUP_WAITER(t); | |
| 147 break; | |
| 148 } | |
| 149 return; | |
| 150 } | |
| 151 | |
| 152 /************************************************************************/ | |
| 153 | |
| 154 | |
| 155 PR_IMPLEMENT(PRLock*) PR_NewLock(void) | |
| 156 { | |
| 157 PRLock *lock; | |
| 158 | |
| 159 if (!_pr_initialized) _PR_ImplicitInitialization(); | |
| 160 | |
| 161 lock = PR_NEWZAP(PRLock); | |
| 162 if (lock) { | |
| 163 if (_PR_InitLock(lock) != PR_SUCCESS) { | |
| 164 PR_DELETE(lock); | |
| 165 return NULL; | |
| 166 } | |
| 167 } | |
| 168 return lock; | |
| 169 } | |
| 170 | |
| 171 PRStatus _PR_InitLock(PRLock *lock) | |
| 172 { | |
| 173 if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) { | |
| 174 return PR_FAILURE; | |
| 175 } | |
| 176 PR_INIT_CLIST(&lock->links); | |
| 177 PR_INIT_CLIST(&lock->waitQ); | |
| 178 return PR_SUCCESS; | |
| 179 } | |
| 180 | |
| 181 /* | |
| 182 ** Destroy the given lock "lock". There is no point in making this race | |
| 183 ** free because if some other thread has the pointer to this lock all | |
| 184 ** bets are off. | |
| 185 */ | |
| 186 PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) | |
| 187 { | |
| 188 _PR_FreeLock(lock); | |
| 189 PR_DELETE(lock); | |
| 190 } | |
| 191 | |
| 192 void _PR_FreeLock(PRLock *lock) | |
| 193 { | |
| 194 PR_ASSERT(lock->owner == 0); | |
| 195 _PR_MD_FREE_LOCK(&lock->ilock); | |
| 196 } | |
| 197 | |
| 198 extern PRThread *suspendAllThread; | |
| 199 /* | |
| 200 ** Lock the lock. | |
| 201 */ | |
| 202 PR_IMPLEMENT(void) PR_Lock(PRLock *lock) | |
| 203 { | |
| 204 PRThread *me = _PR_MD_CURRENT_THREAD(); | |
| 205 PRIntn is; | |
| 206 PRThread *t; | |
| 207 PRCList *q; | |
| 208 | |
| 209 PR_ASSERT(me != suspendAllThread); | |
| 210 PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); | |
| 211 PR_ASSERT(lock != NULL); | |
| 212 #ifdef _PR_GLOBAL_THREADS_ONLY | |
| 213 _PR_MD_LOCK(&lock->ilock); | |
| 214 PR_ASSERT(lock->owner == 0); | |
| 215 lock->owner = me; | |
| 216 return; | |
| 217 #else /* _PR_GLOBAL_THREADS_ONLY */ | |
| 218 | |
| 219 if (_native_threads_only) { | |
| 220 _PR_MD_LOCK(&lock->ilock); | |
| 221 PR_ASSERT(lock->owner == 0); | |
| 222 lock->owner = me; | |
| 223 return; | |
| 224 } | |
| 225 | |
| 226 if (!_PR_IS_NATIVE_THREAD(me)) | |
| 227 _PR_INTSOFF(is); | |
| 228 | |
| 229 PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); | |
| 230 | |
| 231 retry: | |
| 232 _PR_LOCK_LOCK(lock); | |
| 233 if (lock->owner == 0) { | |
| 234 /* Just got the lock */ | |
| 235 lock->owner = me; | |
| 236 lock->priority = me->priority; | |
| 237 /* Add the granted lock to this owning thread's lock list */ | |
| 238 PR_APPEND_LINK(&lock->links, &me->lockList); | |
| 239 _PR_LOCK_UNLOCK(lock); | |
| 240 if (!_PR_IS_NATIVE_THREAD(me)) | |
| 241 _PR_FAST_INTSON(is); | |
| 242 return; | |
| 243 } | |
| 244 | |
| 245 /* If this thread already owns this lock, then it is a deadlock */ | |
| 246 PR_ASSERT(lock->owner != me); | |
| 247 | |
| 248 PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); | |
| 249 | |
| 250 #if 0 | |
| 251 if (me->priority > lock->owner->priority) { | |
| 252 /* | |
| 253 ** Give the lock owner a priority boost until we get the | |
| 254 ** lock. Record the priority we boosted it to. | |
| 255 */ | |
| 256 lock->boostPriority = me->priority; | |
| 257 _PR_SetThreadPriority(lock->owner, me->priority); | |
| 258 } | |
| 259 #endif | |
| 260 | |
| 261 /* | |
| 262 Add this thread to the asked for lock's list of waiting threads. We | |
| 263 add this thread thread in the right priority order so when the unlock | |
| 264 occurs, the thread with the higher priority will get the lock. | |
| 265 */ | |
| 266 q = lock->waitQ.next; | |
| 267 if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority == | |
| 268 _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) { | |
| 269 /* | |
| 270 * If all the threads in the lock waitQ have the same priority, | |
| 271 * then avoid scanning the list: insert the element at the end. | |
| 272 */ | |
| 273 q = &lock->waitQ; | |
| 274 } else { | |
| 275 /* Sort thread into lock's waitQ at appropriate point */ | |
| 276 /* Now scan the list for where to insert this entry */ | |
| 277 while (q != &lock->waitQ) { | |
| 278 t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next); | |
| 279 if (me->priority > t->priority) { | |
| 280 /* Found a lower priority thread to insert in fr
ont of */ | |
| 281 break; | |
| 282 } | |
| 283 q = q->next; | |
| 284 } | |
| 285 } | |
| 286 PR_INSERT_BEFORE(&me->waitQLinks, q); | |
| 287 | |
| 288 /* | |
| 289 Now grab the threadLock since we are about to change the state. We have | |
| 290 to do this since a PR_Suspend or PR_SetThreadPriority type call that tak
es | |
| 291 a PRThread* as an argument could be changing the state of this thread fr
om | |
| 292 a thread running on a different cpu. | |
| 293 */ | |
| 294 | |
| 295 _PR_THREAD_LOCK(me); | |
| 296 me->state = _PR_LOCK_WAIT; | |
| 297 me->wait.lock = lock; | |
| 298 _PR_THREAD_UNLOCK(me); | |
| 299 | |
| 300 _PR_LOCK_UNLOCK(lock); | |
| 301 | |
| 302 _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); | |
| 303 goto retry; | |
| 304 | |
| 305 #endif /* _PR_GLOBAL_THREADS_ONLY */ | |
| 306 } | |
| 307 | |
| 308 /* | |
| 309 ** Unlock the lock. | |
| 310 */ | |
| 311 PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) | |
| 312 { | |
| 313 PRCList *q; | |
| 314 PRThreadPriority pri, boost; | |
| 315 PRIntn is; | |
| 316 PRThread *me = _PR_MD_CURRENT_THREAD(); | |
| 317 | |
| 318 PR_ASSERT(lock != NULL); | |
| 319 PR_ASSERT(lock->owner == me); | |
| 320 PR_ASSERT(me != suspendAllThread); | |
| 321 PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); | |
| 322 if (lock->owner != me) { | |
| 323 return PR_FAILURE; | |
| 324 } | |
| 325 | |
| 326 #ifdef _PR_GLOBAL_THREADS_ONLY | |
| 327 lock->owner = 0; | |
| 328 _PR_MD_UNLOCK(&lock->ilock); | |
| 329 return PR_SUCCESS; | |
| 330 #else /* _PR_GLOBAL_THREADS_ONLY */ | |
| 331 | |
| 332 if (_native_threads_only) { | |
| 333 lock->owner = 0; | |
| 334 _PR_MD_UNLOCK(&lock->ilock); | |
| 335 return PR_SUCCESS; | |
| 336 } | |
| 337 | |
| 338 if (!_PR_IS_NATIVE_THREAD(me)) | |
| 339 _PR_INTSOFF(is); | |
| 340 _PR_LOCK_LOCK(lock); | |
| 341 | |
| 342 /* Remove the lock from the owning thread's lock list */ | |
| 343 PR_REMOVE_LINK(&lock->links); | |
| 344 pri = lock->priority; | |
| 345 boost = lock->boostPriority; | |
| 346 if (boost > pri) { | |
| 347 /* | |
| 348 ** We received a priority boost during the time we held the lock. | |
| 349 ** We need to figure out what priority to move to by scanning | |
| 350 ** down our list of lock's that we are still holding and using | |
| 351 ** the highest boosted priority found. | |
| 352 */ | |
| 353 q = me->lockList.next; | |
| 354 while (q != &me->lockList) { | |
| 355 PRLock *ll = _PR_LOCK_PTR(q); | |
| 356 if (ll->boostPriority > pri) { | |
| 357 pri = ll->boostPriority; | |
| 358 } | |
| 359 q = q->next; | |
| 360 } | |
| 361 if (pri != me->priority) { | |
| 362 _PR_SetThreadPriority(me, pri); | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 /* Unblock the first waiting thread */ | |
| 367 q = lock->waitQ.next; | |
| 368 if (q != &lock->waitQ) | |
| 369 _PR_UnblockLockWaiter(lock); | |
| 370 lock->boostPriority = PR_PRIORITY_LOW; | |
| 371 lock->owner = 0; | |
| 372 _PR_LOCK_UNLOCK(lock); | |
| 373 if (!_PR_IS_NATIVE_THREAD(me)) | |
| 374 _PR_INTSON(is); | |
| 375 return PR_SUCCESS; | |
| 376 #endif /* _PR_GLOBAL_THREADS_ONLY */ | |
| 377 } | |
| 378 | |
| 379 /* | |
| 380 ** If the current thread owns |lock|, this assertion is guaranteed to | |
| 381 ** succeed. Otherwise, the behavior of this function is undefined. | |
| 382 */ | |
| 383 PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock) | |
| 384 { | |
| 385 PRThread *me = _PR_MD_CURRENT_THREAD(); | |
| 386 PR_ASSERT(lock->owner == me); | |
| 387 } | |
| 388 | |
| 389 /* | |
| 390 ** Test and then lock the lock if it's not already locked by some other | |
| 391 ** thread. Return PR_FALSE if some other thread owned the lock at the | |
| 392 ** time of the call. | |
| 393 */ | |
| 394 PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock) | |
| 395 { | |
| 396 PRThread *me = _PR_MD_CURRENT_THREAD(); | |
| 397 PRBool rv = PR_FALSE; | |
| 398 PRIntn is; | |
| 399 | |
| 400 #ifdef _PR_GLOBAL_THREADS_ONLY | |
| 401 is = _PR_MD_TEST_AND_LOCK(&lock->ilock); | |
| 402 if (is == 0) { | |
| 403 lock->owner = me; | |
| 404 return PR_TRUE; | |
| 405 } | |
| 406 return PR_FALSE; | |
| 407 #else /* _PR_GLOBAL_THREADS_ONLY */ | |
| 408 | |
| 409 #ifndef _PR_LOCAL_THREADS_ONLY | |
| 410 if (_native_threads_only) { | |
| 411 is = _PR_MD_TEST_AND_LOCK(&lock->ilock); | |
| 412 if (is == 0) { | |
| 413 lock->owner = me; | |
| 414 return PR_TRUE; | |
| 415 } | |
| 416 return PR_FALSE; | |
| 417 } | |
| 418 #endif | |
| 419 | |
| 420 if (!_PR_IS_NATIVE_THREAD(me)) | |
| 421 _PR_INTSOFF(is); | |
| 422 | |
| 423 _PR_LOCK_LOCK(lock); | |
| 424 if (lock->owner == 0) { | |
| 425 /* Just got the lock */ | |
| 426 lock->owner = me; | |
| 427 lock->priority = me->priority; | |
| 428 /* Add the granted lock to this owning thread's lock list */ | |
| 429 PR_APPEND_LINK(&lock->links, &me->lockList); | |
| 430 rv = PR_TRUE; | |
| 431 } | |
| 432 _PR_LOCK_UNLOCK(lock); | |
| 433 | |
| 434 if (!_PR_IS_NATIVE_THREAD(me)) | |
| 435 _PR_INTSON(is); | |
| 436 return rv; | |
| 437 #endif /* _PR_GLOBAL_THREADS_ONLY */ | |
| 438 } | |
| 439 | |
| 440 /************************************************************************/ | |
| 441 /************************************************************************/ | |
| 442 /***********************ROUTINES FOR DCE EMULATION***********************/ | |
| 443 /************************************************************************/ | |
| 444 /************************************************************************/ | |
| 445 PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) | |
| 446 { return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; } | |
| OLD | NEW |