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