1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/threads/combined/prulock.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,446 @@ 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 +#include "primpl.h" 1.10 + 1.11 +#if defined(WIN95) 1.12 +/* 1.13 +** Some local variables report warnings on Win95 because the code paths 1.14 +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. 1.15 +** The pragma suppresses the warning. 1.16 +** 1.17 +*/ 1.18 +#pragma warning(disable : 4101) 1.19 +#endif 1.20 + 1.21 + 1.22 +void _PR_InitLocks(void) 1.23 +{ 1.24 + _PR_MD_INIT_LOCKS(); 1.25 +} 1.26 + 1.27 +/* 1.28 +** Deal with delayed interrupts/requested reschedule during interrupt 1.29 +** re-enables. 1.30 +*/ 1.31 +void _PR_IntsOn(_PRCPU *cpu) 1.32 +{ 1.33 + PRUintn missed, pri, i; 1.34 + _PRInterruptTable *it; 1.35 + PRThread *me; 1.36 + 1.37 + PR_ASSERT(cpu); /* Global threads don't have CPUs */ 1.38 + PR_ASSERT(_PR_MD_GET_INTSOFF() > 0); 1.39 + me = _PR_MD_CURRENT_THREAD(); 1.40 + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); 1.41 + 1.42 + /* 1.43 + ** Process delayed interrupts. This logic is kinda scary because we 1.44 + ** need to avoid losing an interrupt (it's ok to delay an interrupt 1.45 + ** until later). 1.46 + ** 1.47 + ** There are two missed state words. _pr_ints.where indicates to the 1.48 + ** interrupt handler which state word is currently safe for 1.49 + ** modification. 1.50 + ** 1.51 + ** This code scans both interrupt state words, using the where flag 1.52 + ** to indicate to the interrupt which state word is safe for writing. 1.53 + ** If an interrupt comes in during a scan the other word will be 1.54 + ** modified. This modification will be noticed during the next 1.55 + ** iteration of the loop or during the next call to this routine. 1.56 + */ 1.57 + for (i = 0; i < 2; i++) { 1.58 + cpu->where = (1 - i); 1.59 + missed = cpu->u.missed[i]; 1.60 + if (missed != 0) { 1.61 + cpu->u.missed[i] = 0; 1.62 + for (it = _pr_interruptTable; it->name; it++) { 1.63 + if (missed & it->missed_bit) { 1.64 + PR_LOG(_pr_sched_lm, PR_LOG_MIN, 1.65 + ("IntsOn[0]: %s intr", it->name)); 1.66 + (*it->handler)(); 1.67 + } 1.68 + } 1.69 + } 1.70 + } 1.71 + 1.72 + if (cpu->u.missed[3] != 0) { 1.73 + _PRCPU *cpu; 1.74 + 1.75 + _PR_THREAD_LOCK(me); 1.76 + me->state = _PR_RUNNABLE; 1.77 + pri = me->priority; 1.78 + 1.79 + cpu = me->cpu; 1.80 + _PR_RUNQ_LOCK(cpu); 1.81 + _PR_ADD_RUNQ(me, cpu, pri); 1.82 + _PR_RUNQ_UNLOCK(cpu); 1.83 + _PR_THREAD_UNLOCK(me); 1.84 + _PR_MD_SWITCH_CONTEXT(me); 1.85 + } 1.86 +} 1.87 + 1.88 +/* 1.89 +** Unblock the first runnable waiting thread. Skip over 1.90 +** threads that are trying to be suspended 1.91 +** Note: Caller must hold _PR_LOCK_LOCK() 1.92 +*/ 1.93 +void _PR_UnblockLockWaiter(PRLock *lock) 1.94 +{ 1.95 + PRThread *t = NULL; 1.96 + PRThread *me; 1.97 + PRCList *q; 1.98 + 1.99 + q = lock->waitQ.next; 1.100 + PR_ASSERT(q != &lock->waitQ); 1.101 + while (q != &lock->waitQ) { 1.102 + /* Unblock first waiter */ 1.103 + t = _PR_THREAD_CONDQ_PTR(q); 1.104 + 1.105 + /* 1.106 + ** We are about to change the thread's state to runnable and for local 1.107 + ** threads, we are going to assign a cpu to it. So, protect thread's 1.108 + ** data structure. 1.109 + */ 1.110 + _PR_THREAD_LOCK(t); 1.111 + 1.112 + if (t->flags & _PR_SUSPENDING) { 1.113 + q = q->next; 1.114 + _PR_THREAD_UNLOCK(t); 1.115 + continue; 1.116 + } 1.117 + 1.118 + /* Found a runnable thread */ 1.119 + PR_ASSERT(t->state == _PR_LOCK_WAIT); 1.120 + PR_ASSERT(t->wait.lock == lock); 1.121 + t->wait.lock = 0; 1.122 + PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */ 1.123 + 1.124 + /* 1.125 + ** If this is a native thread, nothing else to do except to wake it 1.126 + ** up by calling the machine dependent wakeup routine. 1.127 + ** 1.128 + ** If this is a local thread, we need to assign it a cpu and 1.129 + ** put the thread on that cpu's run queue. There are two cases to 1.130 + ** take care of. If the currently running thread is also a local 1.131 + ** thread, we just assign our own cpu to that thread and put it on 1.132 + ** the cpu's run queue. If the the currently running thread is a 1.133 + ** native thread, we assign the primordial cpu to it (on NT, 1.134 + ** MD_WAKEUP handles the cpu assignment). 1.135 + */ 1.136 + 1.137 + if ( !_PR_IS_NATIVE_THREAD(t) ) { 1.138 + 1.139 + t->state = _PR_RUNNABLE; 1.140 + 1.141 + me = _PR_MD_CURRENT_THREAD(); 1.142 + 1.143 + _PR_AddThreadToRunQ(me, t); 1.144 + _PR_THREAD_UNLOCK(t); 1.145 + } else { 1.146 + t->state = _PR_RUNNING; 1.147 + _PR_THREAD_UNLOCK(t); 1.148 + } 1.149 + _PR_MD_WAKEUP_WAITER(t); 1.150 + break; 1.151 + } 1.152 + return; 1.153 +} 1.154 + 1.155 +/************************************************************************/ 1.156 + 1.157 + 1.158 +PR_IMPLEMENT(PRLock*) PR_NewLock(void) 1.159 +{ 1.160 + PRLock *lock; 1.161 + 1.162 + if (!_pr_initialized) _PR_ImplicitInitialization(); 1.163 + 1.164 + lock = PR_NEWZAP(PRLock); 1.165 + if (lock) { 1.166 + if (_PR_InitLock(lock) != PR_SUCCESS) { 1.167 + PR_DELETE(lock); 1.168 + return NULL; 1.169 + } 1.170 + } 1.171 + return lock; 1.172 +} 1.173 + 1.174 +PRStatus _PR_InitLock(PRLock *lock) 1.175 +{ 1.176 + if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) { 1.177 + return PR_FAILURE; 1.178 + } 1.179 + PR_INIT_CLIST(&lock->links); 1.180 + PR_INIT_CLIST(&lock->waitQ); 1.181 + return PR_SUCCESS; 1.182 +} 1.183 + 1.184 +/* 1.185 +** Destroy the given lock "lock". There is no point in making this race 1.186 +** free because if some other thread has the pointer to this lock all 1.187 +** bets are off. 1.188 +*/ 1.189 +PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) 1.190 +{ 1.191 + _PR_FreeLock(lock); 1.192 + PR_DELETE(lock); 1.193 +} 1.194 + 1.195 +void _PR_FreeLock(PRLock *lock) 1.196 +{ 1.197 + PR_ASSERT(lock->owner == 0); 1.198 + _PR_MD_FREE_LOCK(&lock->ilock); 1.199 +} 1.200 + 1.201 +extern PRThread *suspendAllThread; 1.202 +/* 1.203 +** Lock the lock. 1.204 +*/ 1.205 +PR_IMPLEMENT(void) PR_Lock(PRLock *lock) 1.206 +{ 1.207 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.208 + PRIntn is; 1.209 + PRThread *t; 1.210 + PRCList *q; 1.211 + 1.212 + PR_ASSERT(me != suspendAllThread); 1.213 + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); 1.214 + PR_ASSERT(lock != NULL); 1.215 +#ifdef _PR_GLOBAL_THREADS_ONLY 1.216 + _PR_MD_LOCK(&lock->ilock); 1.217 + PR_ASSERT(lock->owner == 0); 1.218 + lock->owner = me; 1.219 + return; 1.220 +#else /* _PR_GLOBAL_THREADS_ONLY */ 1.221 + 1.222 + if (_native_threads_only) { 1.223 + _PR_MD_LOCK(&lock->ilock); 1.224 + PR_ASSERT(lock->owner == 0); 1.225 + lock->owner = me; 1.226 + return; 1.227 + } 1.228 + 1.229 + if (!_PR_IS_NATIVE_THREAD(me)) 1.230 + _PR_INTSOFF(is); 1.231 + 1.232 + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); 1.233 + 1.234 +retry: 1.235 + _PR_LOCK_LOCK(lock); 1.236 + if (lock->owner == 0) { 1.237 + /* Just got the lock */ 1.238 + lock->owner = me; 1.239 + lock->priority = me->priority; 1.240 + /* Add the granted lock to this owning thread's lock list */ 1.241 + PR_APPEND_LINK(&lock->links, &me->lockList); 1.242 + _PR_LOCK_UNLOCK(lock); 1.243 + if (!_PR_IS_NATIVE_THREAD(me)) 1.244 + _PR_FAST_INTSON(is); 1.245 + return; 1.246 + } 1.247 + 1.248 + /* If this thread already owns this lock, then it is a deadlock */ 1.249 + PR_ASSERT(lock->owner != me); 1.250 + 1.251 + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); 1.252 + 1.253 +#if 0 1.254 + if (me->priority > lock->owner->priority) { 1.255 + /* 1.256 + ** Give the lock owner a priority boost until we get the 1.257 + ** lock. Record the priority we boosted it to. 1.258 + */ 1.259 + lock->boostPriority = me->priority; 1.260 + _PR_SetThreadPriority(lock->owner, me->priority); 1.261 + } 1.262 +#endif 1.263 + 1.264 + /* 1.265 + Add this thread to the asked for lock's list of waiting threads. We 1.266 + add this thread thread in the right priority order so when the unlock 1.267 + occurs, the thread with the higher priority will get the lock. 1.268 + */ 1.269 + q = lock->waitQ.next; 1.270 + if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority == 1.271 + _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) { 1.272 + /* 1.273 + * If all the threads in the lock waitQ have the same priority, 1.274 + * then avoid scanning the list: insert the element at the end. 1.275 + */ 1.276 + q = &lock->waitQ; 1.277 + } else { 1.278 + /* Sort thread into lock's waitQ at appropriate point */ 1.279 + /* Now scan the list for where to insert this entry */ 1.280 + while (q != &lock->waitQ) { 1.281 + t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next); 1.282 + if (me->priority > t->priority) { 1.283 + /* Found a lower priority thread to insert in front of */ 1.284 + break; 1.285 + } 1.286 + q = q->next; 1.287 + } 1.288 + } 1.289 + PR_INSERT_BEFORE(&me->waitQLinks, q); 1.290 + 1.291 + /* 1.292 + Now grab the threadLock since we are about to change the state. We have 1.293 + to do this since a PR_Suspend or PR_SetThreadPriority type call that takes 1.294 + a PRThread* as an argument could be changing the state of this thread from 1.295 + a thread running on a different cpu. 1.296 + */ 1.297 + 1.298 + _PR_THREAD_LOCK(me); 1.299 + me->state = _PR_LOCK_WAIT; 1.300 + me->wait.lock = lock; 1.301 + _PR_THREAD_UNLOCK(me); 1.302 + 1.303 + _PR_LOCK_UNLOCK(lock); 1.304 + 1.305 + _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); 1.306 + goto retry; 1.307 + 1.308 +#endif /* _PR_GLOBAL_THREADS_ONLY */ 1.309 +} 1.310 + 1.311 +/* 1.312 +** Unlock the lock. 1.313 +*/ 1.314 +PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) 1.315 +{ 1.316 + PRCList *q; 1.317 + PRThreadPriority pri, boost; 1.318 + PRIntn is; 1.319 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.320 + 1.321 + PR_ASSERT(lock != NULL); 1.322 + PR_ASSERT(lock->owner == me); 1.323 + PR_ASSERT(me != suspendAllThread); 1.324 + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); 1.325 + if (lock->owner != me) { 1.326 + return PR_FAILURE; 1.327 + } 1.328 + 1.329 +#ifdef _PR_GLOBAL_THREADS_ONLY 1.330 + lock->owner = 0; 1.331 + _PR_MD_UNLOCK(&lock->ilock); 1.332 + return PR_SUCCESS; 1.333 +#else /* _PR_GLOBAL_THREADS_ONLY */ 1.334 + 1.335 + if (_native_threads_only) { 1.336 + lock->owner = 0; 1.337 + _PR_MD_UNLOCK(&lock->ilock); 1.338 + return PR_SUCCESS; 1.339 + } 1.340 + 1.341 + if (!_PR_IS_NATIVE_THREAD(me)) 1.342 + _PR_INTSOFF(is); 1.343 + _PR_LOCK_LOCK(lock); 1.344 + 1.345 + /* Remove the lock from the owning thread's lock list */ 1.346 + PR_REMOVE_LINK(&lock->links); 1.347 + pri = lock->priority; 1.348 + boost = lock->boostPriority; 1.349 + if (boost > pri) { 1.350 + /* 1.351 + ** We received a priority boost during the time we held the lock. 1.352 + ** We need to figure out what priority to move to by scanning 1.353 + ** down our list of lock's that we are still holding and using 1.354 + ** the highest boosted priority found. 1.355 + */ 1.356 + q = me->lockList.next; 1.357 + while (q != &me->lockList) { 1.358 + PRLock *ll = _PR_LOCK_PTR(q); 1.359 + if (ll->boostPriority > pri) { 1.360 + pri = ll->boostPriority; 1.361 + } 1.362 + q = q->next; 1.363 + } 1.364 + if (pri != me->priority) { 1.365 + _PR_SetThreadPriority(me, pri); 1.366 + } 1.367 + } 1.368 + 1.369 + /* Unblock the first waiting thread */ 1.370 + q = lock->waitQ.next; 1.371 + if (q != &lock->waitQ) 1.372 + _PR_UnblockLockWaiter(lock); 1.373 + lock->boostPriority = PR_PRIORITY_LOW; 1.374 + lock->owner = 0; 1.375 + _PR_LOCK_UNLOCK(lock); 1.376 + if (!_PR_IS_NATIVE_THREAD(me)) 1.377 + _PR_INTSON(is); 1.378 + return PR_SUCCESS; 1.379 +#endif /* _PR_GLOBAL_THREADS_ONLY */ 1.380 +} 1.381 + 1.382 +/* 1.383 +** If the current thread owns |lock|, this assertion is guaranteed to 1.384 +** succeed. Otherwise, the behavior of this function is undefined. 1.385 +*/ 1.386 +PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock) 1.387 +{ 1.388 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.389 + PR_ASSERT(lock->owner == me); 1.390 +} 1.391 + 1.392 +/* 1.393 +** Test and then lock the lock if it's not already locked by some other 1.394 +** thread. Return PR_FALSE if some other thread owned the lock at the 1.395 +** time of the call. 1.396 +*/ 1.397 +PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock) 1.398 +{ 1.399 + PRThread *me = _PR_MD_CURRENT_THREAD(); 1.400 + PRBool rv = PR_FALSE; 1.401 + PRIntn is; 1.402 + 1.403 +#ifdef _PR_GLOBAL_THREADS_ONLY 1.404 + is = _PR_MD_TEST_AND_LOCK(&lock->ilock); 1.405 + if (is == 0) { 1.406 + lock->owner = me; 1.407 + return PR_TRUE; 1.408 + } 1.409 + return PR_FALSE; 1.410 +#else /* _PR_GLOBAL_THREADS_ONLY */ 1.411 + 1.412 +#ifndef _PR_LOCAL_THREADS_ONLY 1.413 + if (_native_threads_only) { 1.414 + is = _PR_MD_TEST_AND_LOCK(&lock->ilock); 1.415 + if (is == 0) { 1.416 + lock->owner = me; 1.417 + return PR_TRUE; 1.418 + } 1.419 + return PR_FALSE; 1.420 + } 1.421 +#endif 1.422 + 1.423 + if (!_PR_IS_NATIVE_THREAD(me)) 1.424 + _PR_INTSOFF(is); 1.425 + 1.426 + _PR_LOCK_LOCK(lock); 1.427 + if (lock->owner == 0) { 1.428 + /* Just got the lock */ 1.429 + lock->owner = me; 1.430 + lock->priority = me->priority; 1.431 + /* Add the granted lock to this owning thread's lock list */ 1.432 + PR_APPEND_LINK(&lock->links, &me->lockList); 1.433 + rv = PR_TRUE; 1.434 + } 1.435 + _PR_LOCK_UNLOCK(lock); 1.436 + 1.437 + if (!_PR_IS_NATIVE_THREAD(me)) 1.438 + _PR_INTSON(is); 1.439 + return rv; 1.440 +#endif /* _PR_GLOBAL_THREADS_ONLY */ 1.441 +} 1.442 + 1.443 +/************************************************************************/ 1.444 +/************************************************************************/ 1.445 +/***********************ROUTINES FOR DCE EMULATION***********************/ 1.446 +/************************************************************************/ 1.447 +/************************************************************************/ 1.448 +PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) 1.449 + { return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; }