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 /*
7 ** File: ptthread.c
8 ** Descritpion: Implemenation for threds using pthreds
9 ** Exports: ptthread.h
10 */
11
12 #if defined(_PR_PTHREADS)
13
14 #include "prlog.h"
15 #include "primpl.h"
16 #include "prpdce.h"
17
18 #include <pthread.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <signal.h>
22 #include <dlfcn.h>
23
24 #if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)
25 #include <pthread_np.h>
26 #endif
27
28 #if defined(ANDROID)
29 #include <sys/prctl.h>
30 #endif
31
32 #ifdef _PR_NICE_PRIORITY_SCHEDULING
33 #undef _POSIX_THREAD_PRIORITY_SCHEDULING
34 #include <sys/resource.h>
35 #ifndef HAVE_GETTID
36 #define gettid() (syscall(SYS_gettid))
37 #endif
38 #endif
39
40 /*
41 * Record whether or not we have the privilege to set the scheduling
42 * policy and priority of threads. 0 means that privilege is available.
43 * EPERM means that privilege is not available.
44 */
45
46 static PRIntn pt_schedpriv = 0;
47 extern PRLock *_pr_sleeplock;
48
49 static struct _PT_Bookeeping
50 {
51 PRLock *ml; /* a lock to protect ourselves */
52 PRCondVar *cv; /* used to signal global things */
53 PRInt32 system, user; /* a count of the two different types */
54 PRUintn this_many; /* number of threads allowed for exit */
55 pthread_key_t key; /* thread private data key */
56 PRBool keyCreated; /* whether 'key' should be deleted */
57 PRThread *first, *last; /* list of threads we know about */
58 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
59 PRInt32 minPrio, maxPrio; /* range of scheduling priorities */
60 #endif
61 } pt_book = {0};
62
63 static void _pt_thread_death(void *arg);
64 static void _pt_thread_death_internal(void *arg, PRBool callDestructors);
65 static void init_pthread_gc_support(void);
66
67 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
pt_PriorityMap(PRThreadPriority pri)68 static PRIntn pt_PriorityMap(PRThreadPriority pri)
69 {
70 #ifdef NTO
71 /* This priority algorithm causes lots of problems on Neutrino
72 * for now I have just hard coded everything to run at priority 10
73 * until I can come up with a new algorithm.
74 * Jerry.Kirk@Nexwarecorp.com
75 */
76 return 10;
77 #else
78 return pt_book.minPrio +
79 pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST;
80 #endif
81 }
82 #elif defined(_PR_NICE_PRIORITY_SCHEDULING)
83 /*
84 * This functions maps higher priorities to lower nice values relative to the
85 * nice value specified in the |nice| parameter. The corresponding relative
86 * adjustments are:
87 *
88 * PR_PRIORITY_LOW +1
89 * PR_PRIORITY_NORMAL 0
90 * PR_PRIORITY_HIGH -1
91 * PR_PRIORITY_URGENT -2
92 */
pt_RelativePriority(int nice,PRThreadPriority pri)93 static int pt_RelativePriority(int nice, PRThreadPriority pri)
94 {
95 return nice + (1 - pri);
96 }
97 #endif
98
99 /*
100 ** Initialize a stack for a native pthread thread
101 */
_PR_InitializeStack(PRThreadStack * ts)102 static void _PR_InitializeStack(PRThreadStack *ts)
103 {
104 if( ts && (ts->stackTop == 0) ) {
105 ts->allocBase = (char *) &ts;
106 ts->allocSize = ts->stackSize;
107
108 /*
109 ** Setup stackTop and stackBottom values.
110 */
111 #ifdef HAVE_STACK_GROWING_UP
112 ts->stackBottom = ts->allocBase + ts->stackSize;
113 ts->stackTop = ts->allocBase;
114 #else
115 ts->stackTop = ts->allocBase;
116 ts->stackBottom = ts->allocBase - ts->stackSize;
117 #endif
118 }
119 }
120
_pt_root(void * arg)121 static void *_pt_root(void *arg)
122 {
123 PRIntn rv;
124 PRThread *thred = (PRThread*)arg;
125 PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE;
126 pthread_t id = pthread_self();
127 #ifdef _PR_NICE_PRIORITY_SCHEDULING
128 pid_t tid;
129 #endif
130
131 #ifdef _PR_NICE_PRIORITY_SCHEDULING
132 /*
133 * We need to know the kernel thread ID of each thread in order to
134 * set its nice value hence we do it here instead of at creation time.
135 */
136 tid = gettid();
137 errno = 0;
138 rv = getpriority(PRIO_PROCESS, 0);
139
140 /* If we cannot read the main thread's nice value don't try to change the
141 * new thread's nice value. */
142 if (errno == 0) {
143 setpriority(PRIO_PROCESS, tid,
144 pt_RelativePriority(rv, thred->priority));
145 }
146 #endif
147
148 /* Set up the thread stack information */
149 _PR_InitializeStack(thred->stack);
150
151 /*
152 * Set within the current thread the pointer to our object.
153 * This object will be deleted when the thread termintates,
154 * whether in a join or detached (see _PR_InitThreads()).
155 */
156 rv = pthread_setspecific(pt_book.key, thred);
157 PR_ASSERT(0 == rv);
158
159 /* make the thread visible to the rest of the runtime */
160 PR_Lock(pt_book.ml);
161 /*
162 * Both the parent thread and this new thread set thred->id.
163 * The new thread must ensure that thred->id is set before
164 * it executes its startFunc. The parent thread must ensure
165 * that thred->id is set before PR_CreateThread() returns.
166 * Both threads set thred->id while holding pt_book.ml and
167 * use thred->idSet to ensure thred->id is written only once.
168 */
169 if (!thred->idSet)
170 {
171 thred->id = id;
172 thred->idSet = PR_TRUE;
173 }
174 else
175 {
176 PR_ASSERT(pthread_equal(thred->id, id));
177 }
178
179 #ifdef _PR_NICE_PRIORITY_SCHEDULING
180 thred->tid = tid;
181 PR_NotifyAllCondVar(pt_book.cv);
182 #endif
183
184 /* If this is a GCABLE thread, set its state appropriately */
185 if (thred->suspend & PT_THREAD_SETGCABLE) {
186 thred->state |= PT_THREAD_GCABLE;
187 }
188 thred->suspend = 0;
189
190 thred->prev = pt_book.last;
191 if (pt_book.last) {
192 pt_book.last->next = thred;
193 }
194 else {
195 pt_book.first = thred;
196 }
197 thred->next = NULL;
198 pt_book.last = thred;
199 PR_Unlock(pt_book.ml);
200
201 thred->startFunc(thred->arg); /* make visible to the client */
202
203 /* unhook the thread from the runtime */
204 PR_Lock(pt_book.ml);
205 /*
206 * At this moment, PR_CreateThread() may not have set thred->id yet.
207 * It is safe for a detached thread to free thred only after
208 * PR_CreateThread() has accessed thred->id and thred->idSet.
209 */
210 if (detached)
211 {
212 while (!thred->okToDelete) {
213 PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
214 }
215 }
216
217 if (thred->state & PT_THREAD_SYSTEM) {
218 pt_book.system -= 1;
219 }
220 else if (--pt_book.user == pt_book.this_many) {
221 PR_NotifyAllCondVar(pt_book.cv);
222 }
223 if (NULL == thred->prev) {
224 pt_book.first = thred->next;
225 }
226 else {
227 thred->prev->next = thred->next;
228 }
229 if (NULL == thred->next) {
230 pt_book.last = thred->prev;
231 }
232 else {
233 thred->next->prev = thred->prev;
234 }
235 PR_Unlock(pt_book.ml);
236
237 /*
238 * Here we set the pthread's backpointer to the PRThread to NULL.
239 * Otherwise the destructor would get called eagerly as the thread
240 * returns to the pthread runtime. The joining thread would them be
241 * the proud possessor of a dangling reference. However, this is the
242 * last chance to delete the object if the thread is detached, so
243 * just let the destructor do the work.
244 */
245 if (PR_FALSE == detached)
246 {
247 /* Call TPD destructors on this thread. */
248 _PR_DestroyThreadPrivate(thred);
249 rv = pthread_setspecific(pt_book.key, NULL);
250 PR_ASSERT(0 == rv);
251 }
252
253 return NULL;
254 } /* _pt_root */
255
pt_AttachThread(void)256 static PRThread* pt_AttachThread(void)
257 {
258 PRThread *thred = NULL;
259
260 /*
261 * NSPR must have been initialized when PR_AttachThread is called.
262 * We cannot have PR_AttachThread call implicit initialization
263 * because if multiple threads call PR_AttachThread simultaneously,
264 * NSPR may be initialized more than once.
265 * We can't call any function that calls PR_GetCurrentThread()
266 * either (e.g., PR_SetError()) as that will result in infinite
267 * recursion.
268 */
269 if (!_pr_initialized) {
270 return NULL;
271 }
272
273 /* PR_NEWZAP must not call PR_GetCurrentThread() */
274 thred = PR_NEWZAP(PRThread);
275 if (NULL != thred)
276 {
277 int rv;
278
279 thred->priority = PR_PRIORITY_NORMAL;
280 thred->id = pthread_self();
281 thred->idSet = PR_TRUE;
282 #ifdef _PR_NICE_PRIORITY_SCHEDULING
283 thred->tid = gettid();
284 #endif
285 rv = pthread_setspecific(pt_book.key, thred);
286 PR_ASSERT(0 == rv);
287
288 thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN;
289 PR_Lock(pt_book.ml);
290
291 /* then put it into the list */
292 thred->prev = pt_book.last;
293 if (pt_book.last) {
294 pt_book.last->next = thred;
295 }
296 else {
297 pt_book.first = thred;
298 }
299 thred->next = NULL;
300 pt_book.last = thred;
301 PR_Unlock(pt_book.ml);
302
303 }
304 return thred; /* may be NULL */
305 } /* pt_AttachThread */
306
_PR_CreateThread(PRThreadType type,void (* start)(void * arg),void * arg,PRThreadPriority priority,PRThreadScope scope,PRThreadState state,PRUint32 stackSize,PRBool isGCAble)307 static PRThread* _PR_CreateThread(
308 PRThreadType type, void (*start)(void *arg),
309 void *arg, PRThreadPriority priority, PRThreadScope scope,
310 PRThreadState state, PRUint32 stackSize, PRBool isGCAble)
311 {
312 int rv;
313 PRThread *thred;
314 pthread_attr_t tattr;
315
316 if (!_pr_initialized) {
317 _PR_ImplicitInitialization();
318 }
319
320 if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) {
321 priority = PR_PRIORITY_FIRST;
322 }
323 else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) {
324 priority = PR_PRIORITY_LAST;
325 }
326
327 rv = _PT_PTHREAD_ATTR_INIT(&tattr);
328 PR_ASSERT(0 == rv);
329
330 if (EPERM != pt_schedpriv)
331 {
332 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
333 struct sched_param schedule;
334 #endif
335
336 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
337 rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED);
338 PR_ASSERT(0 == rv);
339 #endif
340
341 /* Use the default scheduling policy */
342
343 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
344 rv = pthread_attr_getschedparam(&tattr, &schedule);
345 PR_ASSERT(0 == rv);
346 schedule.sched_priority = pt_PriorityMap(priority);
347 rv = pthread_attr_setschedparam(&tattr, &schedule);
348 PR_ASSERT(0 == rv);
349 #ifdef NTO
350 rv = pthread_attr_setschedpolicy(&tattr, SCHED_RR); /* Round Robin */
351 PR_ASSERT(0 == rv);
352 #endif
353 #endif /* _POSIX_THREAD_PRIORITY_SCHEDULING > 0 */
354 }
355
356 rv = pthread_attr_setdetachstate(&tattr,
357 ((PR_JOINABLE_THREAD == state) ?
358 PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
359 PR_ASSERT(0 == rv);
360
361 /*
362 * If stackSize is 0, we use the default pthread stack size.
363 */
364 if (stackSize)
365 {
366 #ifdef _MD_MINIMUM_STACK_SIZE
367 if (stackSize < _MD_MINIMUM_STACK_SIZE) {
368 stackSize = _MD_MINIMUM_STACK_SIZE;
369 }
370 #endif
371 rv = pthread_attr_setstacksize(&tattr, stackSize);
372 PR_ASSERT(0 == rv);
373 }
374
375 thred = PR_NEWZAP(PRThread);
376 if (NULL == thred)
377 {
378 PR_SetError(PR_OUT_OF_MEMORY_ERROR, errno);
379 goto done;
380 }
381 else
382 {
383 pthread_t id;
384
385 thred->arg = arg;
386 thred->startFunc = start;
387 thred->priority = priority;
388 if (PR_UNJOINABLE_THREAD == state) {
389 thred->state |= PT_THREAD_DETACHED;
390 }
391
392 if (PR_LOCAL_THREAD == scope) {
393 scope = PR_GLOBAL_THREAD;
394 }
395
396 if (PR_GLOBAL_BOUND_THREAD == scope) {
397 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
398 rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
399 if (rv) {
400 /*
401 * system scope not supported
402 */
403 scope = PR_GLOBAL_THREAD;
404 /*
405 * reset scope
406 */
407 rv = pthread_attr_setscope(&tattr, PTHREAD_SCOPE_PROCESS);
408 PR_ASSERT(0 == rv);
409 }
410 #endif
411 }
412 if (PR_GLOBAL_THREAD == scope) {
413 thred->state |= PT_THREAD_GLOBAL;
414 }
415 else if (PR_GLOBAL_BOUND_THREAD == scope) {
416 thred->state |= (PT_THREAD_GLOBAL | PT_THREAD_BOUND);
417 }
418 else { /* force it global */
419 thred->state |= PT_THREAD_GLOBAL;
420 }
421 if (PR_SYSTEM_THREAD == type) {
422 thred->state |= PT_THREAD_SYSTEM;
423 }
424
425 thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0;
426
427 thred->stack = PR_NEWZAP(PRThreadStack);
428 if (thred->stack == NULL) {
429 PRIntn oserr = errno;
430 PR_Free(thred); /* all that work ... poof! */
431 PR_SetError(PR_OUT_OF_MEMORY_ERROR, oserr);
432 thred = NULL; /* and for what? */
433 goto done;
434 }
435 thred->stack->stackSize = stackSize;
436 thred->stack->thr = thred;
437
438 #ifdef PT_NO_SIGTIMEDWAIT
439 pthread_mutex_init(&thred->suspendResumeMutex,NULL);
440 pthread_cond_init(&thred->suspendResumeCV,NULL);
441 #endif
442
443 /* make the thread counted to the rest of the runtime */
444 PR_Lock(pt_book.ml);
445 if (PR_SYSTEM_THREAD == type) {
446 pt_book.system += 1;
447 }
448 else {
449 pt_book.user += 1;
450 }
451 PR_Unlock(pt_book.ml);
452
453 /*
454 * We pass a pointer to a local copy (instead of thred->id)
455 * to pthread_create() because who knows what wacky things
456 * pthread_create() may be doing to its argument.
457 */
458 rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
459
460 if (EPERM == rv)
461 {
462 /* Remember that we don't have thread scheduling privilege. */
463 pt_schedpriv = EPERM;
464 PR_LOG(_pr_thread_lm, PR_LOG_MIN,
465 ("_PR_CreateThread: no thread scheduling privilege"));
466 /* Try creating the thread again without setting priority. */
467 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
468 rv = pthread_attr_setinheritsched(&tattr, PTHREAD_INHERIT_SCHED);
469 PR_ASSERT(0 == rv);
470 #endif
471 rv = _PT_PTHREAD_CREATE(&id, tattr, _pt_root, thred);
472 }
473
474 if (0 != rv)
475 {
476 PRIntn oserr = rv;
477 PR_Lock(pt_book.ml);
478 if (thred->state & PT_THREAD_SYSTEM) {
479 pt_book.system -= 1;
480 }
481 else if (--pt_book.user == pt_book.this_many) {
482 PR_NotifyAllCondVar(pt_book.cv);
483 }
484 PR_Unlock(pt_book.ml);
485
486 PR_Free(thred->stack);
487 PR_Free(thred); /* all that work ... poof! */
488 PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, oserr);
489 thred = NULL; /* and for what? */
490 goto done;
491 }
492
493 PR_Lock(pt_book.ml);
494 /*
495 * Both the parent thread and this new thread set thred->id.
496 * The parent thread must ensure that thred->id is set before
497 * PR_CreateThread() returns. (See comments in _pt_root().)
498 */
499 if (!thred->idSet)
500 {
501 thred->id = id;
502 thred->idSet = PR_TRUE;
503 }
504 else
505 {
506 PR_ASSERT(pthread_equal(thred->id, id));
507 }
508
509 /*
510 * If the new thread is detached, tell it that PR_CreateThread() has
511 * accessed thred->id and thred->idSet so it's ok to delete thred.
512 */
513 if (PR_UNJOINABLE_THREAD == state)
514 {
515 thred->okToDelete = PR_TRUE;
516 PR_NotifyAllCondVar(pt_book.cv);
517 }
518 PR_Unlock(pt_book.ml);
519 }
520
521 done:
522 rv = _PT_PTHREAD_ATTR_DESTROY(&tattr);
523 PR_ASSERT(0 == rv);
524
525 return thred;
526 } /* _PR_CreateThread */
527
PR_CreateThread(PRThreadType type,void (* start)(void * arg),void * arg,PRThreadPriority priority,PRThreadScope scope,PRThreadState state,PRUint32 stackSize)528 PR_IMPLEMENT(PRThread*) PR_CreateThread(
529 PRThreadType type, void (*start)(void *arg), void *arg,
530 PRThreadPriority priority, PRThreadScope scope,
531 PRThreadState state, PRUint32 stackSize)
532 {
533 return _PR_CreateThread(
534 type, start, arg, priority, scope, state, stackSize, PR_FALSE);
535 } /* PR_CreateThread */
536
PR_CreateThreadGCAble(PRThreadType type,void (* start)(void * arg),void * arg,PRThreadPriority priority,PRThreadScope scope,PRThreadState state,PRUint32 stackSize)537 PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(
538 PRThreadType type, void (*start)(void *arg), void *arg,
539 PRThreadPriority priority, PRThreadScope scope,
540 PRThreadState state, PRUint32 stackSize)
541 {
542 return _PR_CreateThread(
543 type, start, arg, priority, scope, state, stackSize, PR_TRUE);
544 } /* PR_CreateThreadGCAble */
545
GetExecutionEnvironment(PRThread * thred)546 PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred)
547 {
548 return thred->environment;
549 } /* GetExecutionEnvironment */
550
SetExecutionEnvironment(PRThread * thred,void * env)551 PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env)
552 {
553 thred->environment = env;
554 } /* SetExecutionEnvironment */
555
PR_AttachThread(PRThreadType type,PRThreadPriority priority,PRThreadStack * stack)556 PR_IMPLEMENT(PRThread*) PR_AttachThread(
557 PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
558 {
559 return PR_GetCurrentThread();
560 } /* PR_AttachThread */
561
562
PR_JoinThread(PRThread * thred)563 PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred)
564 {
565 int rv = -1;
566 void *result = NULL;
567 PR_ASSERT(thred != NULL);
568
569 if ((0xafafafaf == thred->state)
570 || (PT_THREAD_DETACHED == (PT_THREAD_DETACHED & thred->state))
571 || (PT_THREAD_FOREIGN == (PT_THREAD_FOREIGN & thred->state)))
572 {
573 /*
574 * This might be a bad address, but if it isn't, the state should
575 * either be an unjoinable thread or it's already had the object
576 * deleted. However, the client that called join on a detached
577 * thread deserves all the rath I can muster....
578 */
579 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
580 PR_LogPrint(
581 "PR_JoinThread: %p not joinable | already smashed\n", thred);
582 }
583 else
584 {
585 pthread_t id = thred->id;
586 rv = pthread_join(id, &result);
587 PR_ASSERT(rv == 0 && result == NULL);
588 if (0 == rv)
589 {
590 /*
591 * PR_FALSE, because the thread already called the TPD
592 * destructors before exiting _pt_root.
593 */
594 _pt_thread_death_internal(thred, PR_FALSE);
595 }
596 else
597 {
598 PRErrorCode prerror;
599 switch (rv)
600 {
601 case EINVAL: /* not a joinable thread */
602 case ESRCH: /* no thread with given ID */
603 prerror = PR_INVALID_ARGUMENT_ERROR;
604 break;
605 case EDEADLK: /* a thread joining with itself */
606 prerror = PR_DEADLOCK_ERROR;
607 break;
608 default:
609 prerror = PR_UNKNOWN_ERROR;
610 break;
611 }
612 PR_SetError(prerror, rv);
613 }
614 }
615 return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
616 } /* PR_JoinThread */
617
PR_DetachThread(void)618 PR_IMPLEMENT(void) PR_DetachThread(void)
619 {
620 void *thred;
621 int rv;
622
623 _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
624 if (NULL == thred) {
625 return;
626 }
627 _pt_thread_death(thred);
628 rv = pthread_setspecific(pt_book.key, NULL);
629 PR_ASSERT(0 == rv);
630 } /* PR_DetachThread */
631
PR_GetCurrentThread(void)632 PR_IMPLEMENT(PRThread*) PR_GetCurrentThread(void)
633 {
634 void *thred;
635
636 if (!_pr_initialized) {
637 _PR_ImplicitInitialization();
638 }
639
640 _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
641 if (NULL == thred) {
642 thred = pt_AttachThread();
643 }
644 PR_ASSERT(NULL != thred);
645 return (PRThread*)thred;
646 } /* PR_GetCurrentThread */
647
PR_GetThreadScope(const PRThread * thred)648 PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred)
649 {
650 return (thred->state & PT_THREAD_BOUND) ?
651 PR_GLOBAL_BOUND_THREAD : PR_GLOBAL_THREAD;
652 } /* PR_GetThreadScope() */
653
PR_GetThreadType(const PRThread * thred)654 PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred)
655 {
656 return (thred->state & PT_THREAD_SYSTEM) ?
657 PR_SYSTEM_THREAD : PR_USER_THREAD;
658 }
659
PR_GetThreadState(const PRThread * thred)660 PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred)
661 {
662 return (thred->state & PT_THREAD_DETACHED) ?
663 PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD;
664 } /* PR_GetThreadState */
665
PR_GetThreadPriority(const PRThread * thred)666 PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred)
667 {
668 PR_ASSERT(thred != NULL);
669 return thred->priority;
670 } /* PR_GetThreadPriority */
671
PR_SetThreadPriority(PRThread * thred,PRThreadPriority newPri)672 PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri)
673 {
674 PRIntn rv;
675
676 PR_ASSERT(NULL != thred);
677
678 if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri) {
679 newPri = PR_PRIORITY_FIRST;
680 }
681 else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri) {
682 newPri = PR_PRIORITY_LAST;
683 }
684
685 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
686 if (EPERM != pt_schedpriv)
687 {
688 int policy;
689 struct sched_param schedule;
690
691 rv = pthread_getschedparam(thred->id, &policy, &schedule);
692 if(0 == rv) {
693 schedule.sched_priority = pt_PriorityMap(newPri);
694 rv = pthread_setschedparam(thred->id, policy, &schedule);
695 if (EPERM == rv)
696 {
697 pt_schedpriv = EPERM;
698 PR_LOG(_pr_thread_lm, PR_LOG_MIN,
699 ("PR_SetThreadPriority: no thread scheduling privilege"));
700 }
701 }
702 if (rv != 0) {
703 rv = -1;
704 }
705 }
706 #elif defined(_PR_NICE_PRIORITY_SCHEDULING)
707 PR_Lock(pt_book.ml);
708 while (thred->tid == 0) {
709 PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
710 }
711 PR_Unlock(pt_book.ml);
712
713 errno = 0;
714 rv = getpriority(PRIO_PROCESS, 0);
715
716 /* Do not proceed unless we know the main thread's nice value. */
717 if (errno == 0) {
718 rv = setpriority(PRIO_PROCESS, thred->tid,
719 pt_RelativePriority(rv, newPri));
720
721 if (rv == -1)
722 {
723 /* We don't set pt_schedpriv to EPERM in case errno == EPERM
724 * because adjusting the nice value might be permitted for certain
725 * ranges but not for others. */
726 PR_LOG(_pr_thread_lm, PR_LOG_MIN,
727 ("PR_SetThreadPriority: setpriority failed with error %d",
728 errno));
729 }
730 }
731 #else
732 (void)rv; /* rv is unused */
733 #endif
734
735 thred->priority = newPri;
736 } /* PR_SetThreadPriority */
737
PR_Interrupt(PRThread * thred)738 PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred)
739 {
740 /*
741 ** If the target thread indicates that it's waiting,
742 ** find the condition and broadcast to it. Broadcast
743 ** since we don't know which thread (if there are more
744 ** than one). This sounds risky, but clients must
745 ** test their invariants when resumed from a wait and
746 ** I don't expect very many threads to be waiting on
747 ** a single condition and I don't expect interrupt to
748 ** be used very often.
749 **
750 ** I don't know why I thought this would work. Must have
751 ** been one of those weaker momements after I'd been
752 ** smelling the vapors.
753 **
754 ** Even with the followng changes it is possible that
755 ** the pointer to the condition variable is pointing
756 ** at a bogus value. Will the unerlying code detect
757 ** that?
758 */
759 PRCondVar *cv;
760 PR_ASSERT(NULL != thred);
761 if (NULL == thred) {
762 return PR_FAILURE;
763 }
764
765 thred->state |= PT_THREAD_ABORTED;
766
767 cv = thred->waiting;
768 if ((NULL != cv) && !thred->interrupt_blocked)
769 {
770 PRIntn rv;
771 (void)PR_ATOMIC_INCREMENT(&cv->notify_pending);
772 rv = pthread_cond_broadcast(&cv->cv);
773 PR_ASSERT(0 == rv);
774 if (0 > PR_ATOMIC_DECREMENT(&cv->notify_pending)) {
775 PR_DestroyCondVar(cv);
776 }
777 }
778 return PR_SUCCESS;
779 } /* PR_Interrupt */
780
PR_ClearInterrupt(void)781 PR_IMPLEMENT(void) PR_ClearInterrupt(void)
782 {
783 PRThread *me = PR_GetCurrentThread();
784 me->state &= ~PT_THREAD_ABORTED;
785 } /* PR_ClearInterrupt */
786
PR_BlockInterrupt(void)787 PR_IMPLEMENT(void) PR_BlockInterrupt(void)
788 {
789 PRThread *me = PR_GetCurrentThread();
790 _PT_THREAD_BLOCK_INTERRUPT(me);
791 } /* PR_BlockInterrupt */
792
PR_UnblockInterrupt(void)793 PR_IMPLEMENT(void) PR_UnblockInterrupt(void)
794 {
795 PRThread *me = PR_GetCurrentThread();
796 _PT_THREAD_UNBLOCK_INTERRUPT(me);
797 } /* PR_UnblockInterrupt */
798
PR_Yield(void)799 PR_IMPLEMENT(PRStatus) PR_Yield(void)
800 {
801 static PRBool warning = PR_TRUE;
802 if (warning) warning = _PR_Obsolete(
803 "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)");
804 return PR_Sleep(PR_INTERVAL_NO_WAIT);
805 }
806
PR_Sleep(PRIntervalTime ticks)807 PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks)
808 {
809 PRStatus rv = PR_SUCCESS;
810
811 if (!_pr_initialized) {
812 _PR_ImplicitInitialization();
813 }
814
815 if (PR_INTERVAL_NO_WAIT == ticks)
816 {
817 _PT_PTHREAD_YIELD();
818 }
819 else
820 {
821 PRCondVar *cv;
822 PRIntervalTime timein;
823
824 timein = PR_IntervalNow();
825 cv = PR_NewCondVar(_pr_sleeplock);
826 PR_ASSERT(cv != NULL);
827 PR_Lock(_pr_sleeplock);
828 do
829 {
830 PRIntervalTime now = PR_IntervalNow();
831 PRIntervalTime delta = now - timein;
832 if (delta > ticks) {
833 break;
834 }
835 rv = PR_WaitCondVar(cv, ticks - delta);
836 } while (PR_SUCCESS == rv);
837 PR_Unlock(_pr_sleeplock);
838 PR_DestroyCondVar(cv);
839 }
840 return rv;
841 } /* PR_Sleep */
842
_pt_thread_death(void * arg)843 static void _pt_thread_death(void *arg)
844 {
845 void *thred;
846 int rv;
847
848 _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
849 if (NULL == thred)
850 {
851 /*
852 * Have PR_GetCurrentThread return the expected value to the
853 * destructors.
854 */
855 rv = pthread_setspecific(pt_book.key, arg);
856 PR_ASSERT(0 == rv);
857 }
858
859 /* PR_TRUE for: call destructors */
860 _pt_thread_death_internal(arg, PR_TRUE);
861
862 if (NULL == thred)
863 {
864 rv = pthread_setspecific(pt_book.key, NULL);
865 PR_ASSERT(0 == rv);
866 }
867 }
868
_pt_thread_death_internal(void * arg,PRBool callDestructors)869 static void _pt_thread_death_internal(void *arg, PRBool callDestructors)
870 {
871 PRThread *thred = (PRThread*)arg;
872
873 if (thred->state & (PT_THREAD_FOREIGN|PT_THREAD_PRIMORD))
874 {
875 PR_Lock(pt_book.ml);
876 if (NULL == thred->prev) {
877 pt_book.first = thred->next;
878 }
879 else {
880 thred->prev->next = thred->next;
881 }
882 if (NULL == thred->next) {
883 pt_book.last = thred->prev;
884 }
885 else {
886 thred->next->prev = thred->prev;
887 }
888 PR_Unlock(pt_book.ml);
889 }
890 if (callDestructors) {
891 _PR_DestroyThreadPrivate(thred);
892 }
893 PR_Free(thred->privateData);
894 if (NULL != thred->errorString) {
895 PR_Free(thred->errorString);
896 }
897 if (NULL != thred->name) {
898 PR_Free(thred->name);
899 }
900 PR_Free(thred->stack);
901 if (NULL != thred->syspoll_list) {
902 PR_Free(thred->syspoll_list);
903 }
904 #if defined(_PR_POLL_WITH_SELECT)
905 if (NULL != thred->selectfd_list) {
906 PR_Free(thred->selectfd_list);
907 }
908 #endif
909 #if defined(DEBUG)
910 memset(thred, 0xaf, sizeof(PRThread));
911 #endif /* defined(DEBUG) */
912 PR_Free(thred);
913 } /* _pt_thread_death */
914
_PR_InitThreads(PRThreadType type,PRThreadPriority priority,PRUintn maxPTDs)915 void _PR_InitThreads(
916 PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs)
917 {
918 int rv;
919 PRThread *thred;
920
921 PR_ASSERT(priority == PR_PRIORITY_NORMAL);
922
923 #ifdef _PR_NEED_PTHREAD_INIT
924 /*
925 * On BSD/OS (3.1 and 4.0), the pthread subsystem is lazily
926 * initialized, but pthread_self() fails to initialize
927 * pthreads and hence returns a null thread ID if invoked
928 * by the primordial thread before any other pthread call.
929 * So we explicitly initialize pthreads here.
930 */
931 pthread_init();
932 #endif
933
934 #if _POSIX_THREAD_PRIORITY_SCHEDULING > 0
935 #if defined(FREEBSD)
936 {
937 pthread_attr_t attr;
938 int policy;
939 /* get the min and max priorities of the default policy */
940 pthread_attr_init(&attr);
941 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
942 pthread_attr_getschedpolicy(&attr, &policy);
943 pt_book.minPrio = sched_get_priority_min(policy);
944 PR_ASSERT(-1 != pt_book.minPrio);
945 pt_book.maxPrio = sched_get_priority_max(policy);
946 PR_ASSERT(-1 != pt_book.maxPrio);
947 pthread_attr_destroy(&attr);
948 }
949 #else
950 /*
951 ** These might be function evaluations
952 */
953 pt_book.minPrio = PT_PRIO_MIN;
954 pt_book.maxPrio = PT_PRIO_MAX;
955 #endif
956 #endif
957
958 PR_ASSERT(NULL == pt_book.ml);
959 pt_book.ml = PR_NewLock();
960 PR_ASSERT(NULL != pt_book.ml);
961 pt_book.cv = PR_NewCondVar(pt_book.ml);
962 PR_ASSERT(NULL != pt_book.cv);
963 thred = PR_NEWZAP(PRThread);
964 PR_ASSERT(NULL != thred);
965 thred->arg = NULL;
966 thred->startFunc = NULL;
967 thred->priority = priority;
968 thred->id = pthread_self();
969 thred->idSet = PR_TRUE;
970 #ifdef _PR_NICE_PRIORITY_SCHEDULING
971 thred->tid = gettid();
972 #endif
973
974 thred->state = (PT_THREAD_DETACHED | PT_THREAD_PRIMORD);
975 if (PR_SYSTEM_THREAD == type)
976 {
977 thred->state |= PT_THREAD_SYSTEM;
978 pt_book.system += 1;
979 pt_book.this_many = 0;
980 }
981 else
982 {
983 pt_book.user += 1;
984 pt_book.this_many = 1;
985 }
986 thred->next = thred->prev = NULL;
987 pt_book.first = pt_book.last = thred;
988
989 thred->stack = PR_NEWZAP(PRThreadStack);
990 PR_ASSERT(thred->stack != NULL);
991 thred->stack->stackSize = 0;
992 thred->stack->thr = thred;
993 _PR_InitializeStack(thred->stack);
994
995 /*
996 * Create a key for our use to store a backpointer in the pthread
997 * to our PRThread object. This object gets deleted when the thread
998 * returns from its root in the case of a detached thread. Other
999 * threads delete the objects in Join.
1000 *
1001 * NB: The destructor logic seems to have a bug so it isn't used.
1002 * NBB: Oh really? I'm going to give it a spin - AOF 19 June 1998.
1003 * More info - the problem is that pthreads calls the destructor
1004 * eagerly as the thread returns from its root, rather than lazily
1005 * after the thread is joined. Therefore, threads that are joining
1006 * and holding PRThread references are actually holding pointers to
1007 * nothing.
1008 */
1009 rv = _PT_PTHREAD_KEY_CREATE(&pt_book.key, _pt_thread_death);
1010 if (0 != rv) {
1011 PR_Assert("0 == rv", __FILE__, __LINE__);
1012 }
1013 pt_book.keyCreated = PR_TRUE;
1014 rv = pthread_setspecific(pt_book.key, thred);
1015 PR_ASSERT(0 == rv);
1016 } /* _PR_InitThreads */
1017
1018 #ifdef __GNUC__
1019 /*
1020 * GCC supports the constructor and destructor attributes as of
1021 * version 2.5.
1022 */
1023 #if defined(DARWIN)
1024 /*
1025 * The dynamic linker on OSX doesn't execute __attribute__((destructor))
1026 * destructors in the right order wrt non-__attribute((destructor)) destructors
1027 * in other libraries. So use atexit() instead, which does.
1028 * See https://bugzilla.mozilla.org/show_bug.cgi?id=1399746#c99
1029 */
1030 static void _PR_Fini(void);
1031
1032 __attribute__ ((constructor))
_register_PR_Fini()1033 static void _register_PR_Fini() {
1034 atexit(_PR_Fini);
1035 }
1036 #else
1037 static void _PR_Fini(void) __attribute__ ((destructor));
1038 #endif
1039
1040 #elif defined(__SUNPRO_C)
1041 /*
1042 * Sun Studio compiler
1043 */
1044 #pragma fini(_PR_Fini)
1045 static void _PR_Fini(void);
1046 #elif defined(HPUX)
1047 /*
1048 * Current versions of HP C compiler define __HP_cc.
1049 * HP C compiler A.11.01.20 doesn't define __HP_cc.
1050 */
1051 #if defined(__ia64) || defined(_LP64)
1052 #pragma FINI "_PR_Fini"
1053 static void _PR_Fini(void);
1054 #else
1055 /*
1056 * Only HP-UX 10.x style initializers are supported in 32-bit links.
1057 * Need to use the +I PR_HPUX10xInit linker option.
1058 */
1059 #include <dl.h>
1060
1061 static void _PR_Fini(void);
1062
PR_HPUX10xInit(shl_t handle,int loading)1063 void PR_HPUX10xInit(shl_t handle, int loading)
1064 {
1065 /*
1066 * This function is called when a shared library is loaded as well
1067 * as when the shared library is unloaded. Note that it may not
1068 * be called when the user's program terminates.
1069 *
1070 * handle is the shl_load API handle for the shared library being
1071 * initialized.
1072 *
1073 * loading is non-zero at startup and zero at termination.
1074 */
1075 if (loading) {
1076 /* ... do some initializations ... */
1077 } else {
1078 _PR_Fini();
1079 }
1080 }
1081 #endif
1082 #elif defined(AIX)
1083 /* Need to use the -binitfini::_PR_Fini linker option. */
1084 #endif
1085
_PR_Fini(void)1086 void _PR_Fini(void)
1087 {
1088 void *thred;
1089 int rv;
1090
1091 if (!_pr_initialized) {
1092 /* Either NSPR was never successfully initialized or
1093 * PR_Cleanup has been called already. */
1094 if (pt_book.keyCreated)
1095 {
1096 rv = pthread_key_delete(pt_book.key);
1097 PR_ASSERT(0 == rv);
1098 pt_book.keyCreated = PR_FALSE;
1099 }
1100 return;
1101 }
1102
1103 _PT_PTHREAD_GETSPECIFIC(pt_book.key, thred);
1104 if (NULL != thred)
1105 {
1106 /*
1107 * PR_FALSE, because it is unsafe to call back to the
1108 * thread private data destructors at final cleanup.
1109 */
1110 _pt_thread_death_internal(thred, PR_FALSE);
1111 rv = pthread_setspecific(pt_book.key, NULL);
1112 PR_ASSERT(0 == rv);
1113 }
1114 rv = pthread_key_delete(pt_book.key);
1115 PR_ASSERT(0 == rv);
1116 pt_book.keyCreated = PR_FALSE;
1117 /* TODO: free other resources used by NSPR */
1118 /* _pr_initialized = PR_FALSE; */
1119 } /* _PR_Fini */
1120
PR_Cleanup(void)1121 PR_IMPLEMENT(PRStatus) PR_Cleanup(void)
1122 {
1123 PRThread *me = PR_GetCurrentThread();
1124 int rv;
1125 PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR"));
1126 PR_ASSERT(me->state & PT_THREAD_PRIMORD);
1127 if (me->state & PT_THREAD_PRIMORD)
1128 {
1129 PR_Lock(pt_book.ml);
1130 while (pt_book.user > pt_book.this_many) {
1131 PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT);
1132 }
1133 if (me->state & PT_THREAD_SYSTEM) {
1134 pt_book.system -= 1;
1135 }
1136 else {
1137 pt_book.user -= 1;
1138 }
1139 PR_Unlock(pt_book.ml);
1140
1141 _PR_MD_EARLY_CLEANUP();
1142
1143 _PR_CleanupMW();
1144 _PR_CleanupTime();
1145 _PR_CleanupDtoa();
1146 _PR_CleanupCallOnce();
1147 _PR_ShutdownLinker();
1148 _PR_LogCleanup();
1149 _PR_CleanupNet();
1150 /* Close all the fd's before calling _PR_CleanupIO */
1151 _PR_CleanupIO();
1152 _PR_CleanupCMon();
1153
1154 _pt_thread_death(me);
1155 rv = pthread_setspecific(pt_book.key, NULL);
1156 PR_ASSERT(0 == rv);
1157 /*
1158 * I am not sure if it's safe to delete the cv and lock here,
1159 * since there may still be "system" threads around. If this
1160 * call isn't immediately prior to exiting, then there's a
1161 * problem.
1162 */
1163 if (0 == pt_book.system)
1164 {
1165 PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL;
1166 PR_DestroyLock(pt_book.ml); pt_book.ml = NULL;
1167 }
1168 PR_DestroyLock(_pr_sleeplock);
1169 _pr_sleeplock = NULL;
1170 _PR_CleanupLayerCache();
1171 _PR_CleanupEnv();
1172 #ifdef _PR_ZONE_ALLOCATOR
1173 _PR_DestroyZones();
1174 #endif
1175 _pr_initialized = PR_FALSE;
1176 return PR_SUCCESS;
1177 }
1178 return PR_FAILURE;
1179 } /* PR_Cleanup */
1180
PR_ProcessExit(PRIntn status)1181 PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
1182 {
1183 _exit(status);
1184 }
1185
PR_GetThreadID(PRThread * thred)1186 PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thred)
1187 {
1188 return (PRUint32)thred->id; /* and I don't know what they will do with it */
1189 }
1190
1191 /*
1192 * $$$
1193 * The following two thread-to-processor affinity functions are not
1194 * yet implemented for pthreads. By the way, these functions should return
1195 * PRStatus rather than PRInt32 to indicate the success/failure status.
1196 * $$$
1197 */
1198
PR_GetThreadAffinityMask(PRThread * thread,PRUint32 * mask)1199 PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
1200 {
1201 return 0; /* not implemented */
1202 }
1203
PR_SetThreadAffinityMask(PRThread * thread,PRUint32 mask)1204 PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
1205 {
1206 return 0; /* not implemented */
1207 }
1208
1209 PR_IMPLEMENT(void)
PR_SetThreadDumpProc(PRThread * thread,PRThreadDumpProc dump,void * arg)1210 PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg)
1211 {
1212 thread->dump = dump;
1213 thread->dumpArg = arg;
1214 }
1215
1216 /*
1217 * Garbage collection support follows.
1218 */
1219
1220 /* a bogus signal mask for forcing a timed wait */
1221 /* Not so bogus in AIX as we really do a sigwait */
1222 static sigset_t sigwait_set;
1223
1224 static struct timespec onemillisec = {0, 1000000L};
1225 #ifndef PT_NO_SIGTIMEDWAIT
1226 static struct timespec hundredmillisec = {0, 100000000L};
1227 #endif
1228
1229 static void suspend_signal_handler(PRIntn sig);
1230
1231 #ifdef PT_NO_SIGTIMEDWAIT
1232 static void null_signal_handler(PRIntn sig);
1233 #endif
1234
1235 /*
1236 * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which
1237 * conflict with the use of these two signals in our GC support.
1238 * So we don't know how to support GC on Linux pthreads.
1239 */
init_pthread_gc_support(void)1240 static void init_pthread_gc_support(void)
1241 {
1242 PRIntn rv;
1243
1244 {
1245 struct sigaction sigact_usr2;
1246
1247 sigact_usr2.sa_handler = suspend_signal_handler;
1248 sigact_usr2.sa_flags = SA_RESTART;
1249 sigemptyset (&sigact_usr2.sa_mask);
1250
1251 rv = sigaction (SIGUSR2, &sigact_usr2, NULL);
1252 PR_ASSERT(0 == rv);
1253
1254 sigemptyset (&sigwait_set);
1255 #if defined(PT_NO_SIGTIMEDWAIT)
1256 sigaddset (&sigwait_set, SIGUSR1);
1257 #else
1258 sigaddset (&sigwait_set, SIGUSR2);
1259 #endif /* defined(PT_NO_SIGTIMEDWAIT) */
1260 }
1261 #if defined(PT_NO_SIGTIMEDWAIT)
1262 {
1263 struct sigaction sigact_null;
1264 sigact_null.sa_handler = null_signal_handler;
1265 sigact_null.sa_flags = SA_RESTART;
1266 sigemptyset (&sigact_null.sa_mask);
1267 rv = sigaction (SIGUSR1, &sigact_null, NULL);
1268 PR_ASSERT(0 ==rv);
1269 }
1270 #endif /* defined(PT_NO_SIGTIMEDWAIT) */
1271 }
1272
PR_SetThreadGCAble(void)1273 PR_IMPLEMENT(void) PR_SetThreadGCAble(void)
1274 {
1275 PR_Lock(pt_book.ml);
1276 PR_GetCurrentThread()->state |= PT_THREAD_GCABLE;
1277 PR_Unlock(pt_book.ml);
1278 }
1279
PR_ClearThreadGCAble(void)1280 PR_IMPLEMENT(void) PR_ClearThreadGCAble(void)
1281 {
1282 PR_Lock(pt_book.ml);
1283 PR_GetCurrentThread()->state &= (~PT_THREAD_GCABLE);
1284 PR_Unlock(pt_book.ml);
1285 }
1286
1287 #if defined(DEBUG)
1288 static PRBool suspendAllOn = PR_FALSE;
1289 #endif
1290
1291 static PRBool suspendAllSuspended = PR_FALSE;
1292
PR_EnumerateThreads(PREnumerator func,void * arg)1293 PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg)
1294 {
1295 PRIntn count = 0;
1296 PRStatus rv = PR_SUCCESS;
1297 PRThread* thred = pt_book.first;
1298
1299 #if defined(DEBUG) || defined(FORCE_PR_ASSERT)
1300 PRThread *me = PR_GetCurrentThread();
1301 #endif
1302
1303 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n"));
1304 /*
1305 * $$$
1306 * Need to suspend all threads other than me before doing this.
1307 * This is really a gross and disgusting thing to do. The only
1308 * good thing is that since all other threads are suspended, holding
1309 * the lock during a callback seems like child's play.
1310 * $$$
1311 */
1312 PR_ASSERT(suspendAllOn);
1313
1314 while (thred != NULL)
1315 {
1316 /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking
1317 * qp->next after applying the function "func". In particular, "func"
1318 * might remove the thread from the queue and put it into another one in
1319 * which case qp->next no longer points to the next entry in the original
1320 * queue.
1321 *
1322 * To get around this problem, we save qp->next in qp_next before applying
1323 * "func" and use that saved value as the next value after applying "func".
1324 */
1325 PRThread* next = thred->next;
1326
1327 if (_PT_IS_GCABLE_THREAD(thred))
1328 {
1329 PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED));
1330 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1331 ("In PR_EnumerateThreads callback thread %p thid = %X\n",
1332 thred, thred->id));
1333
1334 rv = func(thred, count++, arg);
1335 if (rv != PR_SUCCESS) {
1336 return rv;
1337 }
1338 }
1339 thred = next;
1340 }
1341 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1342 ("End PR_EnumerateThreads count = %d \n", count));
1343 return rv;
1344 } /* PR_EnumerateThreads */
1345
1346 /*
1347 * PR_SuspendAll and PR_ResumeAll are called during garbage collection. The strategy
1348 * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend.
1349 * The signal handler will record the stack pointer and will block until resumed by
1350 * the resume call. Since the signal handler is the last routine called for the
1351 * suspended thread, the stack pointer will also serve as a place where all the
1352 * registers have been saved on the stack for the previously executing routines.
1353 *
1354 * Through global variables, we also make sure that PR_Suspend and PR_Resume does not
1355 * proceed until the thread is suspended or resumed.
1356 */
1357
1358 /*
1359 * In the signal handler, we can not use condition variable notify or wait.
1360 * This does not work consistently across all pthread platforms. We also can not
1361 * use locking since that does not seem to work reliably across platforms.
1362 * Only thing we can do is yielding while testing for a global condition
1363 * to change. This does work on pthread supported platforms. We may have
1364 * to play with priortities if there are any problems detected.
1365 */
1366
1367 /*
1368 * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps
1369 * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no
1370 * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually
1371 * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java,
1372 * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal
1373 * handler as all synchronization mechanisms just break down.
1374 */
1375
1376 #if defined(PT_NO_SIGTIMEDWAIT)
null_signal_handler(PRIntn sig)1377 static void null_signal_handler(PRIntn sig)
1378 {
1379 return;
1380 }
1381 #endif
1382
suspend_signal_handler(PRIntn sig)1383 static void suspend_signal_handler(PRIntn sig)
1384 {
1385 PRThread *me = PR_GetCurrentThread();
1386
1387 PR_ASSERT(me != NULL);
1388 PR_ASSERT(_PT_IS_GCABLE_THREAD(me));
1389 PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0);
1390
1391 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1392 ("Begin suspend_signal_handler thred %p thread id = %X\n",
1393 me, me->id));
1394
1395 /*
1396 * save stack pointer
1397 */
1398 me->sp = &me;
1399
1400 /*
1401 At this point, the thread's stack pointer has been saved,
1402 And it is going to enter a wait loop until it is resumed.
1403 So it is _really_ suspended
1404 */
1405
1406 me->suspend |= PT_THREAD_SUSPENDED;
1407
1408 /*
1409 * now, block current thread
1410 */
1411 #if defined(PT_NO_SIGTIMEDWAIT)
1412 pthread_cond_signal(&me->suspendResumeCV);
1413 while (me->suspend & PT_THREAD_SUSPENDED)
1414 {
1415 #if !defined(FREEBSD) && !defined(NETBSD) && !defined(OPENBSD) \
1416 && !defined(BSDI) && !defined(UNIXWARE) \
1417 && !defined(DARWIN) && !defined(RISCOS)
1418 PRIntn rv;
1419 sigwait(&sigwait_set, &rv);
1420 #endif
1421 }
1422 me->suspend |= PT_THREAD_RESUMED;
1423 pthread_cond_signal(&me->suspendResumeCV);
1424 #else /* defined(PT_NO_SIGTIMEDWAIT) */
1425 while (me->suspend & PT_THREAD_SUSPENDED)
1426 {
1427 PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec);
1428 PR_ASSERT(-1 == rv);
1429 }
1430 me->suspend |= PT_THREAD_RESUMED;
1431 #endif
1432
1433 /*
1434 * At this point, thread has been resumed, so set a global condition.
1435 * The ResumeAll needs to know that this has really been resumed.
1436 * So the signal handler sets a flag which PR_ResumeAll will reset.
1437 * The PR_ResumeAll must reset this flag ...
1438 */
1439
1440 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1441 ("End suspend_signal_handler thred = %p tid = %X\n", me, me->id));
1442 } /* suspend_signal_handler */
1443
pt_SuspendSet(PRThread * thred)1444 static void pt_SuspendSet(PRThread *thred)
1445 {
1446 PRIntn rv;
1447
1448 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1449 ("pt_SuspendSet thred %p thread id = %X\n", thred, thred->id));
1450
1451
1452 /*
1453 * Check the thread state and signal the thread to suspend
1454 */
1455
1456 PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0);
1457
1458 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1459 ("doing pthread_kill in pt_SuspendSet thred %p tid = %X\n",
1460 thred, thred->id));
1461 rv = pthread_kill (thred->id, SIGUSR2);
1462 PR_ASSERT(0 == rv);
1463 }
1464
pt_SuspendTest(PRThread * thred)1465 static void pt_SuspendTest(PRThread *thred)
1466 {
1467 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1468 ("Begin pt_SuspendTest thred %p thread id = %X\n", thred, thred->id));
1469
1470
1471 /*
1472 * Wait for the thread to be really suspended. This happens when the
1473 * suspend signal handler stores the stack pointer and sets the state
1474 * to suspended.
1475 */
1476
1477 #if defined(PT_NO_SIGTIMEDWAIT)
1478 pthread_mutex_lock(&thred->suspendResumeMutex);
1479 while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
1480 {
1481 pthread_cond_timedwait(
1482 &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
1483 }
1484 pthread_mutex_unlock(&thred->suspendResumeMutex);
1485 #else
1486 while ((thred->suspend & PT_THREAD_SUSPENDED) == 0)
1487 {
1488 PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
1489 PR_ASSERT(-1 == rv);
1490 }
1491 #endif
1492
1493 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1494 ("End pt_SuspendTest thred %p tid %X\n", thred, thred->id));
1495 } /* pt_SuspendTest */
1496
pt_ResumeSet(PRThread * thred)1497 static void pt_ResumeSet(PRThread *thred)
1498 {
1499 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1500 ("pt_ResumeSet thred %p thread id = %X\n", thred, thred->id));
1501
1502 /*
1503 * Clear the global state and set the thread state so that it will
1504 * continue past yield loop in the suspend signal handler
1505 */
1506
1507 PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED);
1508
1509
1510 thred->suspend &= ~PT_THREAD_SUSPENDED;
1511
1512 #if defined(PT_NO_SIGTIMEDWAIT)
1513 pthread_kill(thred->id, SIGUSR1);
1514 #endif
1515
1516 } /* pt_ResumeSet */
1517
pt_ResumeTest(PRThread * thred)1518 static void pt_ResumeTest(PRThread *thred)
1519 {
1520 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1521 ("Begin pt_ResumeTest thred %p thread id = %X\n", thred, thred->id));
1522
1523 /*
1524 * Wait for the threads resume state to change
1525 * to indicate it is really resumed
1526 */
1527 #if defined(PT_NO_SIGTIMEDWAIT)
1528 pthread_mutex_lock(&thred->suspendResumeMutex);
1529 while ((thred->suspend & PT_THREAD_RESUMED) == 0)
1530 {
1531 pthread_cond_timedwait(
1532 &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec);
1533 }
1534 pthread_mutex_unlock(&thred->suspendResumeMutex);
1535 #else
1536 while ((thred->suspend & PT_THREAD_RESUMED) == 0) {
1537 PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec);
1538 PR_ASSERT(-1 == rv);
1539 }
1540 #endif
1541
1542 thred->suspend &= ~PT_THREAD_RESUMED;
1543
1544 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, (
1545 "End pt_ResumeTest thred %p tid %X\n", thred, thred->id));
1546 } /* pt_ResumeTest */
1547
1548 static pthread_once_t pt_gc_support_control = PTHREAD_ONCE_INIT;
1549
PR_SuspendAll(void)1550 PR_IMPLEMENT(void) PR_SuspendAll(void)
1551 {
1552 #ifdef DEBUG
1553 PRIntervalTime stime, etime;
1554 #endif
1555 PRThread* thred = pt_book.first;
1556 PRThread *me = PR_GetCurrentThread();
1557 int rv;
1558
1559 rv = pthread_once(&pt_gc_support_control, init_pthread_gc_support);
1560 PR_ASSERT(0 == rv);
1561 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n"));
1562 /*
1563 * Stop all threads which are marked GC able.
1564 */
1565 PR_Lock(pt_book.ml);
1566 #ifdef DEBUG
1567 suspendAllOn = PR_TRUE;
1568 stime = PR_IntervalNow();
1569 #endif
1570 while (thred != NULL)
1571 {
1572 if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1573 pt_SuspendSet(thred);
1574 }
1575 thred = thred->next;
1576 }
1577
1578 /* Wait till they are really suspended */
1579 thred = pt_book.first;
1580 while (thred != NULL)
1581 {
1582 if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1583 pt_SuspendTest(thred);
1584 }
1585 thred = thred->next;
1586 }
1587
1588 suspendAllSuspended = PR_TRUE;
1589
1590 #ifdef DEBUG
1591 etime = PR_IntervalNow();
1592 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\
1593 ("End PR_SuspendAll (time %dms)\n",
1594 PR_IntervalToMilliseconds(etime - stime)));
1595 #endif
1596 } /* PR_SuspendAll */
1597
PR_ResumeAll(void)1598 PR_IMPLEMENT(void) PR_ResumeAll(void)
1599 {
1600 #ifdef DEBUG
1601 PRIntervalTime stime, etime;
1602 #endif
1603 PRThread* thred = pt_book.first;
1604 PRThread *me = PR_GetCurrentThread();
1605 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n"));
1606 /*
1607 * Resume all previously suspended GC able threads.
1608 */
1609 suspendAllSuspended = PR_FALSE;
1610 #ifdef DEBUG
1611 stime = PR_IntervalNow();
1612 #endif
1613
1614 while (thred != NULL)
1615 {
1616 if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1617 pt_ResumeSet(thred);
1618 }
1619 thred = thred->next;
1620 }
1621
1622 thred = pt_book.first;
1623 while (thred != NULL)
1624 {
1625 if ((thred != me) && _PT_IS_GCABLE_THREAD(thred)) {
1626 pt_ResumeTest(thred);
1627 }
1628 thred = thred->next;
1629 }
1630
1631 PR_Unlock(pt_book.ml);
1632 #ifdef DEBUG
1633 suspendAllOn = PR_FALSE;
1634 etime = PR_IntervalNow();
1635 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1636 ("End PR_ResumeAll (time %dms)\n",
1637 PR_IntervalToMilliseconds(etime - stime)));
1638 #endif
1639 } /* PR_ResumeAll */
1640
1641 /* Return the stack pointer for the given thread- used by the GC */
PR_GetSP(PRThread * thred)1642 PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred)
1643 {
1644 PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,
1645 ("in PR_GetSP thred %p thid = %X, sp = %p\n",
1646 thred, thred->id, thred->sp));
1647 return thred->sp;
1648 } /* PR_GetSP */
1649
PR_SetCurrentThreadName(const char * name)1650 PR_IMPLEMENT(PRStatus) PR_SetCurrentThreadName(const char *name)
1651 {
1652 PRThread *thread;
1653 size_t nameLen;
1654 int result = 0;
1655
1656 if (!name) {
1657 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1658 return PR_FAILURE;
1659 }
1660
1661 thread = PR_GetCurrentThread();
1662 if (!thread) {
1663 return PR_FAILURE;
1664 }
1665
1666 PR_Free(thread->name);
1667 nameLen = strlen(name);
1668 thread->name = (char *)PR_Malloc(nameLen + 1);
1669 if (!thread->name) {
1670 return PR_FAILURE;
1671 }
1672 memcpy(thread->name, name, nameLen + 1);
1673
1674 #if defined(OPENBSD) || defined(FREEBSD) || defined(DRAGONFLY)
1675 pthread_set_name_np(thread->id, name);
1676 #elif defined(ANDROID)
1677 prctl(PR_SET_NAME, (unsigned long)(name));
1678 #elif defined(NETBSD)
1679 result = pthread_setname_np(thread->id, "%s", (void *)name);
1680 #else /* not BSD */
1681 /*
1682 * On OSX, pthread_setname_np is only available in 10.6 or later, so test
1683 * for it at runtime. It also may not be available on all linux distros.
1684 */
1685 #if defined(DARWIN)
1686 int (*dynamic_pthread_setname_np)(const char*);
1687 #else
1688 int (*dynamic_pthread_setname_np)(pthread_t, const char*);
1689 #endif
1690
1691 *(void**)(&dynamic_pthread_setname_np) =
1692 dlsym(RTLD_DEFAULT, "pthread_setname_np");
1693 if (!dynamic_pthread_setname_np) {
1694 return PR_SUCCESS;
1695 }
1696
1697 #if defined(DARWIN)
1698 /* Mac OS X has a length limit of 63 characters, but there is no API
1699 * exposing it.
1700 */
1701 #define SETNAME_LENGTH_CONSTRAINT 63
1702 #else
1703 /*
1704 * The 15-character name length limit is an experimentally determined
1705 * length of a null-terminated string that most linux distros accept
1706 * as an argument to pthread_setname_np. Otherwise the E2BIG
1707 * error is returned by the function.
1708 */
1709 #define SETNAME_LENGTH_CONSTRAINT 15
1710 #endif
1711 #define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1)
1712 #define SETNAME_FRAGMENT2_LENGTH \
1713 (SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1)
1714 char name_dup[SETNAME_LENGTH_CONSTRAINT + 1];
1715 if (nameLen > SETNAME_LENGTH_CONSTRAINT) {
1716 memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH);
1717 name_dup[SETNAME_FRAGMENT1_LENGTH] = '~';
1718 /* Note that this also copies the null terminator. */
1719 memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1,
1720 name + nameLen - SETNAME_FRAGMENT2_LENGTH,
1721 SETNAME_FRAGMENT2_LENGTH + 1);
1722 name = name_dup;
1723 }
1724
1725 #if defined(DARWIN)
1726 result = dynamic_pthread_setname_np(name);
1727 #else
1728 result = dynamic_pthread_setname_np(thread->id, name);
1729 #endif
1730 #endif /* not BSD */
1731
1732 if (result) {
1733 PR_SetError(PR_UNKNOWN_ERROR, result);
1734 return PR_FAILURE;
1735 }
1736 return PR_SUCCESS;
1737 }
1738
PR_GetThreadName(const PRThread * thread)1739 PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread)
1740 {
1741 if (!thread) {
1742 return NULL;
1743 }
1744 return thread->name;
1745 }
1746
1747 #endif /* defined(_PR_PTHREADS) */
1748
1749 /* ptthread.c */
1750