Source code

Revision control

Other Tools

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
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
*/
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
*/
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
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
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
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
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
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
546
PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred)
547
{
548
return thred->environment;
549
} /* GetExecutionEnvironment */
550
551
PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env)
552
{
553
thred->environment = env;
554
} /* SetExecutionEnvironment */
555
556
PR_IMPLEMENT(PRThread*) PR_AttachThread(
557
PRThreadType type, PRThreadPriority priority, PRThreadStack *stack)
558
{
559
return PR_GetCurrentThread();
560
} /* PR_AttachThread */
561
562
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
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
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
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
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
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
666
PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred)
667
{
668
PR_ASSERT(thred != NULL);
669
return thred->priority;
670
} /* PR_GetThreadPriority */
671
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
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
781
PR_IMPLEMENT(void) PR_ClearInterrupt(void)
782
{
783
PRThread *me = PR_GetCurrentThread();
784
me->state &= ~PT_THREAD_ABORTED;
785
} /* PR_ClearInterrupt */
786
787
PR_IMPLEMENT(void) PR_BlockInterrupt(void)
788
{
789
PRThread *me = PR_GetCurrentThread();
790
_PT_THREAD_BLOCK_INTERRUPT(me);
791
} /* PR_BlockInterrupt */
792
793
PR_IMPLEMENT(void) PR_UnblockInterrupt(void)
794
{
795
PRThread *me = PR_GetCurrentThread();
796
_PT_THREAD_UNBLOCK_INTERRUPT(me);
797
} /* PR_UnblockInterrupt */
798
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
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
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
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
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.
1029
*/
1030
static void _PR_Fini(void);
1031
1032
__attribute__ ((constructor))
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
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
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
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
1181
PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status)
1182
{
1183
_exit(status);
1184
}
1185
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
1199
PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask)
1200
{
1201
return 0; /* not implemented */
1202
}
1203
1204
PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask )
1205
{
1206
return 0; /* not implemented */
1207
}
1208
1209
PR_IMPLEMENT(void)
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
*/
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
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
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
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)
1377
static void null_signal_handler(PRIntn sig)
1378
{
1379
return;
1380
}
1381
#endif
1382
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
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
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
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
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
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
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 */
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
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
/*
1698
* The 15-character name length limit is an experimentally determined
1699
* length of a null-terminated string that most linux distros and OS X
1700
* accept as an argument to pthread_setname_np. Otherwise the E2BIG
1701
* error is returned by the function.
1702
*/
1703
#define SETNAME_LENGTH_CONSTRAINT 15
1704
#define SETNAME_FRAGMENT1_LENGTH (SETNAME_LENGTH_CONSTRAINT >> 1)
1705
#define SETNAME_FRAGMENT2_LENGTH \
1706
(SETNAME_LENGTH_CONSTRAINT - SETNAME_FRAGMENT1_LENGTH - 1)
1707
char name_dup[SETNAME_LENGTH_CONSTRAINT + 1];
1708
if (nameLen > SETNAME_LENGTH_CONSTRAINT) {
1709
memcpy(name_dup, name, SETNAME_FRAGMENT1_LENGTH);
1710
name_dup[SETNAME_FRAGMENT1_LENGTH] = '~';
1711
/* Note that this also copies the null terminator. */
1712
memcpy(name_dup + SETNAME_FRAGMENT1_LENGTH + 1,
1713
name + nameLen - SETNAME_FRAGMENT2_LENGTH,
1714
SETNAME_FRAGMENT2_LENGTH + 1);
1715
name = name_dup;
1716
}
1717
1718
#if defined(DARWIN)
1719
result = dynamic_pthread_setname_np(name);
1720
#else
1721
result = dynamic_pthread_setname_np(thread->id, name);
1722
#endif
1723
#endif /* not BSD */
1724
1725
if (result) {
1726
PR_SetError(PR_UNKNOWN_ERROR, result);
1727
return PR_FAILURE;
1728
}
1729
return PR_SUCCESS;
1730
}
1731
1732
PR_IMPLEMENT(const char *) PR_GetThreadName(const PRThread *thread)
1733
{
1734
if (!thread) {
1735
return NULL;
1736
}
1737
return thread->name;
1738
}
1739
1740
#endif /* defined(_PR_PTHREADS) */
1741
1742
/* ptthread.c */