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