2
* Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
3
* Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
4
* Copyright (c) 1999-2003 by Hewlett-Packard Company. All rights reserved.
6
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
9
* Permission is hereby granted to use or copy this program
10
* for any purpose, provided the above notices are retained on all copies.
11
* Permission to modify the code and to distribute modified code is granted,
12
* provided the above notices are retained, and a notice that the code was
13
* modified is included with the above copyright notice.
16
* Support code for Irix (>=6.2) Pthreads and for AIX pthreads.
17
* This relies on properties
18
* not guaranteed by the Pthread standard. It may or may not be portable
19
* to other implementations.
21
* Note that there is a lot of code duplication between this file and
22
* (pthread_support.c, pthread_stop_world.c). They should be merged.
23
* Pthread_support.c should be directly usable.
25
* Please avoid adding new ports here; use the generic pthread support
29
# include "private/gc_priv.h"
31
# if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)
35
# include <semaphore.h>
39
# include <sys/mman.h>
40
# include <sys/time.h>
43
#undef pthread_sigmask
46
#if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)
47
#define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
53
void GC_print_sig_mask()
58
if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
59
ABORT("pthread_sigmask");
60
GC_printf0("Blocked: ");
61
for (i = 1; i <= MAXSIG; i++) {
62
if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
68
/* We use the allocation lock to protect thread-related data structures. */
70
/* The set of all known threads. We intercept thread creation and */
71
/* joins. We never actually create detached threads. We allocate all */
72
/* new thread stacks ourselves. These allow us to maintain this */
74
/* Protected by GC_thr_lock. */
75
/* Some of this should be declared volatile, but that's incosnsistent */
76
/* with some library routine declarations. */
77
typedef struct GC_Thread_Rep {
78
struct GC_Thread_Rep * next; /* More recently allocated threads */
79
/* with a given pthread id come */
80
/* first. (All but the first are */
81
/* guaranteed to be dead, but we may */
82
/* not yet have registered the join.) */
85
# define NOT_STOPPED 0
86
# define PLEASE_STOP 1
89
# define FINISHED 1 /* Thread has exited. */
90
# define DETACHED 2 /* Thread is intended to be detached. */
91
ptr_t stack_cold; /* cold end of the stack */
92
ptr_t stack_hot; /* Valid only when stopped. */
93
/* But must be within stack region at */
95
void * status; /* Used only to avoid premature */
96
/* reclamation of any data it might */
100
GC_thread GC_lookup_thread(pthread_t id);
103
* The only way to suspend threads given the pthread interface is to send
104
* signals. Unfortunately, this means we have to reserve
105
* a signal, and intercept client calls to change the signal mask.
108
# if defined(GC_AIX_THREADS)
109
# define SIG_SUSPEND SIGUSR1
111
# define SIG_SUSPEND (SIGRTMIN + 6)
115
pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
116
/* Number of threads stopped so far */
117
pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
118
pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
120
void GC_suspend_handler(int sig)
128
if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
129
me = GC_lookup_thread(pthread_self());
130
/* The lookup here is safe, since I'm doing this on behalf */
131
/* of a thread which holds the allocation lock in order */
132
/* to stop the world. Thus concurrent modification of the */
133
/* data structure is impossible. */
134
if (PLEASE_STOP != me -> stop) {
135
/* Misdirected signal. */
136
pthread_mutex_unlock(&GC_suspend_lock);
139
pthread_mutex_lock(&GC_suspend_lock);
140
me -> stack_hot = (ptr_t)(&dummy);
141
me -> stop = STOPPED;
142
pthread_cond_signal(&GC_suspend_ack_cv);
143
pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
144
pthread_mutex_unlock(&GC_suspend_lock);
145
/* GC_printf1("Continuing 0x%x\n", pthread_self()); */
149
GC_bool GC_thr_initialized = FALSE;
152
# define THREAD_TABLE_SZ 128 /* Must be power of 2 */
153
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
155
void GC_push_thread_structures GC_PROTO((void))
157
GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
160
/* Add a thread to GC_threads. We assume it wasn't already there. */
161
/* Caller holds allocation lock. */
162
GC_thread GC_new_thread(pthread_t id)
164
int hv = ((word)id) % THREAD_TABLE_SZ;
166
static struct GC_Thread_Rep first_thread;
167
static GC_bool first_thread_used = FALSE;
169
GC_ASSERT(I_HOLD_LOCK());
170
if (!first_thread_used) {
171
result = &first_thread;
172
first_thread_used = TRUE;
173
/* Dont acquire allocation lock, since we may already hold it. */
175
result = (struct GC_Thread_Rep *)
176
GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
178
if (result == 0) return(0);
180
result -> next = GC_threads[hv];
181
GC_threads[hv] = result;
182
/* result -> flags = 0; */
183
/* result -> stop = 0; */
187
/* Delete a thread from GC_threads. We assume it is there. */
188
/* (The code intentionally traps if it wasn't.) */
189
/* Caller holds allocation lock. */
190
/* We explicitly pass in the GC_thread we're looking for, since */
191
/* if a thread has been joined, but we have not yet */
192
/* been notified, then there may be more than one thread */
193
/* in the table with the same pthread id. */
194
/* This is OK, but we need a way to delete a specific one. */
195
void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
197
int hv = ((word)id) % THREAD_TABLE_SZ;
198
register GC_thread p = GC_threads[hv];
199
register GC_thread prev = 0;
201
GC_ASSERT(I_HOLD_LOCK());
207
GC_threads[hv] = p -> next;
209
prev -> next = p -> next;
213
/* Return a GC_thread corresponding to a given thread_t. */
214
/* Returns 0 if it's not there. */
215
/* Caller holds allocation lock or otherwise inhibits */
217
/* If there is more than one thread with the given id we */
218
/* return the most recent one. */
219
GC_thread GC_lookup_thread(pthread_t id)
221
int hv = ((word)id) % THREAD_TABLE_SZ;
222
register GC_thread p = GC_threads[hv];
224
/* I either hold the lock, or i'm being called from the stop-the-world
226
#if defined(GC_AIX_THREADS)
227
GC_ASSERT(I_HOLD_LOCK()); /* no stop-the-world handler needed on AIX */
229
while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
233
#if defined(GC_AIX_THREADS)
236
pthread_t my_thread = pthread_self();
238
register GC_thread p;
240
struct timespec timeout;
242
GC_ASSERT(I_HOLD_LOCK());
243
for (i = 0; i < THREAD_TABLE_SZ; i++) {
244
for (p = GC_threads[i]; p != 0; p = p -> next) {
245
if (p -> id != my_thread) {
246
pthread_suspend_np(p->id);
250
/* GC_printf1("World stopped 0x%x\n", pthread_self()); */
253
void GC_start_world()
257
pthread_t my_thread = pthread_self();
259
/* GC_printf0("World starting\n"); */
260
GC_ASSERT(I_HOLD_LOCK());
261
for (i = 0; i < THREAD_TABLE_SZ; i++) {
262
for (p = GC_threads[i]; p != 0; p = p -> next) {
263
if (p -> id != my_thread) {
264
pthread_continue_np(p->id);
270
#else /* GC_AIX_THREADS */
272
/* Caller holds allocation lock. */
275
pthread_t my_thread = pthread_self();
277
register GC_thread p;
279
struct timespec timeout;
281
GC_ASSERT(I_HOLD_LOCK());
282
for (i = 0; i < THREAD_TABLE_SZ; i++) {
283
for (p = GC_threads[i]; p != 0; p = p -> next) {
284
if (p -> id != my_thread) {
285
if (p -> flags & FINISHED) {
289
p -> stop = PLEASE_STOP;
290
result = pthread_kill(p -> id, SIG_SUSPEND);
291
/* GC_printf1("Sent signal to 0x%x\n", p -> id); */
294
/* Not really there anymore. Possible? */
300
ABORT("pthread_kill failed");
305
pthread_mutex_lock(&GC_suspend_lock);
306
for (i = 0; i < THREAD_TABLE_SZ; i++) {
307
for (p = GC_threads[i]; p != 0; p = p -> next) {
308
while (p -> id != my_thread && p -> stop != STOPPED) {
309
clock_gettime(CLOCK_REALTIME, &timeout);
310
timeout.tv_nsec += 50000000; /* 50 msecs */
311
if (timeout.tv_nsec >= 1000000000) {
312
timeout.tv_nsec -= 1000000000;
315
result = pthread_cond_timedwait(&GC_suspend_ack_cv,
318
if (result == ETIMEDOUT) {
319
/* Signal was lost or misdirected. Try again. */
320
/* Duplicate signals should be benign. */
321
result = pthread_kill(p -> id, SIG_SUSPEND);
326
pthread_mutex_unlock(&GC_suspend_lock);
327
/* GC_printf1("World stopped 0x%x\n", pthread_self()); */
330
/* Caller holds allocation lock. */
331
void GC_start_world()
336
/* GC_printf0("World starting\n"); */
337
GC_ASSERT(I_HOLD_LOCK());
338
for (i = 0; i < THREAD_TABLE_SZ; i++) {
339
for (p = GC_threads[i]; p != 0; p = p -> next) {
340
p -> stop = NOT_STOPPED;
343
pthread_mutex_lock(&GC_suspend_lock);
344
/* All other threads are at pthread_cond_wait in signal handler. */
345
/* Otherwise we couldn't have acquired the lock. */
346
pthread_mutex_unlock(&GC_suspend_lock);
347
pthread_cond_broadcast(&GC_continue_cv);
350
#endif /* GC_AIX_THREADS */
353
/* We hold allocation lock. Should do exactly the right thing if the */
354
/* world is stopped. Should not fail if it isn't. */
355
void GC_push_all_stacks()
358
register GC_thread p;
359
register ptr_t hot, cold;
360
pthread_t me = pthread_self();
362
/* GC_init() should have been called before GC_push_all_stacks is
363
* invoked, and GC_init calls GC_thr_init(), which sets
364
* GC_thr_initialized. */
365
GC_ASSERT(GC_thr_initialized);
367
/* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
368
GC_ASSERT(I_HOLD_LOCK());
369
for (i = 0; i < THREAD_TABLE_SZ; i++) {
370
for (p = GC_threads[i]; p != 0; p = p -> next) {
371
if (p -> flags & FINISHED) continue;
372
cold = p->stack_cold;
373
if (!cold) cold=GC_stackbottom; /* 0 indicates 'original stack' */
374
if (pthread_equal(p -> id, me)) {
375
hot = GC_approx_sp();
377
# ifdef GC_AIX_THREADS
378
/* AIX doesn't use signals to suspend, so we need to get an */
379
/* accurate hot stack pointer. */
380
/* See http://publib16.boulder.ibm.com/pseries/en_US/libs/basetrf1/pthread_getthrds_np.htm */
381
pthread_t id = p -> id;
382
struct __pthrdsinfo pinfo;
384
int val = sizeof(regbuf);
385
int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo,
386
sizeof(pinfo), regbuf, &val);
388
printf("ERROR: pthread_getthrds_np() failed in GC\n");
391
/* according to the AIX ABI,
392
"the lowest possible valid stack address is 288 bytes (144 + 144)
393
less than the current value of the stack pointer. Functions may
394
use this stack space as volatile storage which is not preserved
395
across function calls."
396
ftp://ftp.penguinppc64.org/pub/people/amodra/PPC-elf64abi.txt.gz
398
hot = (ptr_t)(unsigned long)pinfo.__pi_ustk-288;
399
cold = (ptr_t)pinfo.__pi_stackend; /* more precise */
400
/* push the registers too, because they won't be on stack */
401
GC_push_all_eager((ptr_t)&pinfo.__pi_context,
402
(ptr_t)((&pinfo.__pi_context)+1));
403
GC_push_all_eager((ptr_t)regbuf, ((ptr_t)regbuf)+val);
405
hot = p -> stack_hot;
408
# ifdef STACK_GROWS_UP
409
GC_push_all_stack(cold, hot);
411
/* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */
412
GC_push_all_stack(hot, cold);
419
/* We hold the allocation lock. */
423
struct sigaction act;
425
if (GC_thr_initialized) return;
426
GC_ASSERT(I_HOLD_LOCK());
427
GC_thr_initialized = TRUE;
428
#ifndef GC_AIX_THREADS
429
(void) sigaction(SIG_SUSPEND, 0, &act);
430
if (act.sa_handler != SIG_DFL)
431
ABORT("Previously installed SIG_SUSPEND handler");
432
/* Install handler. */
433
act.sa_handler = GC_suspend_handler;
434
act.sa_flags = SA_RESTART;
435
(void) sigemptyset(&act.sa_mask);
436
if (0 != sigaction(SIG_SUSPEND, &act, 0))
437
ABORT("Failed to install SIG_SUSPEND handler");
439
/* Add the initial thread, so we can stop it. */
440
t = GC_new_thread(pthread_self());
441
/* use '0' to indicate GC_stackbottom, since GC_init() has not
442
* completed by the time we are called (from GC_init_inner()) */
443
t -> stack_cold = 0; /* the original stack. */
444
t -> stack_hot = (ptr_t)(&t);
445
t -> flags = DETACHED;
448
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
452
#ifdef GC_AIX_THREADS
453
return(pthread_sigmask(how, set, oset));
456
if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
458
sigdelset(&fudged_set, SIG_SUSPEND);
461
return(pthread_sigmask(how, set, oset));
465
void *(*start_routine)(void *);
468
pthread_mutex_t registeredlock;
469
pthread_cond_t registered;
470
int volatile registereddone;
473
void GC_thread_exit_proc(void *arg)
478
me = GC_lookup_thread(pthread_self());
479
me -> flags |= FINISHED;
480
/* reclaim DETACHED thread right away; otherwise wait until join() */
481
if (me -> flags & DETACHED) {
482
GC_delete_gc_thread(pthread_self(), me);
487
int GC_pthread_join(pthread_t thread, void **retval)
490
GC_thread thread_gc_id;
493
thread_gc_id = GC_lookup_thread(thread);
494
/* This is guaranteed to be the intended one, since the thread id */
495
/* cant have been recycled by pthreads. */
497
GC_ASSERT(!(thread_gc_id->flags & DETACHED));
498
result = pthread_join(thread, retval);
499
/* Some versions of the Irix pthreads library can erroneously */
500
/* return EINTR when the call succeeds. */
501
if (EINTR == result) result = 0;
502
GC_ASSERT(thread_gc_id->flags & FINISHED);
504
/* Here the pthread thread id may have been recycled. */
505
GC_delete_gc_thread(thread, thread_gc_id);
510
void * GC_start_routine(void * arg)
513
struct start_info * si = arg;
516
pthread_t my_pthread;
517
void *(*start)(void *);
520
my_pthread = pthread_self();
521
/* If a GC occurs before the thread is registered, that GC will */
522
/* ignore this thread. That's fine, since it will block trying to */
523
/* acquire the allocation lock, and won't yet hold interesting */
526
/* We register the thread here instead of in the parent, so that */
527
/* we don't need to hold the allocation lock during pthread_create. */
528
/* Holding the allocation lock there would make REDIRECT_MALLOC */
529
/* impossible. It probably still doesn't work, but we're a little */
531
/* This unfortunately means that we have to be careful the parent */
532
/* doesn't try to do a pthread_join before we're registered. */
533
me = GC_new_thread(my_pthread);
534
me -> flags = si -> flags;
535
me -> stack_cold = (ptr_t) &dummy; /* this now the 'start of stack' */
536
me -> stack_hot = me->stack_cold;/* this field should always be sensible */
538
start = si -> start_routine;
539
start_arg = si -> arg;
541
pthread_mutex_lock(&(si->registeredlock));
542
si->registereddone = 1;
543
pthread_cond_signal(&(si->registered));
544
pthread_mutex_unlock(&(si->registeredlock));
545
/* si went away as soon as we did this unlock */
547
pthread_cleanup_push(GC_thread_exit_proc, 0);
548
result = (*start)(start_arg);
549
me -> status = result;
550
pthread_cleanup_pop(1);
551
/* This involves acquiring the lock, ensuring that we can't exit */
552
/* while a collection that thinks we're alive is trying to stop */
558
GC_pthread_create(pthread_t *new_thread,
559
const pthread_attr_t *attr,
560
void *(*start_routine)(void *), void *arg)
566
struct start_info * si;
567
/* This is otherwise saved only in an area mmapped by the thread */
568
/* library, which isn't visible to the collector. */
571
/* GC_INTERNAL_MALLOC implicitly calls GC_init() if required */
572
si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info),
574
GC_ASSERT(GC_thr_initialized); /* initialized by GC_init() */
576
if (0 == si) return(ENOMEM);
577
pthread_mutex_init(&(si->registeredlock), NULL);
578
pthread_cond_init(&(si->registered),NULL);
579
pthread_mutex_lock(&(si->registeredlock));
580
si -> start_routine = start_routine;
583
pthread_attr_getdetachstate(attr, &detachstate);
584
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
585
si -> flags = my_flags;
586
result = pthread_create(new_thread, attr, GC_start_routine, si);
588
/* Wait until child has been added to the thread table. */
589
/* This also ensures that we hold onto si until the child is done */
590
/* with it. Thus it doesn't matter whether it is otherwise */
591
/* visible to the collector. */
594
si->registereddone = 0;
595
while (!si->registereddone)
596
pthread_cond_wait(&(si->registered), &(si->registeredlock));
598
pthread_mutex_unlock(&(si->registeredlock));
600
pthread_cond_destroy(&(si->registered));
601
pthread_mutex_destroy(&(si->registeredlock));
603
GC_INTERNAL_FREE(si);
609
/* For now we use the pthreads locking primitives on HP/UX */
611
VOLATILE GC_bool GC_collecting = 0; /* A hint that we're in the collector and */
612
/* holding the allocation lock for an */
613
/* extended period. */
615
/* Reasonably fast spin locks. Basically the same implementation */
616
/* as STL alloc.h. */
618
#define SLEEP_THRESHOLD 3
620
volatile unsigned int GC_allocate_lock = 0;
621
#define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
622
#define GC_LOCK_TAKEN GC_allocate_lock
626
# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
627
# define high_spin_max 1000 /* spin cycles for multiprocessor */
628
static unsigned spin_max = low_spin_max;
629
unsigned my_spin_max;
630
static unsigned last_spins = 0;
631
unsigned my_last_spins;
632
volatile unsigned junk;
633
# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
640
my_spin_max = spin_max;
641
my_last_spins = last_spins;
642
for (i = 0; i < my_spin_max; i++) {
643
if (GC_collecting) goto yield;
644
if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
651
* Spinning worked. Thus we're probably not being scheduled
652
* against the other process with which we were contending.
653
* Thus it makes sense to spin longer the next time.
656
spin_max = high_spin_max;
660
/* We are probably being scheduled against the other process. Sleep. */
661
spin_max = low_spin_max;
667
if (i < SLEEP_THRESHOLD) {
673
/* Don't wait for more than about 60msecs, even */
674
/* under extreme contention. */
682
# else /* !GC_IRIX_THREADS && !GC_AIX_THREADS */
685
int GC_no_Irix_threads;
688
# endif /* IRIX_THREADS */