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 
_PR_InitLocks(void)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 */
_PR_IntsOn(_PRCPU * cpu)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 */
_PR_UnblockLockWaiter(PRLock * lock)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 thread'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 local
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 
PR_NewLock(void)155 PR_IMPLEMENT(PRLock*) PR_NewLock(void)
156 {
157     PRLock *lock;
158 
159     if (!_pr_initialized) {
160         _PR_ImplicitInitialization();
161     }
162 
163     lock = PR_NEWZAP(PRLock);
164     if (lock) {
165         if (_PR_InitLock(lock) != PR_SUCCESS) {
166             PR_DELETE(lock);
167             return NULL;
168         }
169     }
170     return lock;
171 }
172 
_PR_InitLock(PRLock * lock)173 PRStatus _PR_InitLock(PRLock *lock)
174 {
175     if (_PR_MD_NEW_LOCK(&lock->ilock) != PR_SUCCESS) {
176         return PR_FAILURE;
177     }
178     PR_INIT_CLIST(&lock->links);
179     PR_INIT_CLIST(&lock->waitQ);
180     return PR_SUCCESS;
181 }
182 
183 /*
184 ** Destroy the given lock "lock". There is no point in making this race
185 ** free because if some other thread has the pointer to this lock all
186 ** bets are off.
187 */
PR_DestroyLock(PRLock * lock)188 PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock)
189 {
190     _PR_FreeLock(lock);
191     PR_DELETE(lock);
192 }
193 
_PR_FreeLock(PRLock * lock)194 void _PR_FreeLock(PRLock *lock)
195 {
196     PR_ASSERT(lock->owner == 0);
197     _PR_MD_FREE_LOCK(&lock->ilock);
198 }
199 
200 extern PRThread *suspendAllThread;
201 /*
202 ** Lock the lock.
203 */
PR_Lock(PRLock * lock)204 PR_IMPLEMENT(void) PR_Lock(PRLock *lock)
205 {
206     PRThread *me = _PR_MD_CURRENT_THREAD();
207     PRIntn is;
208     PRThread *t;
209     PRCList *q;
210 
211     PR_ASSERT(me != suspendAllThread);
212     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
213     PR_ASSERT(lock != NULL);
214 #ifdef _PR_GLOBAL_THREADS_ONLY
215     _PR_MD_LOCK(&lock->ilock);
216     PR_ASSERT(lock->owner == 0);
217     lock->owner = me;
218     return;
219 #else  /* _PR_GLOBAL_THREADS_ONLY */
220 
221     if (_native_threads_only) {
222         _PR_MD_LOCK(&lock->ilock);
223         PR_ASSERT(lock->owner == 0);
224         lock->owner = me;
225         return;
226     }
227 
228     if (!_PR_IS_NATIVE_THREAD(me)) {
229         _PR_INTSOFF(is);
230     }
231 
232     PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
233 
234 retry:
235     _PR_LOCK_LOCK(lock);
236     if (lock->owner == 0) {
237         /* Just got the lock */
238         lock->owner = me;
239         lock->priority = me->priority;
240         /* Add the granted lock to this owning thread's lock list */
241         PR_APPEND_LINK(&lock->links, &me->lockList);
242         _PR_LOCK_UNLOCK(lock);
243         if (!_PR_IS_NATIVE_THREAD(me)) {
244             _PR_FAST_INTSON(is);
245         }
246         return;
247     }
248 
249     /* If this thread already owns this lock, then it is a deadlock */
250     PR_ASSERT(lock->owner != me);
251 
252     PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
253 
254 #if 0
255     if (me->priority > lock->owner->priority) {
256         /*
257         ** Give the lock owner a priority boost until we get the
258         ** lock. Record the priority we boosted it to.
259         */
260         lock->boostPriority = me->priority;
261         _PR_SetThreadPriority(lock->owner, me->priority);
262     }
263 #endif
264 
265     /*
266     Add this thread to the asked for lock's list of waiting threads.  We
267     add this thread thread in the right priority order so when the unlock
268     occurs, the thread with the higher priority will get the lock.
269     */
270     q = lock->waitQ.next;
271     if (q == &lock->waitQ || _PR_THREAD_CONDQ_PTR(q)->priority ==
272         _PR_THREAD_CONDQ_PTR(lock->waitQ.prev)->priority) {
273         /*
274          * If all the threads in the lock waitQ have the same priority,
275          * then avoid scanning the list:  insert the element at the end.
276          */
277         q = &lock->waitQ;
278     } else {
279         /* Sort thread into lock's waitQ at appropriate point */
280         /* Now scan the list for where to insert this entry */
281         while (q != &lock->waitQ) {
282             t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next);
283             if (me->priority > t->priority) {
284                 /* Found a lower priority thread to insert in front of */
285                 break;
286             }
287             q = q->next;
288         }
289     }
290     PR_INSERT_BEFORE(&me->waitQLinks, q);
291 
292     /*
293     Now grab the threadLock since we are about to change the state.  We have
294     to do this since a PR_Suspend or PR_SetThreadPriority type call that takes
295     a PRThread* as an argument could be changing the state of this thread from
296     a thread running on a different cpu.
297     */
298 
299     _PR_THREAD_LOCK(me);
300     me->state = _PR_LOCK_WAIT;
301     me->wait.lock = lock;
302     _PR_THREAD_UNLOCK(me);
303 
304     _PR_LOCK_UNLOCK(lock);
305 
306     _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
307     goto retry;
308 
309 #endif  /* _PR_GLOBAL_THREADS_ONLY */
310 }
311 
312 /*
313 ** Unlock the lock.
314 */
PR_Unlock(PRLock * lock)315 PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock)
316 {
317     PRCList *q;
318     PRThreadPriority pri, boost;
319     PRIntn is;
320     PRThread *me = _PR_MD_CURRENT_THREAD();
321 
322     PR_ASSERT(lock != NULL);
323     PR_ASSERT(lock->owner == me);
324     PR_ASSERT(me != suspendAllThread);
325     PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
326     if (lock->owner != me) {
327         return PR_FAILURE;
328     }
329 
330 #ifdef _PR_GLOBAL_THREADS_ONLY
331     lock->owner = 0;
332     _PR_MD_UNLOCK(&lock->ilock);
333     return PR_SUCCESS;
334 #else  /* _PR_GLOBAL_THREADS_ONLY */
335 
336     if (_native_threads_only) {
337         lock->owner = 0;
338         _PR_MD_UNLOCK(&lock->ilock);
339         return PR_SUCCESS;
340     }
341 
342     if (!_PR_IS_NATIVE_THREAD(me)) {
343         _PR_INTSOFF(is);
344     }
345     _PR_LOCK_LOCK(lock);
346 
347     /* Remove the lock from the owning thread's lock list */
348     PR_REMOVE_LINK(&lock->links);
349     pri = lock->priority;
350     boost = lock->boostPriority;
351     if (boost > pri) {
352         /*
353         ** We received a priority boost during the time we held the lock.
354         ** We need to figure out what priority to move to by scanning
355         ** down our list of lock's that we are still holding and using
356         ** the highest boosted priority found.
357         */
358         q = me->lockList.next;
359         while (q != &me->lockList) {
360             PRLock *ll = _PR_LOCK_PTR(q);
361             if (ll->boostPriority > pri) {
362                 pri = ll->boostPriority;
363             }
364             q = q->next;
365         }
366         if (pri != me->priority) {
367             _PR_SetThreadPriority(me, pri);
368         }
369     }
370 
371     /* Unblock the first waiting thread */
372     q = lock->waitQ.next;
373     if (q != &lock->waitQ) {
374         _PR_UnblockLockWaiter(lock);
375     }
376     lock->boostPriority = PR_PRIORITY_LOW;
377     lock->owner = 0;
378     _PR_LOCK_UNLOCK(lock);
379     if (!_PR_IS_NATIVE_THREAD(me)) {
380         _PR_INTSON(is);
381     }
382     return PR_SUCCESS;
383 #endif  /* _PR_GLOBAL_THREADS_ONLY */
384 }
385 
386 /*
387 **  If the current thread owns |lock|, this assertion is guaranteed to
388 **  succeed.  Otherwise, the behavior of this function is undefined.
389 */
PR_AssertCurrentThreadOwnsLock(PRLock * lock)390 PR_IMPLEMENT(void) PR_AssertCurrentThreadOwnsLock(PRLock *lock)
391 {
392     PRThread *me = _PR_MD_CURRENT_THREAD();
393     PR_ASSERT(lock->owner == me);
394 }
395 
396 /*
397 ** Test and then lock the lock if it's not already locked by some other
398 ** thread. Return PR_FALSE if some other thread owned the lock at the
399 ** time of the call.
400 */
PR_TestAndLock(PRLock * lock)401 PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock)
402 {
403     PRThread *me = _PR_MD_CURRENT_THREAD();
404     PRBool rv = PR_FALSE;
405     PRIntn is;
406 
407 #ifdef _PR_GLOBAL_THREADS_ONLY
408     is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
409     if (is == 0) {
410         lock->owner = me;
411         return PR_TRUE;
412     }
413     return PR_FALSE;
414 #else  /* _PR_GLOBAL_THREADS_ONLY */
415 
416 #ifndef _PR_LOCAL_THREADS_ONLY
417     if (_native_threads_only) {
418         is = _PR_MD_TEST_AND_LOCK(&lock->ilock);
419         if (is == 0) {
420             lock->owner = me;
421             return PR_TRUE;
422         }
423         return PR_FALSE;
424     }
425 #endif
426 
427     if (!_PR_IS_NATIVE_THREAD(me)) {
428         _PR_INTSOFF(is);
429     }
430 
431     _PR_LOCK_LOCK(lock);
432     if (lock->owner == 0) {
433         /* Just got the lock */
434         lock->owner = me;
435         lock->priority = me->priority;
436         /* Add the granted lock to this owning thread's lock list */
437         PR_APPEND_LINK(&lock->links, &me->lockList);
438         rv = PR_TRUE;
439     }
440     _PR_LOCK_UNLOCK(lock);
441 
442     if (!_PR_IS_NATIVE_THREAD(me)) {
443         _PR_INTSON(is);
444     }
445     return rv;
446 #endif  /* _PR_GLOBAL_THREADS_ONLY */
447 }
448 
449 /************************************************************************/
450 /************************************************************************/
451 /***********************ROUTINES FOR DCE EMULATION***********************/
452 /************************************************************************/
453 /************************************************************************/
PRP_TryLock(PRLock * lock)454 PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock)
455 {
456     return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE;
457 }
458