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 # if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)
31 # include "private/gc_priv.h"
34 # include <semaphore.h>
38 # include <sys/mman.h>
39 # include <sys/time.h>
42 #undef pthread_sigmask
45 #if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)
46 #define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
52 void GC_print_sig_mask()
57 if (pthread_sigmask(SIG_BLOCK
, NULL
, &blocked
) != 0)
58 ABORT("pthread_sigmask");
59 GC_printf0("Blocked: ");
60 for (i
= 1; i
<= MAXSIG
; i
++) {
61 if (sigismember(&blocked
, i
)) { GC_printf1("%ld ",(long) i
); }
67 /* We use the allocation lock to protect thread-related data structures. */
69 /* The set of all known threads. We intercept thread creation and */
70 /* joins. We never actually create detached threads. We allocate all */
71 /* new thread stacks ourselves. These allow us to maintain this */
73 /* Protected by GC_thr_lock. */
74 /* Some of this should be declared volatile, but that's incosnsistent */
75 /* with some library routine declarations. */
76 typedef struct GC_Thread_Rep
{
77 struct GC_Thread_Rep
* next
; /* More recently allocated threads */
78 /* with a given pthread id come */
79 /* first. (All but the first are */
80 /* guaranteed to be dead, but we may */
81 /* not yet have registered the join.) */
84 # define NOT_STOPPED 0
85 # define PLEASE_STOP 1
88 # define FINISHED 1 /* Thread has exited. */
89 # define DETACHED 2 /* Thread is intended to be detached. */
90 ptr_t stack_cold
; /* cold end of the stack */
91 ptr_t stack_hot
; /* Valid only when stopped. */
92 /* But must be within stack region at */
94 void * status
; /* Used only to avoid premature */
95 /* reclamation of any data it might */
99 GC_thread
GC_lookup_thread(pthread_t id
);
102 * The only way to suspend threads given the pthread interface is to send
103 * signals. Unfortunately, this means we have to reserve
104 * a signal, and intercept client calls to change the signal mask.
107 # if defined(GC_AIX_THREADS)
108 # define SIG_SUSPEND SIGUSR1
110 # define SIG_SUSPEND (SIGRTMIN + 6)
114 pthread_mutex_t GC_suspend_lock
= PTHREAD_MUTEX_INITIALIZER
;
115 /* Number of threads stopped so far */
116 pthread_cond_t GC_suspend_ack_cv
= PTHREAD_COND_INITIALIZER
;
117 pthread_cond_t GC_continue_cv
= PTHREAD_COND_INITIALIZER
;
119 void GC_suspend_handler(int sig
)
127 if (sig
!= SIG_SUSPEND
) ABORT("Bad signal in suspend_handler");
128 me
= GC_lookup_thread(pthread_self());
129 /* The lookup here is safe, since I'm doing this on behalf */
130 /* of a thread which holds the allocation lock in order */
131 /* to stop the world. Thus concurrent modification of the */
132 /* data structure is impossible. */
133 if (PLEASE_STOP
!= me
-> stop
) {
134 /* Misdirected signal. */
135 pthread_mutex_unlock(&GC_suspend_lock
);
138 pthread_mutex_lock(&GC_suspend_lock
);
139 me
-> stack_hot
= (ptr_t
)(&dummy
);
140 me
-> stop
= STOPPED
;
141 pthread_cond_signal(&GC_suspend_ack_cv
);
142 pthread_cond_wait(&GC_continue_cv
, &GC_suspend_lock
);
143 pthread_mutex_unlock(&GC_suspend_lock
);
144 /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
148 GC_bool GC_thr_initialized
= FALSE
;
151 # define THREAD_TABLE_SZ 128 /* Must be power of 2 */
152 volatile GC_thread GC_threads
[THREAD_TABLE_SZ
];
154 void GC_push_thread_structures
GC_PROTO((void))
156 GC_push_all((ptr_t
)(GC_threads
), (ptr_t
)(GC_threads
)+sizeof(GC_threads
));
159 /* Add a thread to GC_threads. We assume it wasn't already there. */
160 /* Caller holds allocation lock. */
161 GC_thread
GC_new_thread(pthread_t id
)
163 int hv
= ((word
)id
) % THREAD_TABLE_SZ
;
165 static struct GC_Thread_Rep first_thread
;
166 static GC_bool first_thread_used
= FALSE
;
168 GC_ASSERT(I_HOLD_LOCK());
169 if (!first_thread_used
) {
170 result
= &first_thread
;
171 first_thread_used
= TRUE
;
172 /* Dont acquire allocation lock, since we may already hold it. */
174 result
= (struct GC_Thread_Rep
*)
175 GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep
), NORMAL
);
177 if (result
== 0) return(0);
179 result
-> next
= GC_threads
[hv
];
180 GC_threads
[hv
] = result
;
181 /* result -> flags = 0; */
182 /* result -> stop = 0; */
186 /* Delete a thread from GC_threads. We assume it is there. */
187 /* (The code intentionally traps if it wasn't.) */
188 /* Caller holds allocation lock. */
189 /* We explicitly pass in the GC_thread we're looking for, since */
190 /* if a thread has been joined, but we have not yet */
191 /* been notified, then there may be more than one thread */
192 /* in the table with the same pthread id. */
193 /* This is OK, but we need a way to delete a specific one. */
194 void GC_delete_gc_thread(pthread_t id
, GC_thread gc_id
)
196 int hv
= ((word
)id
) % THREAD_TABLE_SZ
;
197 register GC_thread p
= GC_threads
[hv
];
198 register GC_thread prev
= 0;
200 GC_ASSERT(I_HOLD_LOCK());
206 GC_threads
[hv
] = p
-> next
;
208 prev
-> next
= p
-> next
;
212 /* Return a GC_thread corresponding to a given thread_t. */
213 /* Returns 0 if it's not there. */
214 /* Caller holds allocation lock or otherwise inhibits */
216 /* If there is more than one thread with the given id we */
217 /* return the most recent one. */
218 GC_thread
GC_lookup_thread(pthread_t id
)
220 int hv
= ((word
)id
) % THREAD_TABLE_SZ
;
221 register GC_thread p
= GC_threads
[hv
];
223 /* I either hold the lock, or i'm being called from the stop-the-world
225 #if defined(GC_AIX_THREADS)
226 GC_ASSERT(I_HOLD_LOCK()); /* no stop-the-world handler needed on AIX */
228 while (p
!= 0 && !pthread_equal(p
-> id
, id
)) p
= p
-> next
;
232 #if defined(GC_AIX_THREADS)
235 pthread_t my_thread
= pthread_self();
237 register GC_thread p
;
239 struct timespec timeout
;
241 GC_ASSERT(I_HOLD_LOCK());
242 for (i
= 0; i
< THREAD_TABLE_SZ
; i
++) {
243 for (p
= GC_threads
[i
]; p
!= 0; p
= p
-> next
) {
244 if (p
-> id
!= my_thread
) {
245 pthread_suspend_np(p
->id
);
249 /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
252 void GC_start_world()
256 pthread_t my_thread
= pthread_self();
258 /* GC_printf0("World starting\n"); */
259 GC_ASSERT(I_HOLD_LOCK());
260 for (i
= 0; i
< THREAD_TABLE_SZ
; i
++) {
261 for (p
= GC_threads
[i
]; p
!= 0; p
= p
-> next
) {
262 if (p
-> id
!= my_thread
) {
263 pthread_continue_np(p
->id
);
269 #else /* GC_AIX_THREADS */
271 /* Caller holds allocation lock. */
274 pthread_t my_thread
= pthread_self();
276 register GC_thread p
;
278 struct timespec timeout
;
280 GC_ASSERT(I_HOLD_LOCK());
281 for (i
= 0; i
< THREAD_TABLE_SZ
; i
++) {
282 for (p
= GC_threads
[i
]; p
!= 0; p
= p
-> next
) {
283 if (p
-> id
!= my_thread
) {
284 if (p
-> flags
& FINISHED
) {
288 p
-> stop
= PLEASE_STOP
;
289 result
= pthread_kill(p
-> id
, SIG_SUSPEND
);
290 /* GC_printf1("Sent signal to 0x%x\n", p -> id); */
293 /* Not really there anymore. Possible? */
299 ABORT("pthread_kill failed");
304 pthread_mutex_lock(&GC_suspend_lock
);
305 for (i
= 0; i
< THREAD_TABLE_SZ
; i
++) {
306 for (p
= GC_threads
[i
]; p
!= 0; p
= p
-> next
) {
307 while (p
-> id
!= my_thread
&& p
-> stop
!= STOPPED
) {
308 clock_gettime(CLOCK_REALTIME
, &timeout
);
309 timeout
.tv_nsec
+= 50000000; /* 50 msecs */
310 if (timeout
.tv_nsec
>= 1000000000) {
311 timeout
.tv_nsec
-= 1000000000;
314 result
= pthread_cond_timedwait(&GC_suspend_ack_cv
,
317 if (result
== ETIMEDOUT
) {
318 /* Signal was lost or misdirected. Try again. */
319 /* Duplicate signals should be benign. */
320 result
= pthread_kill(p
-> id
, SIG_SUSPEND
);
325 pthread_mutex_unlock(&GC_suspend_lock
);
326 /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
329 /* Caller holds allocation lock. */
330 void GC_start_world()
335 /* GC_printf0("World starting\n"); */
336 GC_ASSERT(I_HOLD_LOCK());
337 for (i
= 0; i
< THREAD_TABLE_SZ
; i
++) {
338 for (p
= GC_threads
[i
]; p
!= 0; p
= p
-> next
) {
339 p
-> stop
= NOT_STOPPED
;
342 pthread_mutex_lock(&GC_suspend_lock
);
343 /* All other threads are at pthread_cond_wait in signal handler. */
344 /* Otherwise we couldn't have acquired the lock. */
345 pthread_mutex_unlock(&GC_suspend_lock
);
346 pthread_cond_broadcast(&GC_continue_cv
);
349 #endif /* GC_AIX_THREADS */
352 /* We hold allocation lock. Should do exactly the right thing if the */
353 /* world is stopped. Should not fail if it isn't. */
354 void GC_push_all_stacks()
357 register GC_thread p
;
358 register ptr_t hot
, cold
;
359 pthread_t me
= pthread_self();
361 /* GC_init() should have been called before GC_push_all_stacks is
362 * invoked, and GC_init calls GC_thr_init(), which sets
363 * GC_thr_initialized. */
364 GC_ASSERT(GC_thr_initialized
);
366 /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
367 GC_ASSERT(I_HOLD_LOCK());
368 for (i
= 0; i
< THREAD_TABLE_SZ
; i
++) {
369 for (p
= GC_threads
[i
]; p
!= 0; p
= p
-> next
) {
370 if (p
-> flags
& FINISHED
) continue;
371 cold
= p
->stack_cold
;
372 if (!cold
) cold
=GC_stackbottom
; /* 0 indicates 'original stack' */
373 if (pthread_equal(p
-> id
, me
)) {
374 hot
= GC_approx_sp();
376 # ifdef GC_AIX_THREADS
377 /* AIX doesn't use signals to suspend, so we need to get an */
378 /* accurate hot stack pointer. */
379 /* See http://publib16.boulder.ibm.com/pseries/en_US/libs/basetrf1/pthread_getthrds_np.htm */
380 pthread_t id
= p
-> id
;
381 struct __pthrdsinfo pinfo
;
383 int val
= sizeof(regbuf
);
384 int retval
= pthread_getthrds_np(&id
, PTHRDSINFO_QUERY_ALL
, &pinfo
,
385 sizeof(pinfo
), regbuf
, &val
);
387 printf("ERROR: pthread_getthrds_np() failed in GC\n");
390 /* according to the AIX ABI,
391 "the lowest possible valid stack address is 288 bytes (144 + 144)
392 less than the current value of the stack pointer. Functions may
393 use this stack space as volatile storage which is not preserved
394 across function calls."
395 ftp://ftp.penguinppc64.org/pub/people/amodra/PPC-elf64abi.txt.gz
397 hot
= (ptr_t
)(unsigned long)pinfo
.__pi_ustk
-288;
398 cold
= (ptr_t
)pinfo
.__pi_stackend
; /* more precise */
399 /* push the registers too, because they won't be on stack */
400 GC_push_all_eager((ptr_t
)&pinfo
.__pi_context
,
401 (ptr_t
)((&pinfo
.__pi_context
)+1));
402 GC_push_all_eager((ptr_t
)regbuf
, ((ptr_t
)regbuf
)+val
);
404 hot
= p
-> stack_hot
;
407 # ifdef STACK_GROWS_UP
408 GC_push_all_stack(cold
, hot
);
410 /* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */
411 GC_push_all_stack(hot
, cold
);
418 /* We hold the allocation lock. */
422 struct sigaction act
;
424 if (GC_thr_initialized
) return;
426 /* unfortunately, GC_init_inner calls us without the lock, so
427 * this assertion is not always true. */
428 /* Why doesn't GC_init_inner hold the lock? - HB */
429 GC_ASSERT(I_HOLD_LOCK());
431 GC_thr_initialized
= TRUE
;
432 #ifndef GC_AIX_THREADS
433 (void) sigaction(SIG_SUSPEND
, 0, &act
);
434 if (act
.sa_handler
!= SIG_DFL
)
435 ABORT("Previously installed SIG_SUSPEND handler");
436 /* Install handler. */
437 act
.sa_handler
= GC_suspend_handler
;
438 act
.sa_flags
= SA_RESTART
;
439 (void) sigemptyset(&act
.sa_mask
);
440 if (0 != sigaction(SIG_SUSPEND
, &act
, 0))
441 ABORT("Failed to install SIG_SUSPEND handler");
443 /* Add the initial thread, so we can stop it. */
444 t
= GC_new_thread(pthread_self());
445 /* use '0' to indicate GC_stackbottom, since GC_init() has not
446 * completed by the time we are called (from GC_init_inner()) */
447 t
-> stack_cold
= 0; /* the original stack. */
448 t
-> stack_hot
= (ptr_t
)(&t
);
449 t
-> flags
= DETACHED
;
452 int GC_pthread_sigmask(int how
, const sigset_t
*set
, sigset_t
*oset
)
456 #ifdef GC_AIX_THREADS
457 return(pthread_sigmask(how
, set
, oset
));
460 if (set
!= NULL
&& (how
== SIG_BLOCK
|| how
== SIG_SETMASK
)) {
462 sigdelset(&fudged_set
, SIG_SUSPEND
);
465 return(pthread_sigmask(how
, set
, oset
));
469 void *(*start_routine
)(void *);
472 pthread_mutex_t registeredlock
;
473 pthread_cond_t registered
;
474 int volatile registereddone
;
477 void GC_thread_exit_proc(void *arg
)
482 me
= GC_lookup_thread(pthread_self());
483 me
-> flags
|= FINISHED
;
484 /* reclaim DETACHED thread right away; otherwise wait until join() */
485 if (me
-> flags
& DETACHED
) {
486 GC_delete_gc_thread(pthread_self(), me
);
491 int GC_pthread_join(pthread_t thread
, void **retval
)
494 GC_thread thread_gc_id
;
497 thread_gc_id
= GC_lookup_thread(thread
);
498 /* This is guaranteed to be the intended one, since the thread id */
499 /* cant have been recycled by pthreads. */
501 GC_ASSERT(!(thread_gc_id
->flags
& DETACHED
));
502 result
= pthread_join(thread
, retval
);
503 /* Some versions of the Irix pthreads library can erroneously */
504 /* return EINTR when the call succeeds. */
505 if (EINTR
== result
) result
= 0;
506 GC_ASSERT(thread_gc_id
->flags
& FINISHED
);
508 /* Here the pthread thread id may have been recycled. */
509 GC_delete_gc_thread(thread
, thread_gc_id
);
514 void * GC_start_routine(void * arg
)
517 struct start_info
* si
= arg
;
520 pthread_t my_pthread
;
521 void *(*start
)(void *);
524 my_pthread
= pthread_self();
525 /* If a GC occurs before the thread is registered, that GC will */
526 /* ignore this thread. That's fine, since it will block trying to */
527 /* acquire the allocation lock, and won't yet hold interesting */
530 /* We register the thread here instead of in the parent, so that */
531 /* we don't need to hold the allocation lock during pthread_create. */
532 /* Holding the allocation lock there would make REDIRECT_MALLOC */
533 /* impossible. It probably still doesn't work, but we're a little */
535 /* This unfortunately means that we have to be careful the parent */
536 /* doesn't try to do a pthread_join before we're registered. */
537 me
= GC_new_thread(my_pthread
);
538 me
-> flags
= si
-> flags
;
539 me
-> stack_cold
= (ptr_t
) &dummy
; /* this now the 'start of stack' */
540 me
-> stack_hot
= me
->stack_cold
;/* this field should always be sensible */
542 start
= si
-> start_routine
;
543 start_arg
= si
-> arg
;
545 pthread_mutex_lock(&(si
->registeredlock
));
546 si
->registereddone
= 1;
547 pthread_cond_signal(&(si
->registered
));
548 pthread_mutex_unlock(&(si
->registeredlock
));
549 /* si went away as soon as we did this unlock */
551 pthread_cleanup_push(GC_thread_exit_proc
, 0);
552 result
= (*start
)(start_arg
);
553 me
-> status
= result
;
554 pthread_cleanup_pop(1);
555 /* This involves acquiring the lock, ensuring that we can't exit */
556 /* while a collection that thinks we're alive is trying to stop */
562 GC_pthread_create(pthread_t
*new_thread
,
563 const pthread_attr_t
*attr
,
564 void *(*start_routine
)(void *), void *arg
)
570 struct start_info
* si
;
571 /* This is otherwise saved only in an area mmapped by the thread */
572 /* library, which isn't visible to the collector. */
575 /* GC_INTERNAL_MALLOC implicitly calls GC_init() if required */
576 si
= (struct start_info
*)GC_INTERNAL_MALLOC(sizeof(struct start_info
),
578 GC_ASSERT(GC_thr_initialized
); /* initialized by GC_init() */
580 if (0 == si
) return(ENOMEM
);
581 pthread_mutex_init(&(si
->registeredlock
), NULL
);
582 pthread_cond_init(&(si
->registered
),NULL
);
583 pthread_mutex_lock(&(si
->registeredlock
));
584 si
-> start_routine
= start_routine
;
587 pthread_attr_getdetachstate(attr
, &detachstate
);
588 if (PTHREAD_CREATE_DETACHED
== detachstate
) my_flags
|= DETACHED
;
589 si
-> flags
= my_flags
;
590 result
= pthread_create(new_thread
, attr
, GC_start_routine
, si
);
592 /* Wait until child has been added to the thread table. */
593 /* This also ensures that we hold onto si until the child is done */
594 /* with it. Thus it doesn't matter whether it is otherwise */
595 /* visible to the collector. */
598 si
->registereddone
= 0;
599 while (!si
->registereddone
)
600 pthread_cond_wait(&(si
->registered
), &(si
->registeredlock
));
602 pthread_mutex_unlock(&(si
->registeredlock
));
604 pthread_cond_destroy(&(si
->registered
));
605 pthread_mutex_destroy(&(si
->registeredlock
));
607 GC_INTERNAL_FREE(si
);
613 /* For now we use the pthreads locking primitives on HP/UX */
615 VOLATILE GC_bool GC_collecting
= 0; /* A hint that we're in the collector and */
616 /* holding the allocation lock for an */
617 /* extended period. */
619 /* Reasonably fast spin locks. Basically the same implementation */
620 /* as STL alloc.h. */
622 #define SLEEP_THRESHOLD 3
624 volatile unsigned int GC_allocate_lock
= 0;
625 #define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
626 #define GC_LOCK_TAKEN GC_allocate_lock
630 # define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
631 # define high_spin_max 1000 /* spin cycles for multiprocessor */
632 static unsigned spin_max
= low_spin_max
;
633 unsigned my_spin_max
;
634 static unsigned last_spins
= 0;
635 unsigned my_last_spins
;
636 volatile unsigned junk
;
637 # define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
644 my_spin_max
= spin_max
;
645 my_last_spins
= last_spins
;
646 for (i
= 0; i
< my_spin_max
; i
++) {
647 if (GC_collecting
) goto yield
;
648 if (i
< my_last_spins
/2 || GC_LOCK_TAKEN
) {
655 * Spinning worked. Thus we're probably not being scheduled
656 * against the other process with which we were contending.
657 * Thus it makes sense to spin longer the next time.
660 spin_max
= high_spin_max
;
664 /* We are probably being scheduled against the other process. Sleep. */
665 spin_max
= low_spin_max
;
671 if (i
< SLEEP_THRESHOLD
) {
677 /* Don't wait for more than about 60msecs, even */
678 /* under extreme contention. */
686 # else /* !GC_IRIX_THREADS && !GC_AIX_THREADS */
689 int GC_no_Irix_threads
;
692 # endif /* IRIX_THREADS */