version 1.1.1.1, 1999/11/27 10:58:32 |
version 1.1.1.3, 2000/12/01 14:48:25 |
|
|
# if defined(LINUX_THREADS) |
# if defined(LINUX_THREADS) |
|
|
# include <pthread.h> |
# include <pthread.h> |
|
# include <sched.h> |
# include <time.h> |
# include <time.h> |
# include <errno.h> |
# include <errno.h> |
# include <unistd.h> |
# include <unistd.h> |
# include <sys/mman.h> |
# include <sys/mman.h> |
# include <sys/time.h> |
# include <sys/time.h> |
# include <semaphore.h> |
# include <semaphore.h> |
|
# include <signal.h> |
|
|
#undef pthread_create |
#ifdef USE_LD_WRAP |
#undef pthread_sigmask |
# define WRAP_FUNC(f) __wrap_##f |
#undef pthread_join |
# define REAL_FUNC(f) __real_##f |
|
#else |
|
# define WRAP_FUNC(f) GC_##f |
|
# define REAL_FUNC(f) f |
|
# undef pthread_create |
|
# undef pthread_sigmask |
|
# undef pthread_join |
|
#endif |
|
|
|
|
void GC_thr_init(); |
void GC_thr_init(); |
|
|
#if 0 |
#if 0 |
Line 86 typedef struct GC_Thread_Rep { |
|
Line 96 typedef struct GC_Thread_Rep { |
|
# define DETACHED 2 /* Thread is intended to be detached. */ |
# define DETACHED 2 /* Thread is intended to be detached. */ |
# define MAIN_THREAD 4 /* True for the original thread only. */ |
# define MAIN_THREAD 4 /* True for the original thread only. */ |
|
|
ptr_t stack_end; |
ptr_t stack_end; /* Cold end of the stack. */ |
ptr_t stack_ptr; /* Valid only when stopped. */ |
ptr_t stack_ptr; /* Valid only when stopped. */ |
|
# ifdef IA64 |
|
ptr_t backing_store_end; |
|
ptr_t backing_store_ptr; |
|
# endif |
int signal; |
int signal; |
void * status; /* The value returned from the thread. */ |
void * status; /* The value returned from the thread. */ |
/* Used only to avoid premature */ |
/* Used only to avoid premature */ |
Line 118 GC_linux_thread_top_of_stack() relies on implementatio |
|
Line 132 GC_linux_thread_top_of_stack() relies on implementatio |
|
LinuxThreads, namely that thread stacks are allocated on 2M boundaries |
LinuxThreads, namely that thread stacks are allocated on 2M boundaries |
and grow to no more than 2M. |
and grow to no more than 2M. |
To make sure that we're using LinuxThreads and not some other thread |
To make sure that we're using LinuxThreads and not some other thread |
package, we generate a dummy reference to `__pthread_initial_thread_bos', |
package, we generate a dummy reference to `pthread_kill_other_threads_np' |
|
(was `__pthread_initial_thread_bos' but that disappeared), |
which is a symbol defined in LinuxThreads, but (hopefully) not in other |
which is a symbol defined in LinuxThreads, but (hopefully) not in other |
thread packages. |
thread packages. |
*/ |
*/ |
extern char * __pthread_initial_thread_bos; |
void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np; |
char **dummy_var_to_force_linux_threads = &__pthread_initial_thread_bos; |
|
|
|
#define LINUX_THREADS_STACK_SIZE (2 * 1024 * 1024) |
#define LINUX_THREADS_STACK_SIZE (2 * 1024 * 1024) |
|
|
Line 138 static inline ptr_t GC_linux_thread_top_of_stack(void) |
|
Line 152 static inline ptr_t GC_linux_thread_top_of_stack(void) |
|
return tos; |
return tos; |
} |
} |
|
|
|
#ifdef IA64 |
|
extern word GC_save_regs_in_stack(); |
|
#endif |
|
|
void GC_suspend_handler(int sig) |
void GC_suspend_handler(int sig) |
{ |
{ |
int dummy; |
int dummy; |
Line 160 void GC_suspend_handler(int sig) |
|
Line 178 void GC_suspend_handler(int sig) |
|
/* to stop the world. Thus concurrent modification of the */ |
/* to stop the world. Thus concurrent modification of the */ |
/* data structure is impossible. */ |
/* data structure is impossible. */ |
me -> stack_ptr = (ptr_t)(&dummy); |
me -> stack_ptr = (ptr_t)(&dummy); |
me -> stack_end = GC_linux_thread_top_of_stack(); |
# ifdef IA64 |
|
me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack(); |
|
# endif |
|
|
/* Tell the thread that wants to stop the world that this */ |
/* Tell the thread that wants to stop the world that this */ |
/* thread has been stopped. Note that sem_post() is */ |
/* thread has been stopped. Note that sem_post() is */ |
Line 173 void GC_suspend_handler(int sig) |
|
Line 193 void GC_suspend_handler(int sig) |
|
/* is no race. */ |
/* is no race. */ |
if (sigfillset(&mask) != 0) ABORT("sigfillset() failed"); |
if (sigfillset(&mask) != 0) ABORT("sigfillset() failed"); |
if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed"); |
if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed"); |
|
# ifdef NO_SIGNALS |
|
if (sigdelset(&mask, SIGINT) != 0) ABORT("sigdelset() failed"); |
|
if (sigdelset(&mask, SIGQUIT) != 0) ABORT("sigdelset() failed"); |
|
if (sigdelset(&mask, SIGTERM) != 0) ABORT("sigdelset() failed"); |
|
# endif |
do { |
do { |
me->signal = 0; |
me->signal = 0; |
sigsuspend(&mask); /* Wait for signal */ |
sigsuspend(&mask); /* Wait for signal */ |
Line 375 void GC_start_world() |
|
Line 400 void GC_start_world() |
|
#endif |
#endif |
} |
} |
|
|
/* We hold allocation lock. We assume the world is stopped. */ |
# ifdef IA64 |
|
# define IF_IA64(x) x |
|
# else |
|
# define IF_IA64(x) |
|
# endif |
|
/* We hold allocation lock. Should do exactly the right thing if the */ |
|
/* world is stopped. Should not fail if it isn't. */ |
void GC_push_all_stacks() |
void GC_push_all_stacks() |
{ |
{ |
register int i; |
int i; |
register GC_thread p; |
GC_thread p; |
register ptr_t sp = GC_approx_sp(); |
ptr_t sp = GC_approx_sp(); |
register ptr_t lo, hi; |
ptr_t lo, hi; |
|
/* On IA64, we also need to scan the register backing store. */ |
|
IF_IA64(ptr_t bs_lo; ptr_t bs_hi;) |
pthread_t me = pthread_self(); |
pthread_t me = pthread_self(); |
|
|
if (!GC_thr_initialized) GC_thr_init(); |
if (!GC_thr_initialized) GC_thr_init(); |
Line 393 void GC_push_all_stacks() |
|
Line 426 void GC_push_all_stacks() |
|
if (p -> flags & FINISHED) continue; |
if (p -> flags & FINISHED) continue; |
if (pthread_equal(p -> id, me)) { |
if (pthread_equal(p -> id, me)) { |
lo = GC_approx_sp(); |
lo = GC_approx_sp(); |
|
IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();) |
} else { |
} else { |
lo = p -> stack_ptr; |
lo = p -> stack_ptr; |
|
IF_IA64(bs_hi = p -> backing_store_ptr;) |
} |
} |
if ((p -> flags & MAIN_THREAD) == 0) { |
if ((p -> flags & MAIN_THREAD) == 0) { |
if (pthread_equal(p -> id, me)) { |
hi = p -> stack_end; |
hi = GC_linux_thread_top_of_stack(); |
IF_IA64(bs_lo = p -> backing_store_end); |
} else { |
|
hi = p -> stack_end; |
|
} |
|
} else { |
} else { |
/* The original stack. */ |
/* The original stack. */ |
hi = GC_stackbottom; |
hi = GC_stackbottom; |
|
IF_IA64(bs_lo = BACKING_STORE_BASE;) |
} |
} |
#if DEBUG_THREADS |
#if DEBUG_THREADS |
GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n", |
GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n", |
(unsigned long) p -> id, |
(unsigned long) p -> id, |
(unsigned long) lo, (unsigned long) hi); |
(unsigned long) lo, (unsigned long) hi); |
#endif |
#endif |
|
if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n"); |
GC_push_all_stack(lo, hi); |
GC_push_all_stack(lo, hi); |
|
# ifdef IA64 |
|
if (pthread_equal(p -> id, me)) { |
|
GC_push_all_eager(bs_lo, bs_hi); |
|
} else { |
|
GC_push_all_stack(bs_lo, bs_hi); |
|
} |
|
# endif |
} |
} |
} |
} |
} |
} |
Line 420 void GC_push_all_stacks() |
|
Line 461 void GC_push_all_stacks() |
|
/* We hold the allocation lock. */ |
/* We hold the allocation lock. */ |
void GC_thr_init() |
void GC_thr_init() |
{ |
{ |
|
int dummy; |
GC_thread t; |
GC_thread t; |
struct sigaction act; |
struct sigaction act; |
|
|
Line 433 void GC_thr_init() |
|
Line 475 void GC_thr_init() |
|
if (sigfillset(&act.sa_mask) != 0) { |
if (sigfillset(&act.sa_mask) != 0) { |
ABORT("sigfillset() failed"); |
ABORT("sigfillset() failed"); |
} |
} |
|
# ifdef NO_SIGNALS |
|
if (sigdelset(&act.sa_mask, SIGINT) != 0 |
|
|| sigdelset(&act.sa_mask, SIGQUIT != 0) |
|
|| sigdelset(&act.sa_mask, SIGTERM != 0)) { |
|
ABORT("sigdelset() failed"); |
|
} |
|
# endif |
|
|
/* SIG_RESTART is unmasked by the handler when necessary. */ |
/* SIG_RESTART is unmasked by the handler when necessary. */ |
act.sa_handler = GC_suspend_handler; |
act.sa_handler = GC_suspend_handler; |
if (sigaction(SIG_SUSPEND, &act, NULL) != 0) { |
if (sigaction(SIG_SUSPEND, &act, NULL) != 0) { |
Line 446 void GC_thr_init() |
|
Line 496 void GC_thr_init() |
|
|
|
/* Add the initial thread, so we can stop it. */ |
/* Add the initial thread, so we can stop it. */ |
t = GC_new_thread(pthread_self()); |
t = GC_new_thread(pthread_self()); |
t -> stack_ptr = 0; |
t -> stack_ptr = (ptr_t)(&dummy); |
t -> flags = DETACHED | MAIN_THREAD; |
t -> flags = DETACHED | MAIN_THREAD; |
} |
} |
|
|
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) |
int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset) |
{ |
{ |
sigset_t fudged_set; |
sigset_t fudged_set; |
|
|
Line 459 int GC_pthread_sigmask(int how, const sigset_t *set, s |
|
Line 509 int GC_pthread_sigmask(int how, const sigset_t *set, s |
|
sigdelset(&fudged_set, SIG_SUSPEND); |
sigdelset(&fudged_set, SIG_SUSPEND); |
set = &fudged_set; |
set = &fudged_set; |
} |
} |
return(pthread_sigmask(how, set, oset)); |
return(REAL_FUNC(pthread_sigmask)(how, set, oset)); |
} |
} |
|
|
struct start_info { |
struct start_info { |
Line 483 void GC_thread_exit_proc(void *arg) |
|
Line 533 void GC_thread_exit_proc(void *arg) |
|
} else { |
} else { |
me -> flags |= FINISHED; |
me -> flags |= FINISHED; |
} |
} |
|
if (GC_incremental && GC_collection_in_progress()) { |
|
int old_gc_no = GC_gc_no; |
|
|
|
/* Make sure that no part of our stack is still on the mark stack, */ |
|
/* since it's about to be unmapped. */ |
|
while (GC_incremental && GC_collection_in_progress() |
|
&& old_gc_no == GC_gc_no) { |
|
ENTER_GC(); |
|
GC_collect_a_little_inner(1); |
|
EXIT_GC(); |
|
UNLOCK(); |
|
sched_yield(); |
|
LOCK(); |
|
} |
|
} |
UNLOCK(); |
UNLOCK(); |
} |
} |
|
|
int GC_pthread_join(pthread_t thread, void **retval) |
int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval) |
{ |
{ |
int result; |
int result; |
GC_thread thread_gc_id; |
GC_thread thread_gc_id; |
Line 496 int GC_pthread_join(pthread_t thread, void **retval) |
|
Line 561 int GC_pthread_join(pthread_t thread, void **retval) |
|
/* This is guaranteed to be the intended one, since the thread id */ |
/* This is guaranteed to be the intended one, since the thread id */ |
/* cant have been recycled by pthreads. */ |
/* cant have been recycled by pthreads. */ |
UNLOCK(); |
UNLOCK(); |
result = pthread_join(thread, retval); |
result = REAL_FUNC(pthread_join)(thread, retval); |
LOCK(); |
LOCK(); |
/* Here the pthread thread id may have been recycled. */ |
/* Here the pthread thread id may have been recycled. */ |
GC_delete_gc_thread(thread, thread_gc_id); |
GC_delete_gc_thread(thread, thread_gc_id); |
Line 506 int GC_pthread_join(pthread_t thread, void **retval) |
|
Line 571 int GC_pthread_join(pthread_t thread, void **retval) |
|
|
|
void * GC_start_routine(void * arg) |
void * GC_start_routine(void * arg) |
{ |
{ |
|
int dummy; |
struct start_info * si = arg; |
struct start_info * si = arg; |
void * result; |
void * result; |
GC_thread me; |
GC_thread me; |
Line 514 void * GC_start_routine(void * arg) |
|
Line 580 void * GC_start_routine(void * arg) |
|
void *start_arg; |
void *start_arg; |
|
|
my_pthread = pthread_self(); |
my_pthread = pthread_self(); |
|
# ifdef DEBUG_THREADS |
|
GC_printf1("Starting thread 0x%lx\n", my_pthread); |
|
GC_printf1("pid = %ld\n", (long) getpid()); |
|
GC_printf1("sp = 0x%lx\n", (long) &arg); |
|
# endif |
LOCK(); |
LOCK(); |
me = GC_new_thread(my_pthread); |
me = GC_new_thread(my_pthread); |
me -> flags = si -> flags; |
me -> flags = si -> flags; |
me -> stack_ptr = 0; |
me -> stack_ptr = 0; |
me -> stack_end = 0; |
/* me -> stack_end = GC_linux_stack_base(); -- currently (11/99) */ |
|
/* doesn't work because the stack base in /proc/self/stat is the */ |
|
/* one for the main thread. There is a strong argument that that's */ |
|
/* a kernel bug, but a pervasive one. */ |
|
# ifdef STACK_GROWS_DOWN |
|
me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1)) |
|
& ~(GC_page_size - 1)); |
|
me -> stack_ptr = me -> stack_end - 0x10; |
|
/* Needs to be plausible, since an asynchronous stack mark */ |
|
/* should not crash. */ |
|
# else |
|
me -> stack_end = (ptr_t)(((word)(&dummy) & ~(GC_page_size - 1)); |
|
me -> stack_ptr = me -> stack_end + 0x10; |
|
# endif |
|
/* This is dubious, since we may be more than a page into the stack, */ |
|
/* and hence skip some of it, though it's not clear that matters. */ |
|
# ifdef IA64 |
|
me -> backing_store_end = (ptr_t) |
|
(GC_save_regs_in_stack() & ~(GC_page_size - 1)); |
|
/* This is also < 100% convincing. We should also read this */ |
|
/* from /proc, but the hook to do so isn't there yet. */ |
|
# endif /* IA64 */ |
UNLOCK(); |
UNLOCK(); |
start = si -> start_routine; |
start = si -> start_routine; |
start_arg = si -> arg; |
|
sem_post(&(si -> registered)); |
|
pthread_cleanup_push(GC_thread_exit_proc, si); |
|
# ifdef DEBUG_THREADS |
# ifdef DEBUG_THREADS |
GC_printf1("Starting thread 0x%lx\n", pthread_self()); |
|
GC_printf1("pid = %ld\n", (long) getpid()); |
|
GC_printf1("sp = 0x%lx\n", (long) &arg); |
|
GC_printf1("start_routine = 0x%lx\n", start); |
GC_printf1("start_routine = 0x%lx\n", start); |
# endif |
# endif |
|
start_arg = si -> arg; |
|
sem_post(&(si -> registered)); |
|
pthread_cleanup_push(GC_thread_exit_proc, si); |
result = (*start)(start_arg); |
result = (*start)(start_arg); |
#if DEBUG_THREADS |
#if DEBUG_THREADS |
GC_printf1("Finishing thread 0x%x\n", pthread_self()); |
GC_printf1("Finishing thread 0x%x\n", pthread_self()); |
Line 544 void * GC_start_routine(void * arg) |
|
Line 633 void * GC_start_routine(void * arg) |
|
} |
} |
|
|
int |
int |
GC_pthread_create(pthread_t *new_thread, |
WRAP_FUNC(pthread_create)(pthread_t *new_thread, |
const pthread_attr_t *attr, |
const pthread_attr_t *attr, |
void *(*start_routine)(void *), void *arg) |
void *(*start_routine)(void *), void *arg) |
{ |
{ |
Line 576 GC_pthread_create(pthread_t *new_thread, |
|
Line 665 GC_pthread_create(pthread_t *new_thread, |
|
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; |
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; |
si -> flags = my_flags; |
si -> flags = my_flags; |
UNLOCK(); |
UNLOCK(); |
result = pthread_create(new_thread, &new_attr, GC_start_routine, si); |
# ifdef DEBUG_THREADS |
|
GC_printf1("About to start new thread from thread 0x%X\n", |
|
pthread_self()); |
|
# endif |
|
result = REAL_FUNC(pthread_create)(new_thread, &new_attr, GC_start_routine, si); |
|
# ifdef DEBUG_THREADS |
|
GC_printf1("Started thread 0x%X\n", *new_thread); |
|
# endif |
/* Wait until child has been added to the thread table. */ |
/* Wait until child has been added to the thread table. */ |
/* This also ensures that we hold onto si until the child is done */ |
/* This also ensures that we hold onto si until the child is done */ |
/* with it. Thus it doesn't matter whether it is otherwise */ |
/* with it. Thus it doesn't matter whether it is otherwise */ |
Line 588 GC_pthread_create(pthread_t *new_thread, |
|
Line 684 GC_pthread_create(pthread_t *new_thread, |
|
return(result); |
return(result); |
} |
} |
|
|
GC_bool GC_collecting = 0; |
#if defined(USE_SPIN_LOCK) |
|
|
|
VOLATILE GC_bool GC_collecting = 0; |
/* A hint that we're in the collector and */ |
/* A hint that we're in the collector and */ |
/* holding the allocation lock for an */ |
/* holding the allocation lock for an */ |
/* extended period. */ |
/* extended period. */ |
|
|
} |
} |
} |
} |
} |
} |
|
|
|
#endif /* known architecture */ |
|
|
# endif /* LINUX_THREADS */ |
# endif /* LINUX_THREADS */ |
|
|