Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Side by Side Diff: nspr/pr/src/threads/combined/pruthr.c

Issue 2078763002: Delete bundled copy of NSS and replace with README. (Closed) Base URL: https://chromium.googlesource.com/chromium/deps/nss@master
Patch Set: Delete bundled copy of NSS and replace with README. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « nspr/pr/src/threads/combined/prustack.c ('k') | nspr/pr/src/threads/prcmon.c » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 #include <signal.h>
8 #include <string.h>
9
10 #if defined(WIN95)
11 /*
12 ** Some local variables report warnings on Win95 because the code paths
13 ** using them are conditioned on HAVE_CUSTOME_USER_THREADS.
14 ** The pragma suppresses the warning.
15 **
16 */
17 #pragma warning(disable : 4101)
18 #endif
19
20 /* _pr_activeLock protects the following global variables */
21 PRLock *_pr_activeLock;
22 PRInt32 _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread
23 * waits until all other user (non-system)
24 * threads have terminated before it exits.
25 * So whenever we decrement _pr_userActive,
26 * it is compared with
27 * _pr_primordialExitCount.
28 * If the primordial thread is a system
29 * thread, then _pr_primordialExitCount
30 * is 0. If the primordial thread is
31 * itself a user thread, then
32 * _pr_primordialThread is 1.
33 */
34 PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to
35 * _pr_primordialExitCount, this condition
36 * variable is notified.
37 */
38
39 PRLock *_pr_deadQLock;
40 PRUint32 _pr_numNativeDead;
41 PRUint32 _pr_numUserDead;
42 PRCList _pr_deadNativeQ;
43 PRCList _pr_deadUserQ;
44
45 PRUint32 _pr_join_counter;
46
47 PRUint32 _pr_local_threads;
48 PRUint32 _pr_global_threads;
49
50 PRBool suspendAllOn = PR_FALSE;
51 PRThread *suspendAllThread = NULL;
52
53 extern PRCList _pr_active_global_threadQ;
54 extern PRCList _pr_active_local_threadQ;
55
56 static void _PR_DecrActiveThreadCount(PRThread *thread);
57 static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *);
58 static void _PR_InitializeNativeStack(PRThreadStack *ts);
59 static void _PR_InitializeRecycledThread(PRThread *thread);
60 static void _PR_UserRunThread(void);
61
62 void _PR_InitThreads(PRThreadType type, PRThreadPriority priority,
63 PRUintn maxPTDs)
64 {
65 PRThread *thread;
66 PRThreadStack *stack;
67
68 PR_ASSERT(priority == PR_PRIORITY_NORMAL);
69
70 _pr_terminationCVLock = PR_NewLock();
71 _pr_activeLock = PR_NewLock();
72
73 #ifndef HAVE_CUSTOM_USER_THREADS
74 stack = PR_NEWZAP(PRThreadStack);
75 #ifdef HAVE_STACK_GROWING_UP
76 stack->stackTop = (char*) ((((PRWord)&type) >> _pr_pageShift)
77 << _pr_pageShift);
78 #else
79 #if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS)
80 stack->stackTop = (char*) &thread;
81 #else
82 stack->stackTop = (char*) ((((PRWord)&type + _pr_pageSize - 1)
83 >> _pr_pageShift) << _pr_pageShift);
84 #endif
85 #endif
86 #else
87 /* If stack is NULL, we're using custom user threads like NT fibers. */
88 stack = PR_NEWZAP(PRThreadStack);
89 if (stack) {
90 stack->stackSize = 0;
91 _PR_InitializeNativeStack(stack);
92 }
93 #endif /* HAVE_CUSTOM_USER_THREADS */
94
95 thread = _PR_AttachThread(type, priority, stack);
96 if (thread) {
97 _PR_MD_SET_CURRENT_THREAD(thread);
98
99 if (type == PR_SYSTEM_THREAD) {
100 thread->flags = _PR_SYSTEM;
101 _pr_systemActive++;
102 _pr_primordialExitCount = 0;
103 } else {
104 _pr_userActive++;
105 _pr_primordialExitCount = 1;
106 }
107 thread->no_sched = 1;
108 _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock);
109 }
110
111 if (!thread) PR_Abort();
112 #ifdef _PR_LOCAL_THREADS_ONLY
113 thread->flags |= _PR_PRIMORDIAL;
114 #else
115 thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE;
116 #endif
117
118 /*
119 * Needs _PR_PRIMORDIAL flag set before calling
120 * _PR_MD_INIT_THREAD()
121 */
122 if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
123 /*
124 * XXX do what?
125 */
126 }
127
128 if (_PR_IS_NATIVE_THREAD(thread)) {
129 PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
130 _pr_global_threads++;
131 } else {
132 PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
133 _pr_local_threads++;
134 }
135
136 _pr_recycleThreads = 0;
137 _pr_deadQLock = PR_NewLock();
138 _pr_numNativeDead = 0;
139 _pr_numUserDead = 0;
140 PR_INIT_CLIST(&_pr_deadNativeQ);
141 PR_INIT_CLIST(&_pr_deadUserQ);
142 }
143
144 void _PR_CleanupThreads(void)
145 {
146 if (_pr_terminationCVLock) {
147 PR_DestroyLock(_pr_terminationCVLock);
148 _pr_terminationCVLock = NULL;
149 }
150 if (_pr_activeLock) {
151 PR_DestroyLock(_pr_activeLock);
152 _pr_activeLock = NULL;
153 }
154 if (_pr_primordialExitCVar) {
155 PR_DestroyCondVar(_pr_primordialExitCVar);
156 _pr_primordialExitCVar = NULL;
157 }
158 /* TODO _pr_dead{Native,User}Q need to be deleted */
159 if (_pr_deadQLock) {
160 PR_DestroyLock(_pr_deadQLock);
161 _pr_deadQLock = NULL;
162 }
163 }
164
165 /*
166 ** Initialize a stack for a native thread
167 */
168 static void _PR_InitializeNativeStack(PRThreadStack *ts)
169 {
170 if( ts && (ts->stackTop == 0) ) {
171 ts->allocSize = ts->stackSize;
172
173 /*
174 ** Setup stackTop and stackBottom values.
175 */
176 #ifdef HAVE_STACK_GROWING_UP
177 ts->allocBase = (char*) ((((PRWord)&ts) >> _pr_pageShift)
178 << _pr_pageShift);
179 ts->stackBottom = ts->allocBase + ts->stackSize;
180 ts->stackTop = ts->allocBase;
181 #else
182 ts->allocBase = (char*) ((((PRWord)&ts + _pr_pageSize - 1)
183 >> _pr_pageShift) << _pr_pageShift);
184 ts->stackTop = ts->allocBase;
185 ts->stackBottom = ts->allocBase - ts->stackSize;
186 #endif
187 }
188 }
189
190 void _PR_NotifyJoinWaiters(PRThread *thread)
191 {
192 /*
193 ** Handle joinable threads. Change the state to waiting for join.
194 ** Remove from our run Q and put it on global waiting to join Q.
195 ** Notify on our "termination" condition variable so that joining
196 ** thread will know about our termination. Switch our context and
197 ** come back later on to continue the cleanup.
198 */
199 PR_ASSERT(thread == _PR_MD_CURRENT_THREAD());
200 if (thread->term != NULL) {
201 PR_Lock(_pr_terminationCVLock);
202 _PR_THREAD_LOCK(thread);
203 thread->state = _PR_JOIN_WAIT;
204 if ( !_PR_IS_NATIVE_THREAD(thread) ) {
205 _PR_MISCQ_LOCK(thread->cpu);
206 _PR_ADD_JOINQ(thread, thread->cpu);
207 _PR_MISCQ_UNLOCK(thread->cpu);
208 }
209 _PR_THREAD_UNLOCK(thread);
210 PR_NotifyCondVar(thread->term);
211 PR_Unlock(_pr_terminationCVLock);
212 _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
213 PR_ASSERT(thread->state != _PR_JOIN_WAIT);
214 }
215
216 }
217
218 /*
219 * Zero some of the data members of a recycled thread.
220 *
221 * Note that we can do this either when a dead thread is added to
222 * the dead thread queue or when it is reused. Here, we are doing
223 * this lazily, when the thread is reused in _PR_CreateThread().
224 */
225 static void _PR_InitializeRecycledThread(PRThread *thread)
226 {
227 /*
228 * Assert that the following data members are already zeroed
229 * by _PR_CleanupThread().
230 */
231 #ifdef DEBUG
232 if (thread->privateData) {
233 unsigned int i;
234 for (i = 0; i < thread->tpdLength; i++) {
235 PR_ASSERT(thread->privateData[i] == NULL);
236 }
237 }
238 #endif
239 PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0);
240 PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0);
241 PR_ASSERT(thread->errorStringLength == 0);
242 PR_ASSERT(thread->name == 0);
243
244 /* Reset data members in thread structure */
245 thread->errorCode = thread->osErrorCode = 0;
246 thread->io_pending = thread->io_suspended = PR_FALSE;
247 thread->environment = 0;
248 PR_INIT_CLIST(&thread->lockList);
249 }
250
251 PRStatus _PR_RecycleThread(PRThread *thread)
252 {
253 if ( _PR_IS_NATIVE_THREAD(thread) &&
254 _PR_NUM_DEADNATIVE < _pr_recycleThreads) {
255 _PR_DEADQ_LOCK;
256 PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ);
257 _PR_INC_DEADNATIVE;
258 _PR_DEADQ_UNLOCK;
259 return (PR_SUCCESS);
260 } else if ( !_PR_IS_NATIVE_THREAD(thread) &&
261 _PR_NUM_DEADUSER < _pr_recycleThreads) {
262 _PR_DEADQ_LOCK;
263 PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ);
264 _PR_INC_DEADUSER;
265 _PR_DEADQ_UNLOCK;
266 return (PR_SUCCESS);
267 }
268 return (PR_FAILURE);
269 }
270
271 /*
272 * Decrement the active thread count, either _pr_systemActive or
273 * _pr_userActive, depending on whether the thread is a system thread
274 * or a user thread. If all the user threads, except possibly
275 * the primordial thread, have terminated, we notify the primordial
276 * thread of this condition.
277 *
278 * Since this function will lock _pr_activeLock, do not call this
279 * function while holding the _pr_activeLock lock, as this will result
280 * in a deadlock.
281 */
282
283 static void
284 _PR_DecrActiveThreadCount(PRThread *thread)
285 {
286 PR_Lock(_pr_activeLock);
287 if (thread->flags & _PR_SYSTEM) {
288 _pr_systemActive--;
289 } else {
290 _pr_userActive--;
291 if (_pr_userActive == _pr_primordialExitCount) {
292 PR_NotifyCondVar(_pr_primordialExitCVar);
293 }
294 }
295 PR_Unlock(_pr_activeLock);
296 }
297
298 /*
299 ** Detach thread structure
300 */
301 static void
302 _PR_DestroyThread(PRThread *thread)
303 {
304 _PR_MD_FREE_LOCK(&thread->threadLock);
305 PR_DELETE(thread);
306 }
307
308 void
309 _PR_NativeDestroyThread(PRThread *thread)
310 {
311 if(thread->term) {
312 PR_DestroyCondVar(thread->term);
313 thread->term = 0;
314 }
315 if (NULL != thread->privateData) {
316 PR_ASSERT(0 != thread->tpdLength);
317 PR_DELETE(thread->privateData);
318 thread->tpdLength = 0;
319 }
320 PR_DELETE(thread->stack);
321 _PR_DestroyThread(thread);
322 }
323
324 void
325 _PR_UserDestroyThread(PRThread *thread)
326 {
327 if(thread->term) {
328 PR_DestroyCondVar(thread->term);
329 thread->term = 0;
330 }
331 if (NULL != thread->privateData) {
332 PR_ASSERT(0 != thread->tpdLength);
333 PR_DELETE(thread->privateData);
334 thread->tpdLength = 0;
335 }
336 _PR_MD_FREE_LOCK(&thread->threadLock);
337 if (thread->threadAllocatedOnStack == 1) {
338 _PR_MD_CLEAN_THREAD(thread);
339 /*
340 * Because the no_sched field is set, this thread/stack will
341 * will not be re-used until the flag is cleared by the thread
342 * we will context switch to.
343 */
344 _PR_FreeStack(thread->stack);
345 } else {
346 #ifdef WINNT
347 _PR_MD_CLEAN_THREAD(thread);
348 #else
349 /*
350 * This assertion does not apply to NT. On NT, every fiber
351 * has its threadAllocatedOnStack equal to 0. Elsewhere,
352 * only the primordial thread has its threadAllocatedOnStack
353 * equal to 0.
354 */
355 PR_ASSERT(thread->flags & _PR_PRIMORDIAL);
356 #endif
357 }
358 }
359
360
361 /*
362 ** Run a thread's start function. When the start function returns the
363 ** thread is done executing and no longer needs the CPU. If there are no
364 ** more user threads running then we can exit the program.
365 */
366 void _PR_NativeRunThread(void *arg)
367 {
368 PRThread *thread = (PRThread *)arg;
369
370 _PR_MD_SET_CURRENT_THREAD(thread);
371
372 _PR_MD_SET_CURRENT_CPU(NULL);
373
374 /* Set up the thread stack information */
375 _PR_InitializeNativeStack(thread->stack);
376
377 /* Set up the thread md information */
378 if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
379 /*
380 * thread failed to initialize itself, possibly due to
381 * failure to allocate per-thread resources
382 */
383 return;
384 }
385
386 while(1) {
387 thread->state = _PR_RUNNING;
388
389 /*
390 * Add to list of active threads
391 */
392 PR_Lock(_pr_activeLock);
393 PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ());
394 _pr_global_threads++;
395 PR_Unlock(_pr_activeLock);
396
397 (*thread->startFunc)(thread->arg);
398
399 /*
400 * The following two assertions are meant for NT asynch io.
401 *
402 * The thread should have no asynch io in progress when it
403 * exits, otherwise the overlapped buffer, which is part of
404 * the thread structure, would become invalid.
405 */
406 PR_ASSERT(thread->io_pending == PR_FALSE);
407 /*
408 * This assertion enforces the programming guideline that
409 * if an io function times out or is interrupted, the thread
410 * should close the fd to force the asynch io to abort
411 * before it exits. Right now, closing the fd is the only
412 * way to clear the io_suspended flag.
413 */
414 PR_ASSERT(thread->io_suspended == PR_FALSE);
415
416 /*
417 * remove thread from list of active threads
418 */
419 PR_Lock(_pr_activeLock);
420 PR_REMOVE_LINK(&thread->active);
421 _pr_global_threads--;
422 PR_Unlock(_pr_activeLock);
423
424 PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
425
426 /* All done, time to go away */
427 _PR_CleanupThread(thread);
428
429 _PR_NotifyJoinWaiters(thread);
430
431 _PR_DecrActiveThreadCount(thread);
432
433 thread->state = _PR_DEAD_STATE;
434
435 if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
436 PR_FAILURE)) {
437 /*
438 * thread not recycled
439 * platform-specific thread exit processing
440 * - for stuff like releasing native-thread resources, etc.
441 */
442 _PR_MD_EXIT_THREAD(thread);
443 /*
444 * Free memory allocated for the thread
445 */
446 _PR_NativeDestroyThread(thread);
447 /*
448 * thread gone, cannot de-reference thread now
449 */
450 return;
451 }
452
453 /* Now wait for someone to activate us again... */
454 _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT);
455 }
456 }
457
458 static void _PR_UserRunThread(void)
459 {
460 PRThread *thread = _PR_MD_CURRENT_THREAD();
461 PRIntn is;
462
463 if (_MD_LAST_THREAD())
464 _MD_LAST_THREAD()->no_sched = 0;
465
466 #ifdef HAVE_CUSTOM_USER_THREADS
467 if (thread->stack == NULL) {
468 thread->stack = PR_NEWZAP(PRThreadStack);
469 _PR_InitializeNativeStack(thread->stack);
470 }
471 #endif /* HAVE_CUSTOM_USER_THREADS */
472
473 while(1) {
474 /* Run thread main */
475 if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_MD_SET_INTSOFF(0);
476
477 /*
478 * Add to list of active threads
479 */
480 if (!(thread->flags & _PR_IDLE_THREAD)) {
481 PR_Lock(_pr_activeLock);
482 PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ());
483 _pr_local_threads++;
484 PR_Unlock(_pr_activeLock);
485 }
486
487 (*thread->startFunc)(thread->arg);
488
489 /*
490 * The following two assertions are meant for NT asynch io.
491 *
492 * The thread should have no asynch io in progress when it
493 * exits, otherwise the overlapped buffer, which is part of
494 * the thread structure, would become invalid.
495 */
496 PR_ASSERT(thread->io_pending == PR_FALSE);
497 /*
498 * This assertion enforces the programming guideline that
499 * if an io function times out or is interrupted, the thread
500 * should close the fd to force the asynch io to abort
501 * before it exits. Right now, closing the fd is the only
502 * way to clear the io_suspended flag.
503 */
504 PR_ASSERT(thread->io_suspended == PR_FALSE);
505
506 PR_Lock(_pr_activeLock);
507 /*
508 * remove thread from list of active threads
509 */
510 if (!(thread->flags & _PR_IDLE_THREAD)) {
511 PR_REMOVE_LINK(&thread->active);
512 _pr_local_threads--;
513 }
514 PR_Unlock(_pr_activeLock);
515 PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting"));
516
517 /* All done, time to go away */
518 _PR_CleanupThread(thread);
519
520 _PR_INTSOFF(is);
521
522 _PR_NotifyJoinWaiters(thread);
523
524 _PR_DecrActiveThreadCount(thread);
525
526 thread->state = _PR_DEAD_STATE;
527
528 if (!_pr_recycleThreads || (_PR_RecycleThread(thread) ==
529 PR_FAILURE)) {
530 /*
531 ** Destroy the thread resources
532 */
533 _PR_UserDestroyThread(thread);
534 }
535
536 /*
537 ** Find another user thread to run. This cpu has finished the
538 ** previous threads main and is now ready to run another thread.
539 */
540 {
541 PRInt32 is;
542 _PR_INTSOFF(is);
543 _PR_MD_SWITCH_CONTEXT(thread);
544 }
545
546 /* Will land here when we get scheduled again if we are recycling... */
547 }
548 }
549
550 void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri)
551 {
552 PRThread *me = _PR_MD_CURRENT_THREAD();
553 PRIntn is;
554
555 if ( _PR_IS_NATIVE_THREAD(thread) ) {
556 _PR_MD_SET_PRIORITY(&(thread->md), newPri);
557 return;
558 }
559
560 if (!_PR_IS_NATIVE_THREAD(me))
561 _PR_INTSOFF(is);
562 _PR_THREAD_LOCK(thread);
563 if (newPri != thread->priority) {
564 _PRCPU *cpu = thread->cpu;
565
566 switch (thread->state) {
567 case _PR_RUNNING:
568 /* Change my priority */
569
570 _PR_RUNQ_LOCK(cpu);
571 thread->priority = newPri;
572 if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) {
573 if (!_PR_IS_NATIVE_THREAD(me))
574 _PR_SET_RESCHED_FLAG();
575 }
576 _PR_RUNQ_UNLOCK(cpu);
577 break;
578
579 case _PR_RUNNABLE:
580
581 _PR_RUNQ_LOCK(cpu);
582 /* Move to different runQ */
583 _PR_DEL_RUNQ(thread);
584 thread->priority = newPri;
585 PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
586 _PR_ADD_RUNQ(thread, cpu, newPri);
587 _PR_RUNQ_UNLOCK(cpu);
588
589 if (newPri > me->priority) {
590 if (!_PR_IS_NATIVE_THREAD(me))
591 _PR_SET_RESCHED_FLAG();
592 }
593
594 break;
595
596 case _PR_LOCK_WAIT:
597 case _PR_COND_WAIT:
598 case _PR_IO_WAIT:
599 case _PR_SUSPENDED:
600
601 thread->priority = newPri;
602 break;
603 }
604 }
605 _PR_THREAD_UNLOCK(thread);
606 if (!_PR_IS_NATIVE_THREAD(me))
607 _PR_INTSON(is);
608 }
609
610 /*
611 ** Suspend the named thread and copy its gc registers into regBuf
612 */
613 static void _PR_Suspend(PRThread *thread)
614 {
615 PRIntn is;
616 PRThread *me = _PR_MD_CURRENT_THREAD();
617
618 PR_ASSERT(thread != me);
619 PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu));
620
621 if (!_PR_IS_NATIVE_THREAD(me))
622 _PR_INTSOFF(is);
623 _PR_THREAD_LOCK(thread);
624 switch (thread->state) {
625 case _PR_RUNNABLE:
626 if (!_PR_IS_NATIVE_THREAD(thread)) {
627 _PR_RUNQ_LOCK(thread->cpu);
628 _PR_DEL_RUNQ(thread);
629 _PR_RUNQ_UNLOCK(thread->cpu);
630
631 _PR_MISCQ_LOCK(thread->cpu);
632 _PR_ADD_SUSPENDQ(thread, thread->cpu);
633 _PR_MISCQ_UNLOCK(thread->cpu);
634 } else {
635 /*
636 * Only LOCAL threads are suspended by _PR_Suspend
637 */
638 PR_ASSERT(0);
639 }
640 thread->state = _PR_SUSPENDED;
641 break;
642
643 case _PR_RUNNING:
644 /*
645 * The thread being suspended should be a LOCAL thread with
646 * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
647 */
648 PR_ASSERT(0);
649 break;
650
651 case _PR_LOCK_WAIT:
652 case _PR_IO_WAIT:
653 case _PR_COND_WAIT:
654 if (_PR_IS_NATIVE_THREAD(thread)) {
655 _PR_MD_SUSPEND_THREAD(thread);
656 }
657 thread->flags |= _PR_SUSPENDING;
658 break;
659
660 default:
661 PR_Abort();
662 }
663 _PR_THREAD_UNLOCK(thread);
664 if (!_PR_IS_NATIVE_THREAD(me))
665 _PR_INTSON(is);
666 }
667
668 static void _PR_Resume(PRThread *thread)
669 {
670 PRThreadPriority pri;
671 PRIntn is;
672 PRThread *me = _PR_MD_CURRENT_THREAD();
673
674 if (!_PR_IS_NATIVE_THREAD(me))
675 _PR_INTSOFF(is);
676 _PR_THREAD_LOCK(thread);
677 switch (thread->state) {
678 case _PR_SUSPENDED:
679 thread->state = _PR_RUNNABLE;
680 thread->flags &= ~_PR_SUSPENDING;
681 if (!_PR_IS_NATIVE_THREAD(thread)) {
682 _PR_MISCQ_LOCK(thread->cpu);
683 _PR_DEL_SUSPENDQ(thread);
684 _PR_MISCQ_UNLOCK(thread->cpu);
685
686 pri = thread->priority;
687
688 _PR_RUNQ_LOCK(thread->cpu);
689 _PR_ADD_RUNQ(thread, thread->cpu, pri);
690 _PR_RUNQ_UNLOCK(thread->cpu);
691
692 if (pri > _PR_MD_CURRENT_THREAD()->priority) {
693 if (!_PR_IS_NATIVE_THREAD(me))
694 _PR_SET_RESCHED_FLAG();
695 }
696 } else {
697 PR_ASSERT(0);
698 }
699 break;
700
701 case _PR_IO_WAIT:
702 case _PR_COND_WAIT:
703 thread->flags &= ~_PR_SUSPENDING;
704 /* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */
705 break;
706
707 case _PR_LOCK_WAIT:
708 {
709 PRLock *wLock = thread->wait.lock;
710
711 thread->flags &= ~_PR_SUSPENDING;
712
713 _PR_LOCK_LOCK(wLock);
714 if (thread->wait.lock->owner == 0) {
715 _PR_UnblockLockWaiter(thread->wait.lock);
716 }
717 _PR_LOCK_UNLOCK(wLock);
718 break;
719 }
720 case _PR_RUNNABLE:
721 break;
722 case _PR_RUNNING:
723 /*
724 * The thread being suspended should be a LOCAL thread with
725 * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state
726 */
727 PR_ASSERT(0);
728 break;
729
730 default:
731 /*
732 * thread should have been in one of the above-listed blocked states
733 * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE)
734 */
735 PR_Abort();
736 }
737 _PR_THREAD_UNLOCK(thread);
738 if (!_PR_IS_NATIVE_THREAD(me))
739 _PR_INTSON(is);
740
741 }
742
743 #if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
744 static PRThread *get_thread(_PRCPU *cpu, PRBool *wakeup_cpus)
745 {
746 PRThread *thread;
747 PRIntn pri;
748 PRUint32 r;
749 PRCList *qp;
750 PRIntn priMin, priMax;
751
752 _PR_RUNQ_LOCK(cpu);
753 r = _PR_RUNQREADYMASK(cpu);
754 if (r==0) {
755 priMin = priMax = PR_PRIORITY_FIRST;
756 } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
757 priMin = priMax = PR_PRIORITY_NORMAL;
758 } else {
759 priMin = PR_PRIORITY_FIRST;
760 priMax = PR_PRIORITY_LAST;
761 }
762 thread = NULL;
763 for (pri = priMax; pri >= priMin ; pri-- ) {
764 if (r & (1 << pri)) {
765 for (qp = _PR_RUNQ(cpu)[pri].next;
766 qp != &_PR_RUNQ(cpu)[pri];
767 qp = qp->next) {
768 thread = _PR_THREAD_PTR(qp);
769 /*
770 * skip non-schedulable threads
771 */
772 PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
773 if (thread->no_sched) {
774 thread = NULL;
775 /*
776 * Need to wakeup cpus to avoid missing a
777 * runnable thread
778 * Waking up all CPU's need happen only once.
779 */
780
781 *wakeup_cpus = PR_TRUE;
782 continue;
783 } else if (thread->flags & _PR_BOUND_THREAD) {
784 /*
785 * Thread bound to cpu 0
786 */
787
788 thread = NULL;
789 #ifdef IRIX
790 _PR_MD_WAKEUP_PRIMORDIAL_CPU();
791 #endif
792 continue;
793 } else if (thread->io_pending == PR_TRUE) {
794 /*
795 * A thread that is blocked for I/O needs to run
796 * on the same cpu on which it was blocked. This is because
797 * the cpu's ioq is accessed without lock protection and sch eduling
798 * the thread on a different cpu would preclude this optimiz ation.
799 */
800 thread = NULL;
801 continue;
802 } else {
803 /* Pull thread off of its run queue */
804 _PR_DEL_RUNQ(thread);
805 _PR_RUNQ_UNLOCK(cpu);
806 return(thread);
807 }
808 }
809 }
810 thread = NULL;
811 }
812 _PR_RUNQ_UNLOCK(cpu);
813 return(thread);
814 }
815 #endif /* !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) */
816
817 /*
818 ** Schedule this native thread by finding the highest priority nspr
819 ** thread that is ready to run.
820 **
821 ** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls
822 ** PR_Schedule() rather than calling PR_Schedule. Otherwise if there
823 ** is initialization required for switching from SWITCH_CONTEXT,
824 ** it will not get done!
825 */
826 void _PR_Schedule(void)
827 {
828 PRThread *thread, *me = _PR_MD_CURRENT_THREAD();
829 _PRCPU *cpu = _PR_MD_CURRENT_CPU();
830 PRIntn pri;
831 PRUint32 r;
832 PRCList *qp;
833 PRIntn priMin, priMax;
834 #if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
835 PRBool wakeup_cpus;
836 #endif
837
838 /* Interrupts must be disabled */
839 PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0);
840
841 /* Since we are rescheduling, we no longer want to */
842 _PR_CLEAR_RESCHED_FLAG();
843
844 /*
845 ** Find highest priority thread to run. Bigger priority numbers are
846 ** higher priority threads
847 */
848 _PR_RUNQ_LOCK(cpu);
849 /*
850 * if we are in SuspendAll mode, can schedule only the thread
851 * that called PR_SuspendAll
852 *
853 * The thread may be ready to run now, after completing an I/O
854 * operation, for example
855 */
856 if ((thread = suspendAllThread) != 0) {
857 if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) {
858 /* Pull thread off of its run queue */
859 _PR_DEL_RUNQ(thread);
860 _PR_RUNQ_UNLOCK(cpu);
861 goto found_thread;
862 } else {
863 thread = NULL;
864 _PR_RUNQ_UNLOCK(cpu);
865 goto idle_thread;
866 }
867 }
868 r = _PR_RUNQREADYMASK(cpu);
869 if (r==0) {
870 priMin = priMax = PR_PRIORITY_FIRST;
871 } else if (r == (1<<PR_PRIORITY_NORMAL) ) {
872 priMin = priMax = PR_PRIORITY_NORMAL;
873 } else {
874 priMin = PR_PRIORITY_FIRST;
875 priMax = PR_PRIORITY_LAST;
876 }
877 thread = NULL;
878 for (pri = priMax; pri >= priMin ; pri-- ) {
879 if (r & (1 << pri)) {
880 for (qp = _PR_RUNQ(cpu)[pri].next;
881 qp != &_PR_RUNQ(cpu)[pri];
882 qp = qp->next) {
883 thread = _PR_THREAD_PTR(qp);
884 /*
885 * skip non-schedulable threads
886 */
887 PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD));
888 if ((thread->no_sched) && (me != thread)){
889 thread = NULL;
890 continue;
891 } else {
892 /* Pull thread off of its run queue */
893 _PR_DEL_RUNQ(thread);
894 _PR_RUNQ_UNLOCK(cpu);
895 goto found_thread;
896 }
897 }
898 }
899 thread = NULL;
900 }
901 _PR_RUNQ_UNLOCK(cpu);
902
903 #if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX)
904
905 wakeup_cpus = PR_FALSE;
906 _PR_CPU_LIST_LOCK();
907 for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) {
908 if (cpu != _PR_CPU_PTR(qp)) {
909 if ((thread = get_thread(_PR_CPU_PTR(qp), &wakeup_cpus))
910 != NULL) {
911 thread->cpu = cpu;
912 _PR_CPU_LIST_UNLOCK();
913 if (wakeup_cpus == PR_TRUE)
914 _PR_MD_WAKEUP_CPUS();
915 goto found_thread;
916 }
917 }
918 }
919 _PR_CPU_LIST_UNLOCK();
920 if (wakeup_cpus == PR_TRUE)
921 _PR_MD_WAKEUP_CPUS();
922
923 #endif /* _PR_LOCAL_THREADS_ONLY */
924
925 idle_thread:
926 /*
927 ** There are no threads to run. Switch to the idle thread
928 */
929 PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing"));
930 thread = _PR_MD_CURRENT_CPU()->idle_thread;
931
932 found_thread:
933 PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) &&
934 (!(thread->no_sched))));
935
936 /* Resume the thread */
937 PR_LOG(_pr_sched_lm, PR_LOG_MAX,
938 ("switching to %d[%p]", thread->id, thread));
939 PR_ASSERT(thread->state != _PR_RUNNING);
940 thread->state = _PR_RUNNING;
941
942 /* If we are on the runq, it just means that we went to sleep on some
943 * resource, and by the time we got here another real native thread had
944 * already given us the resource and put us back on the runqueue
945 */
946 PR_ASSERT(thread->cpu == _PR_MD_CURRENT_CPU());
947 if (thread != me)
948 _PR_MD_RESTORE_CONTEXT(thread);
949 #if 0
950 /* XXXMB; with setjmp/longjmp it is impossible to land here, but
951 * it is not with fibers... Is this a bad thing? I believe it is
952 * still safe.
953 */
954 PR_NOT_REACHED("impossible return from schedule");
955 #endif
956 }
957
958 /*
959 ** Attaches a thread.
960 ** Does not set the _PR_MD_CURRENT_THREAD.
961 ** Does not specify the scope of the thread.
962 */
963 static PRThread *
964 _PR_AttachThread(PRThreadType type, PRThreadPriority priority,
965 PRThreadStack *stack)
966 {
967 PRThread *thread;
968 char *mem;
969
970 if (priority > PR_PRIORITY_LAST) {
971 priority = PR_PRIORITY_LAST;
972 } else if (priority < PR_PRIORITY_FIRST) {
973 priority = PR_PRIORITY_FIRST;
974 }
975
976 mem = (char*) PR_CALLOC(sizeof(PRThread));
977 if (mem) {
978 thread = (PRThread*) mem;
979 thread->priority = priority;
980 thread->stack = stack;
981 thread->state = _PR_RUNNING;
982 PR_INIT_CLIST(&thread->lockList);
983 if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
984 PR_DELETE(thread);
985 return 0;
986 }
987
988 return thread;
989 }
990 return 0;
991 }
992
993
994
995 PR_IMPLEMENT(PRThread*)
996 _PR_NativeCreateThread(PRThreadType type,
997 void (*start)(void *arg),
998 void *arg,
999 PRThreadPriority priority,
1000 PRThreadScope scope,
1001 PRThreadState state,
1002 PRUint32 stackSize,
1003 PRUint32 flags)
1004 {
1005 PRThread *thread;
1006
1007 thread = _PR_AttachThread(type, priority, NULL);
1008
1009 if (thread) {
1010 PR_Lock(_pr_activeLock);
1011 thread->flags = (flags | _PR_GLOBAL_SCOPE);
1012 thread->id = ++_pr_utid;
1013 if (type == PR_SYSTEM_THREAD) {
1014 thread->flags |= _PR_SYSTEM;
1015 _pr_systemActive++;
1016 } else {
1017 _pr_userActive++;
1018 }
1019 PR_Unlock(_pr_activeLock);
1020
1021 thread->stack = PR_NEWZAP(PRThreadStack);
1022 if (!thread->stack) {
1023 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1024 goto done;
1025 }
1026 thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE;
1027 thread->stack->thr = thread;
1028 thread->startFunc = start;
1029 thread->arg = arg;
1030
1031 /*
1032 Set thread flags related to scope and joinable state. If joinable
1033 thread, allocate a "termination" conidition variable.
1034 */
1035 if (state == PR_JOINABLE_THREAD) {
1036 thread->term = PR_NewCondVar(_pr_terminationCVLock);
1037 if (thread->term == NULL) {
1038 PR_DELETE(thread->stack);
1039 goto done;
1040 }
1041 }
1042
1043 thread->state = _PR_RUNNING;
1044 if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority,
1045 scope,state,stackSize) == PR_SUCCESS) {
1046 return thread;
1047 }
1048 if (thread->term) {
1049 PR_DestroyCondVar(thread->term);
1050 thread->term = NULL;
1051 }
1052 PR_DELETE(thread->stack);
1053 }
1054
1055 done:
1056 if (thread) {
1057 _PR_DecrActiveThreadCount(thread);
1058 _PR_DestroyThread(thread);
1059 }
1060 return NULL;
1061 }
1062
1063 /************************************************************************/
1064
1065 PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type,
1066 void (*start)(void *arg),
1067 void *arg,
1068 PRThreadPriority priority,
1069 PRThreadScope scope,
1070 PRThreadState state,
1071 PRUint32 stackSize,
1072 PRUint32 flags)
1073 {
1074 PRThread *me;
1075 PRThread *thread = NULL;
1076 PRThreadStack *stack;
1077 char *top;
1078 PRIntn is;
1079 PRIntn native = 0;
1080 PRIntn useRecycled = 0;
1081 PRBool status;
1082
1083 /*
1084 First, pin down the priority. Not all compilers catch passing out of
1085 range enum here. If we let bad values thru, priority queues won't work.
1086 */
1087 if (priority > PR_PRIORITY_LAST) {
1088 priority = PR_PRIORITY_LAST;
1089 } else if (priority < PR_PRIORITY_FIRST) {
1090 priority = PR_PRIORITY_FIRST;
1091 }
1092
1093 if (!_pr_initialized) _PR_ImplicitInitialization();
1094
1095 if (! (flags & _PR_IDLE_THREAD))
1096 me = _PR_MD_CURRENT_THREAD();
1097
1098 #if defined(_PR_GLOBAL_THREADS_ONLY)
1099 /*
1100 * can create global threads only
1101 */
1102 if (scope == PR_LOCAL_THREAD)
1103 scope = PR_GLOBAL_THREAD;
1104 #endif
1105
1106 if (_native_threads_only)
1107 scope = PR_GLOBAL_THREAD;
1108
1109 native = (((scope == PR_GLOBAL_THREAD)|| (scope == PR_GLOBAL_BOUND_THREAD))
1110 && _PR_IS_NATIVE_THREAD_ SUPPORTED());
1111
1112 _PR_ADJUST_STACKSIZE(stackSize);
1113
1114 if (native) {
1115 /*
1116 * clear the IDLE_THREAD flag which applies to LOCAL
1117 * threads only
1118 */
1119 flags &= ~_PR_IDLE_THREAD;
1120 flags |= _PR_GLOBAL_SCOPE;
1121 if (_PR_NUM_DEADNATIVE > 0) {
1122 _PR_DEADQ_LOCK;
1123
1124 if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */
1125 _PR_DEADQ_UNLOCK;
1126 } else {
1127 thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next);
1128 PR_REMOVE_LINK(&thread->links);
1129 _PR_DEC_DEADNATIVE;
1130 _PR_DEADQ_UNLOCK;
1131
1132 _PR_InitializeRecycledThread(thread);
1133 thread->startFunc = start;
1134 thread->arg = arg;
1135 thread->flags = (flags | _PR_GLOBAL_SCOPE);
1136 if (type == PR_SYSTEM_THREAD)
1137 {
1138 thread->flags |= _PR_SYSTEM;
1139 PR_ATOMIC_INCREMENT(&_pr_systemActive);
1140 }
1141 else PR_ATOMIC_INCREMENT(&_pr_userActive);
1142
1143 if (state == PR_JOINABLE_THREAD) {
1144 if (!thread->term)
1145 thread->term = PR_NewCondVar(_pr_terminationCVLock);
1146 }
1147 else {
1148 if(thread->term) {
1149 PR_DestroyCondVar(thread->term);
1150 thread->term = 0;
1151 }
1152 }
1153
1154 thread->priority = priority;
1155 _PR_MD_SET_PRIORITY(&(thread->md), priority);
1156 /* XXX what about stackSize? */
1157 thread->state = _PR_RUNNING;
1158 _PR_MD_WAKEUP_WAITER(thread);
1159 return thread;
1160 }
1161 }
1162 thread = _PR_NativeCreateThread(type, start, arg, priority,
1163 scope, state, stackSize, flags);
1164 } else {
1165 if (_PR_NUM_DEADUSER > 0) {
1166 _PR_DEADQ_LOCK;
1167
1168 if (_PR_NUM_DEADUSER == 0) { /* thread safe check */
1169 _PR_DEADQ_UNLOCK;
1170 } else {
1171 PRCList *ptr;
1172
1173 /* Go down list checking for a recycled thread with a
1174 * large enough stack. XXXMB - this has a bad degenerate case.
1175 */
1176 ptr = _PR_DEADUSERQ.next;
1177 while( ptr != &_PR_DEADUSERQ ) {
1178 thread = _PR_THREAD_PTR(ptr);
1179 if ((thread->stack->stackSize >= stackSize) &&
1180 (!thread->no_sched)) {
1181 PR_REMOVE_LINK(&thread->links);
1182 _PR_DEC_DEADUSER;
1183 break;
1184 } else {
1185 ptr = ptr->next;
1186 thread = NULL;
1187 }
1188 }
1189
1190 _PR_DEADQ_UNLOCK;
1191
1192 if (thread) {
1193 _PR_InitializeRecycledThread(thread);
1194 thread->startFunc = start;
1195 thread->arg = arg;
1196 thread->priority = priority;
1197 if (state == PR_JOINABLE_THREAD) {
1198 if (!thread->term)
1199 thread->term = PR_NewCondVar(_pr_terminationCVLock);
1200 } else {
1201 if(thread->term) {
1202 PR_DestroyCondVar(thread->term);
1203 thread->term = 0;
1204 }
1205 }
1206 useRecycled++;
1207 }
1208 }
1209 }
1210 if (thread == NULL) {
1211 #ifndef HAVE_CUSTOM_USER_THREADS
1212 stack = _PR_NewStack(stackSize);
1213 if (!stack) {
1214 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1215 return NULL;
1216 }
1217
1218 /* Allocate thread object and per-thread data off the top of the sta ck*/
1219 top = stack->stackTop;
1220 #ifdef HAVE_STACK_GROWING_UP
1221 thread = (PRThread*) top;
1222 top = top + sizeof(PRThread);
1223 /*
1224 * Make stack 64-byte aligned
1225 */
1226 if ((PRUptrdiff)top & 0x3f) {
1227 top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f);
1228 }
1229 #else
1230 top = top - sizeof(PRThread);
1231 thread = (PRThread*) top;
1232 /*
1233 * Make stack 64-byte aligned
1234 */
1235 if ((PRUptrdiff)top & 0x3f) {
1236 top = (char*)((PRUptrdiff)top & ~0x3f);
1237 }
1238 #endif
1239 stack->thr = thread;
1240 memset(thread, 0, sizeof(PRThread));
1241 thread->threadAllocatedOnStack = 1;
1242 #else
1243 thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg);
1244 if (!thread) {
1245 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
1246 return NULL;
1247 }
1248 thread->threadAllocatedOnStack = 0;
1249 stack = NULL;
1250 top = NULL;
1251 #endif
1252
1253 /* Initialize thread */
1254 thread->tpdLength = 0;
1255 thread->privateData = NULL;
1256 thread->stack = stack;
1257 thread->priority = priority;
1258 thread->startFunc = start;
1259 thread->arg = arg;
1260 PR_INIT_CLIST(&thread->lockList);
1261
1262 if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) {
1263 if (thread->threadAllocatedOnStack == 1)
1264 _PR_FreeStack(thread->stack);
1265 else {
1266 PR_DELETE(thread);
1267 }
1268 PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
1269 return NULL;
1270 }
1271
1272 if (_PR_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) {
1273 if (thread->threadAllocatedOnStack == 1)
1274 _PR_FreeStack(thread->stack);
1275 else {
1276 PR_DELETE(thread->privateData);
1277 PR_DELETE(thread);
1278 }
1279 PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0);
1280 return NULL;
1281 }
1282
1283 _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status);
1284
1285 if (status == PR_FALSE) {
1286 _PR_MD_FREE_LOCK(&thread->threadLock);
1287 if (thread->threadAllocatedOnStack == 1)
1288 _PR_FreeStack(thread->stack);
1289 else {
1290 PR_DELETE(thread->privateData);
1291 PR_DELETE(thread);
1292 }
1293 return NULL;
1294 }
1295
1296 /*
1297 Set thread flags related to scope and joinable state. If joinable
1298 thread, allocate a "termination" condition variable.
1299 */
1300 if (state == PR_JOINABLE_THREAD) {
1301 thread->term = PR_NewCondVar(_pr_terminationCVLock);
1302 if (thread->term == NULL) {
1303 _PR_MD_FREE_LOCK(&thread->threadLock);
1304 if (thread->threadAllocatedOnStack == 1)
1305 _PR_FreeStack(thread->stack);
1306 else {
1307 PR_DELETE(thread->privateData);
1308 PR_DELETE(thread);
1309 }
1310 return NULL;
1311 }
1312 }
1313
1314 }
1315
1316 /* Update thread type counter */
1317 PR_Lock(_pr_activeLock);
1318 thread->flags = flags;
1319 thread->id = ++_pr_utid;
1320 if (type == PR_SYSTEM_THREAD) {
1321 thread->flags |= _PR_SYSTEM;
1322 _pr_systemActive++;
1323 } else {
1324 _pr_userActive++;
1325 }
1326
1327 /* Make thread runnable */
1328 thread->state = _PR_RUNNABLE;
1329 /*
1330 * Add to list of active threads
1331 */
1332 PR_Unlock(_pr_activeLock);
1333
1334 if ((! (thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me) )
1335 thread->cpu = _PR_GetPrimordialCPU();
1336 else
1337 thread->cpu = _PR_MD_CURRENT_CPU();
1338
1339 PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
1340
1341 if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) {
1342 _PR_INTSOFF(is);
1343 _PR_RUNQ_LOCK(thread->cpu);
1344 _PR_ADD_RUNQ(thread, thread->cpu, priority);
1345 _PR_RUNQ_UNLOCK(thread->cpu);
1346 }
1347
1348 if (thread->flags & _PR_IDLE_THREAD) {
1349 /*
1350 ** If the creating thread is a kernel thread, we need to
1351 ** awaken the user thread idle thread somehow; potentially
1352 ** it could be sleeping in its idle loop, and we need to poke
1353 ** it. To do so, wake the idle thread...
1354 */
1355 _PR_MD_WAKEUP_WAITER(NULL);
1356 } else if (_PR_IS_NATIVE_THREAD(me)) {
1357 _PR_MD_WAKEUP_WAITER(thread);
1358 }
1359 if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) )
1360 _PR_INTSON(is);
1361 }
1362
1363 return thread;
1364 }
1365
1366 PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type,
1367 void (*start)(void *arg),
1368 void *arg,
1369 PRThreadPriority priority,
1370 PRThreadScope scope,
1371 PRThreadState state,
1372 PRUint32 stackSize)
1373 {
1374 return _PR_CreateThread(type, start, arg, priority, scope, state,
1375 stackSize, 0);
1376 }
1377
1378 /*
1379 ** Associate a thread object with an existing native thread.
1380 ** "type" is the type of thread object to attach
1381 ** "priority" is the priority to assign to the thread
1382 ** "stack" defines the shape of the threads stack
1383 **
1384 ** This can return NULL if some kind of error occurs, or if memory is
1385 ** tight.
1386 **
1387 ** This call is not normally needed unless you create your own native
1388 ** thread. PR_Init does this automatically for the primordial thread.
1389 */
1390 PRThread* _PRI_AttachThread(PRThreadType type,
1391 PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags)
1392 {
1393 PRThread *thread;
1394
1395 if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) {
1396 return thread;
1397 }
1398 _PR_MD_SET_CURRENT_THREAD(NULL);
1399
1400 /* Clear out any state if this thread was attached before */
1401 _PR_MD_SET_CURRENT_CPU(NULL);
1402
1403 thread = _PR_AttachThread(type, priority, stack);
1404 if (thread) {
1405 PRIntn is;
1406
1407 _PR_MD_SET_CURRENT_THREAD(thread);
1408
1409 thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED;
1410
1411 if (!stack) {
1412 thread->stack = PR_NEWZAP(PRThreadStack);
1413 if (!thread->stack) {
1414 _PR_DestroyThread(thread);
1415 return NULL;
1416 }
1417 thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE;
1418 }
1419 PR_INIT_CLIST(&thread->links);
1420
1421 if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) {
1422 PR_DELETE(thread->stack);
1423 _PR_DestroyThread(thread);
1424 return NULL;
1425 }
1426
1427 _PR_MD_SET_CURRENT_CPU(NULL);
1428
1429 if (_PR_MD_CURRENT_CPU()) {
1430 _PR_INTSOFF(is);
1431 PR_Lock(_pr_activeLock);
1432 }
1433 if (type == PR_SYSTEM_THREAD) {
1434 thread->flags |= _PR_SYSTEM;
1435 _pr_systemActive++;
1436 } else {
1437 _pr_userActive++;
1438 }
1439 if (_PR_MD_CURRENT_CPU()) {
1440 PR_Unlock(_pr_activeLock);
1441 _PR_INTSON(is);
1442 }
1443 }
1444 return thread;
1445 }
1446
1447 PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type,
1448 PRThreadPriority priority, PRThreadStack *stack)
1449 {
1450 return PR_GetCurrentThread();
1451 }
1452
1453 PR_IMPLEMENT(void) PR_DetachThread(void)
1454 {
1455 /*
1456 * On IRIX, Solaris, and Windows, foreign threads are detached when
1457 * they terminate.
1458 */
1459 #if !defined(IRIX) && !defined(WIN32) \
1460 && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))
1461 PRThread *me;
1462 if (_pr_initialized) {
1463 me = _PR_MD_GET_ATTACHED_THREAD();
1464 if ((me != NULL) && (me->flags & _PR_ATTACHED))
1465 _PRI_DetachThread();
1466 }
1467 #endif
1468 }
1469
1470 void _PRI_DetachThread(void)
1471 {
1472 PRThread *me = _PR_MD_CURRENT_THREAD();
1473
1474 if (me->flags & _PR_PRIMORDIAL) {
1475 /*
1476 * ignore, if primordial thread
1477 */
1478 return;
1479 }
1480 PR_ASSERT(me->flags & _PR_ATTACHED);
1481 PR_ASSERT(_PR_IS_NATIVE_THREAD(me));
1482 _PR_CleanupThread(me);
1483 PR_DELETE(me->privateData);
1484
1485 _PR_DecrActiveThreadCount(me);
1486
1487 _PR_MD_CLEAN_THREAD(me);
1488 _PR_MD_SET_CURRENT_THREAD(NULL);
1489 if (!me->threadAllocatedOnStack)
1490 PR_DELETE(me->stack);
1491 _PR_MD_FREE_LOCK(&me->threadLock);
1492 PR_DELETE(me);
1493 }
1494
1495 /*
1496 ** Wait for thread termination:
1497 ** "thread" is the target thread
1498 **
1499 ** This can return PR_FAILURE if no joinable thread could be found
1500 ** corresponding to the specified target thread.
1501 **
1502 ** The calling thread is suspended until the target thread completes.
1503 ** Several threads cannot wait for the same thread to complete; one thread
1504 ** will complete successfully and others will terminate with an error PR_FAILURE .
1505 ** The calling thread will not be blocked if the target thread has already
1506 ** terminated.
1507 */
1508 PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread)
1509 {
1510 PRIntn is;
1511 PRCondVar *term;
1512 PRThread *me = _PR_MD_CURRENT_THREAD();
1513
1514 if (!_PR_IS_NATIVE_THREAD(me))
1515 _PR_INTSOFF(is);
1516 term = thread->term;
1517 /* can't join a non-joinable thread */
1518 if (term == NULL) {
1519 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1520 goto ErrorExit;
1521 }
1522
1523 /* multiple threads can't wait on the same joinable thread */
1524 if (term->condQ.next != &term->condQ) {
1525 goto ErrorExit;
1526 }
1527 if (!_PR_IS_NATIVE_THREAD(me))
1528 _PR_INTSON(is);
1529
1530 /* wait for the target thread's termination cv invariant */
1531 PR_Lock (_pr_terminationCVLock);
1532 while (thread->state != _PR_JOIN_WAIT) {
1533 (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT);
1534 }
1535 (void) PR_Unlock (_pr_terminationCVLock);
1536
1537 /*
1538 Remove target thread from global waiting to join Q; make it runnable
1539 again and put it back on its run Q. When it gets scheduled later in
1540 _PR_RunThread code, it will clean up its stack.
1541 */
1542 if (!_PR_IS_NATIVE_THREAD(me))
1543 _PR_INTSOFF(is);
1544 thread->state = _PR_RUNNABLE;
1545 if ( !_PR_IS_NATIVE_THREAD(thread) ) {
1546 _PR_THREAD_LOCK(thread);
1547
1548 _PR_MISCQ_LOCK(thread->cpu);
1549 _PR_DEL_JOINQ(thread);
1550 _PR_MISCQ_UNLOCK(thread->cpu);
1551
1552 _PR_AddThreadToRunQ(me, thread);
1553 _PR_THREAD_UNLOCK(thread);
1554 }
1555 if (!_PR_IS_NATIVE_THREAD(me))
1556 _PR_INTSON(is);
1557
1558 _PR_MD_WAKEUP_WAITER(thread);
1559
1560 return PR_SUCCESS;
1561
1562 ErrorExit:
1563 if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is);
1564 return PR_FAILURE;
1565 }
1566
1567 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread,
1568 PRThreadPriority newPri)
1569 {
1570
1571 /*
1572 First, pin down the priority. Not all compilers catch passing out of
1573 range enum here. If we let bad values thru, priority queues won't work.
1574 */
1575 if ((PRIntn)newPri > (PRIntn)PR_PRIORITY_LAST) {
1576 newPri = PR_PRIORITY_LAST;
1577 } else if ((PRIntn)newPri < (PRIntn)PR_PRIORITY_FIRST) {
1578 newPri = PR_PRIORITY_FIRST;
1579 }
1580
1581 if ( _PR_IS_NATIVE_THREAD(thread) ) {
1582 thread->priority = newPri;
1583 _PR_MD_SET_PRIORITY(&(thread->md), newPri);
1584 } else _PR_SetThreadPriority(thread, newPri);
1585 }
1586
1587 PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name)
1588 {
1589 PRThread *thread;
1590 size_t nameLen;
1591
1592 if (!name) {
1593 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1594 return PR_FAILURE;
1595 }
1596
1597 thread = PR_GetCurrentThread();
1598 if (!thread)
1599 return PR_FAILURE;
1600
1601 PR_Free(thread->name);
1602 nameLen = strlen(name);
1603 thread->name = (char *)PR_Malloc(nameLen + 1);
1604 if (!thread->name)
1605 return PR_FAILURE;
1606 memcpy(thread->name, name, nameLen + 1);
1607 _PR_MD_SET_CURRENT_THREAD_NAME(thread->name);
1608 return PR_SUCCESS;
1609 }
1610
1611 PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread)
1612 {
1613 if (!thread)
1614 return NULL;
1615 return thread->name;
1616 }
1617
1618
1619 /*
1620 ** This routine prevents all other threads from running. This call is needed by
1621 ** the garbage collector.
1622 */
1623 PR_IMPLEMENT(void) PR_SuspendAll(void)
1624 {
1625 PRThread *me = _PR_MD_CURRENT_THREAD();
1626 PRCList *qp;
1627
1628 /*
1629 * Stop all user and native threads which are marked GC able.
1630 */
1631 PR_Lock(_pr_activeLock);
1632 suspendAllOn = PR_TRUE;
1633 suspendAllThread = _PR_MD_CURRENT_THREAD();
1634 _PR_MD_BEGIN_SUSPEND_ALL();
1635 for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
1636 qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
1637 if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
1638 _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp))) {
1639 _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp));
1640 PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING);
1641 }
1642 }
1643 for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
1644 qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
1645 if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
1646 _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
1647 /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */
1648 _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
1649 }
1650 _PR_MD_END_SUSPEND_ALL();
1651 }
1652
1653 /*
1654 ** This routine unblocks all other threads that were suspended from running by
1655 ** PR_SuspendAll(). This call is needed by the garbage collector.
1656 */
1657 PR_IMPLEMENT(void) PR_ResumeAll(void)
1658 {
1659 PRThread *me = _PR_MD_CURRENT_THREAD();
1660 PRCList *qp;
1661
1662 /*
1663 * Resume all user and native threads which are marked GC able.
1664 */
1665 _PR_MD_BEGIN_RESUME_ALL();
1666 for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
1667 qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) {
1668 if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
1669 _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
1670 _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp));
1671 }
1672 for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
1673 qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) {
1674 if ((me != _PR_ACTIVE_THREAD_PTR(qp)) &&
1675 _PR_IS_GCABLE_THREAD(_PR_ACTIVE_THREAD_PTR(qp)))
1676 _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp));
1677 }
1678 _PR_MD_END_RESUME_ALL();
1679 suspendAllThread = NULL;
1680 suspendAllOn = PR_FALSE;
1681 PR_Unlock(_pr_activeLock);
1682 }
1683
1684 PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
1685 {
1686 PRCList *qp, *qp_next;
1687 PRIntn i = 0;
1688 PRStatus rv = PR_SUCCESS;
1689 PRThread* t;
1690
1691 /*
1692 ** Currently Enumerate threads happen only with suspension and
1693 ** pr_activeLock held
1694 */
1695 PR_ASSERT(suspendAllOn);
1696
1697 /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
1698 * qp->next after applying the function "func". In particular, "func"
1699 * might remove the thread from the queue and put it into another one in
1700 * which case qp->next no longer points to the next entry in the original
1701 * queue.
1702 *
1703 * To get around this problem, we save qp->next in qp_next before applying
1704 * "func" and use that saved value as the next value after applying "func".
1705 */
1706
1707 /*
1708 * Traverse the list of local and global threads
1709 */
1710 for (qp = _PR_ACTIVE_LOCAL_THREADQ().next;
1711 qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next)
1712 {
1713 qp_next = qp->next;
1714 t = _PR_ACTIVE_THREAD_PTR(qp);
1715 if (_PR_IS_GCABLE_THREAD(t))
1716 {
1717 rv = (*func)(t, i, arg);
1718 if (rv != PR_SUCCESS)
1719 return rv;
1720 i++;
1721 }
1722 }
1723 for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next;
1724 qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next)
1725 {
1726 qp_next = qp->next;
1727 t = _PR_ACTIVE_THREAD_PTR(qp);
1728 if (_PR_IS_GCABLE_THREAD(t))
1729 {
1730 rv = (*func)(t, i, arg);
1731 if (rv != PR_SUCCESS)
1732 return rv;
1733 i++;
1734 }
1735 }
1736 return rv;
1737 }
1738
1739 /* FUNCTION: _PR_AddSleepQ
1740 ** DESCRIPTION:
1741 ** Adds a thread to the sleep/pauseQ.
1742 ** RESTRICTIONS:
1743 ** Caller must have the RUNQ lock.
1744 ** Caller must be a user level thread
1745 */
1746 PR_IMPLEMENT(void)
1747 _PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout)
1748 {
1749 _PRCPU *cpu = thread->cpu;
1750
1751 if (timeout == PR_INTERVAL_NO_TIMEOUT) {
1752 /* append the thread to the global pause Q */
1753 PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu));
1754 thread->flags |= _PR_ON_PAUSEQ;
1755 } else {
1756 PRIntervalTime sleep;
1757 PRCList *q;
1758 PRThread *t;
1759
1760 /* sort onto global sleepQ */
1761 sleep = timeout;
1762
1763 /* Check if we are longest timeout */
1764 if (timeout >= _PR_SLEEPQMAX(cpu)) {
1765 PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu));
1766 thread->sleep = timeout - _PR_SLEEPQMAX(cpu);
1767 _PR_SLEEPQMAX(cpu) = timeout;
1768 } else {
1769 /* Sort thread into global sleepQ at appropriate point */
1770 q = _PR_SLEEPQ(cpu).next;
1771
1772 /* Now scan the list for where to insert this entry */
1773 while (q != &_PR_SLEEPQ(cpu)) {
1774 t = _PR_THREAD_PTR(q);
1775 if (sleep < t->sleep) {
1776 /* Found sleeper to insert in front of */
1777 break;
1778 }
1779 sleep -= t->sleep;
1780 q = q->next;
1781 }
1782 thread->sleep = sleep;
1783 PR_INSERT_BEFORE(&thread->links, q);
1784
1785 /*
1786 ** Subtract our sleep time from the sleeper that follows us (there
1787 ** must be one) so that they remain relative to us.
1788 */
1789 PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu));
1790
1791 t = _PR_THREAD_PTR(thread->links.next);
1792 PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread);
1793 t->sleep -= sleep;
1794 }
1795
1796 thread->flags |= _PR_ON_SLEEPQ;
1797 }
1798 }
1799
1800 /* FUNCTION: _PR_DelSleepQ
1801 ** DESCRIPTION:
1802 ** Removes a thread from the sleep/pauseQ.
1803 ** INPUTS:
1804 ** If propogate_time is true, then the thread following the deleted
1805 ** thread will be get the time from the deleted thread. This is used
1806 ** when deleting a sleeper that has not timed out.
1807 ** RESTRICTIONS:
1808 ** Caller must have the RUNQ lock.
1809 ** Caller must be a user level thread
1810 */
1811 PR_IMPLEMENT(void)
1812 _PR_DelSleepQ(PRThread *thread, PRBool propogate_time)
1813 {
1814 _PRCPU *cpu = thread->cpu;
1815
1816 /* Remove from pauseQ/sleepQ */
1817 if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
1818 if (thread->flags & _PR_ON_SLEEPQ) {
1819 PRCList *q = thread->links.next;
1820 if (q != &_PR_SLEEPQ(cpu)) {
1821 if (propogate_time == PR_TRUE) {
1822 PRThread *after = _PR_THREAD_PTR(q);
1823 after->sleep += thread->sleep;
1824 } else
1825 _PR_SLEEPQMAX(cpu) -= thread->sleep;
1826 } else {
1827 /* Check if prev is the beggining of the list; if so,
1828 * we are the only element on the list.
1829 */
1830 if (thread->links.prev != &_PR_SLEEPQ(cpu))
1831 _PR_SLEEPQMAX(cpu) -= thread->sleep;
1832 else
1833 _PR_SLEEPQMAX(cpu) = 0;
1834 }
1835 thread->flags &= ~_PR_ON_SLEEPQ;
1836 } else {
1837 thread->flags &= ~_PR_ON_PAUSEQ;
1838 }
1839 PR_REMOVE_LINK(&thread->links);
1840 } else
1841 PR_ASSERT(0);
1842 }
1843
1844 void
1845 _PR_AddThreadToRunQ(
1846 PRThread *me, /* the current thread */
1847 PRThread *thread) /* the local thread to be added to a run queue */
1848 {
1849 PRThreadPriority pri = thread->priority;
1850 _PRCPU *cpu = thread->cpu;
1851
1852 PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread));
1853
1854 #if defined(WINNT)
1855 /*
1856 * On NT, we can only reliably know that the current CPU
1857 * is not idle. We add the awakened thread to the run
1858 * queue of its CPU if its CPU is the current CPU.
1859 * For any other CPU, we don't really know whether it
1860 * is busy or idle. So in all other cases, we just
1861 * "post" the awakened thread to the IO completion port
1862 * for the next idle CPU to execute (this is done in
1863 * _PR_MD_WAKEUP_WAITER).
1864 * Threads with a suspended I/O operation remain bound to
1865 * the same cpu until I/O is cancelled
1866 *
1867 * NOTE: the boolean expression below must be the exact
1868 * opposite of the corresponding boolean expression in
1869 * _PR_MD_WAKEUP_WAITER.
1870 */
1871 if ((!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) ||
1872 (thread->md.thr_bound_cpu)) {
1873 PR_ASSERT(!thread->md.thr_bound_cpu ||
1874 (thread->md.thr_bound_cp u == cpu));
1875 _PR_RUNQ_LOCK(cpu);
1876 _PR_ADD_RUNQ(thread, cpu, pri);
1877 _PR_RUNQ_UNLOCK(cpu);
1878 }
1879 #else
1880 _PR_RUNQ_LOCK(cpu);
1881 _PR_ADD_RUNQ(thread, cpu, pri);
1882 _PR_RUNQ_UNLOCK(cpu);
1883 if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) {
1884 if (pri > me->priority) {
1885 _PR_SET_RESCHED_FLAG();
1886 }
1887 }
1888 #endif
1889 }
OLDNEW
« no previous file with comments | « nspr/pr/src/threads/combined/prustack.c ('k') | nspr/pr/src/threads/prcmon.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698