summaryrefslogtreecommitdiff
path: root/aix_irix_threads.c
diff options
context:
space:
mode:
Diffstat (limited to 'aix_irix_threads.c')
-rw-r--r--aix_irix_threads.c354
1 files changed, 91 insertions, 263 deletions
diff --git a/aix_irix_threads.c b/aix_irix_threads.c
index d3e8ae0..d8ac345 100644
--- a/aix_irix_threads.c
+++ b/aix_irix_threads.c
@@ -87,13 +87,10 @@ typedef struct GC_Thread_Rep {
word flags;
# define FINISHED 1 /* Thread has exited. */
# define DETACHED 2 /* Thread is intended to be detached. */
-# define CLIENT_OWNS_STACK 4
- /* Stack was supplied by client. */
- ptr_t stack;
- ptr_t stack_ptr; /* Valid only when stopped. */
+ ptr_t stack_cold; /* cold end of the stack */
+ ptr_t stack_hot; /* Valid only when stopped. */
/* But must be within stack region at */
/* all times. */
- size_t stack_size; /* 0 for original thread. */
void * status; /* Used only to avoid premature */
/* reclamation of any data it might */
/* reference. */
@@ -139,7 +136,7 @@ void GC_suspend_handler(int sig)
return;
}
pthread_mutex_lock(&GC_suspend_lock);
- me -> stack_ptr = (ptr_t)(&dummy);
+ me -> stack_hot = (ptr_t)(&dummy);
me -> stop = STOPPED;
pthread_cond_signal(&GC_suspend_ack_cv);
pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
@@ -150,68 +147,6 @@ void GC_suspend_handler(int sig)
GC_bool GC_thr_initialized = FALSE;
-size_t GC_min_stack_sz;
-
-size_t GC_page_sz;
-
-# define N_FREE_LISTS 25
-ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
- /* GC_stack_free_lists[i] is free list for stacks of */
- /* size GC_min_stack_sz*2**i. */
- /* Free lists are linked through first word. */
-
-/* Return a stack of size at least *stack_size. *stack_size is */
-/* replaced by the actual stack size. */
-/* Caller holds allocation lock. */
-ptr_t GC_stack_alloc(size_t * stack_size)
-{
- register size_t requested_sz = *stack_size;
- register size_t search_sz = GC_min_stack_sz;
- register int index = 0; /* = log2(search_sz/GC_min_stack_sz) */
- register ptr_t result;
-
- while (search_sz < requested_sz) {
- search_sz *= 2;
- index++;
- }
- if ((result = GC_stack_free_lists[index]) == 0
- && (result = GC_stack_free_lists[index+1]) != 0) {
- /* Try next size up. */
- search_sz *= 2; index++;
- }
- if (result != 0) {
- GC_stack_free_lists[index] = *(ptr_t *)result;
- } else {
- result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz);
- result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1));
- /* Protect hottest page to detect overflow. */
-# ifdef STACK_GROWS_UP
- /* mprotect(result + search_sz, GC_page_sz, PROT_NONE); */
-# else
- /* mprotect(result, GC_page_sz, PROT_NONE); */
- result += GC_page_sz;
-# endif
- }
- *stack_size = search_sz;
- return(result);
-}
-
-/* Caller holds allocation lock. */
-void GC_stack_free(ptr_t stack, size_t size)
-{
- register int index = 0;
- register size_t search_sz = GC_min_stack_sz;
-
- while (search_sz < size) {
- search_sz *= 2;
- index++;
- }
- if (search_sz != size) ABORT("Bad stack size");
- *(ptr_t *)stack = GC_stack_free_lists[index];
- GC_stack_free_lists[index] = stack;
-}
-
-
# define THREAD_TABLE_SZ 128 /* Must be power of 2 */
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
@@ -230,6 +165,7 @@ GC_thread GC_new_thread(pthread_t id)
static struct GC_Thread_Rep first_thread;
static GC_bool first_thread_used = FALSE;
+ GC_ASSERT(I_HOLD_LOCK());
if (!first_thread_used) {
result = &first_thread;
first_thread_used = TRUE;
@@ -250,24 +186,8 @@ GC_thread GC_new_thread(pthread_t id)
/* Delete a thread from GC_threads. We assume it is there. */
/* (The code intentionally traps if it wasn't.) */
/* Caller holds allocation lock. */
-void GC_delete_thread(pthread_t id)
-{
- int hv = ((word)id) % THREAD_TABLE_SZ;
- register GC_thread p = GC_threads[hv];
- register GC_thread prev = 0;
-
- while (!pthread_equal(p -> id, id)) {
- prev = p;
- p = p -> next;
- }
- if (prev == 0) {
- GC_threads[hv] = p -> next;
- } else {
- prev -> next = p -> next;
- }
-}
-
-/* If a thread has been joined, but we have not yet */
+/* We explicitly pass in the GC_thread we're looking for, since */
+/* if a thread has been joined, but we have not yet */
/* been notified, then there may be more than one thread */
/* in the table with the same pthread id. */
/* This is OK, but we need a way to delete a specific one. */
@@ -277,6 +197,7 @@ void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
register GC_thread p = GC_threads[hv];
register GC_thread prev = 0;
+ GC_ASSERT(I_HOLD_LOCK());
while (p != gc_id) {
prev = p;
p = p -> next;
@@ -299,6 +220,11 @@ GC_thread GC_lookup_thread(pthread_t id)
int hv = ((word)id) % THREAD_TABLE_SZ;
register GC_thread p = GC_threads[hv];
+ /* I either hold the lock, or i'm being called from the stop-the-world
+ * handler. */
+#if defined(GC_AIX_THREADS)
+ GC_ASSERT(I_HOLD_LOCK()); /* no stop-the-world handler needed on AIX */
+#endif
while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
return(p);
}
@@ -312,6 +238,7 @@ void GC_stop_world()
register int result;
struct timespec timeout;
+ GC_ASSERT(I_HOLD_LOCK());
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (p -> id != my_thread) {
@@ -329,6 +256,7 @@ void GC_start_world()
pthread_t my_thread = pthread_self();
/* GC_printf0("World starting\n"); */
+ GC_ASSERT(I_HOLD_LOCK());
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (p -> id != my_thread) {
@@ -349,6 +277,7 @@ void GC_stop_world()
register int result;
struct timespec timeout;
+ GC_ASSERT(I_HOLD_LOCK());
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (p -> id != my_thread) {
@@ -404,6 +333,7 @@ void GC_start_world()
unsigned i;
/* GC_printf0("World starting\n"); */
+ GC_ASSERT(I_HOLD_LOCK());
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
p -> stop = NOT_STOPPED;
@@ -418,25 +348,6 @@ void GC_start_world()
#endif /* GC_AIX_THREADS */
-# ifdef MMAP_STACKS
---> not really supported yet.
-int GC_is_thread_stack(ptr_t addr)
-{
- register int i;
- register GC_thread p;
-
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> stack_size != 0) {
- if (p -> stack <= addr &&
- addr < p -> stack + p -> stack_size)
- return 1;
- }
- }
- }
- return 0;
-}
-# endif
/* We hold allocation lock. Should do exactly the right thing if the */
/* world is stopped. Should not fail if it isn't. */
@@ -444,64 +355,55 @@ void GC_push_all_stacks()
{
register int i;
register GC_thread p;
- register ptr_t sp = GC_approx_sp();
register ptr_t hot, cold;
pthread_t me = pthread_self();
- if (!GC_thr_initialized) GC_thr_init();
+ /* GC_init() should have been called before GC_push_all_stacks is
+ * invoked, and GC_init calls GC_thr_init(), which sets
+ * GC_thr_initialized. */
+ GC_ASSERT(GC_thr_initialized);
+
/* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
+ GC_ASSERT(I_HOLD_LOCK());
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (p -> flags & FINISHED) continue;
+ cold = p->stack_cold;
+ if (!cold) cold=GC_stackbottom; /* 0 indicates 'original stack' */
if (pthread_equal(p -> id, me)) {
hot = GC_approx_sp();
} else {
-#ifdef GC_AIX_THREADS
- /* AIX doesn't use signals to suspend, so we need to get an accurate hot stack pointer */
+# ifdef GC_AIX_THREADS
+ /* AIX doesn't use signals to suspend, so we need to get an */
+ /* accurate hot stack pointer. */
+ /* See http://publib16.boulder.ibm.com/pseries/en_US/libs/basetrf1/pthread_getthrds_np.htm */
pthread_t id = p -> id;
struct __pthrdsinfo pinfo;
- int val = 255;
- char regbuf[255];
- int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo, sizeof(pinfo), regbuf, &val);
- if (retval != 0) { printf("ERROR: pthread_getthrds_np() failed in GC\n"); abort(); }
- hot = (ptr_t)(unsigned long)pinfo.__pi_ustk;
- if ((p -> stack_size != 0 &&
- (pinfo.__pi_stackend != ((ptr_t)p -> stack) + p -> stack_size ||
- p -> stack_ptr < p -> stack ||
- p -> stack_ptr > ((ptr_t)p -> stack) + p -> stack_size))) {
- printf("ERROR in GC_push_all_stacks() stack state:\n"
- "p->stack: 0x%08x\n"
- "p->stack_size: 0x%08x\n"
- "p->stack_ptr: 0x%08x\n"
- "(p->stack+p->stack_size): 0x%08x\n"
- "pinfo.__pi_stackaddr: 0x%08x\n"
- "pinfo.__pi_stacksize: 0x%08x\n"
- "pinfo.__pi_stackend: 0x%08x\n"
- "GC_stackbottom: 0x%08x\n"
- ,
- (uintptr_t)p->stack, (uintptr_t)p->stack_size,
- (uintptr_t)p->stack_ptr, (uintptr_t)(((ptr_t)(p->stack))+p->stack_size),
- (uintptr_t)pinfo.__pi_stackaddr, (uintptr_t)pinfo.__pi_stacksize, (uintptr_t)pinfo.__pi_stackend,
- (uintptr_t)GC_stackbottom
- );
- }
+ int regbuf[64];
+ int val = sizeof(regbuf);
+ int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo,
+ sizeof(pinfo), regbuf, &val);
+ if (retval != 0) {
+ printf("ERROR: pthread_getthrds_np() failed in GC\n");
+ abort();
+ }
+ /* according to the AIX ABI,
+ "the lowest possible valid stack address is 288 bytes (144 + 144)
+ less than the current value of the stack pointer. Functions may
+ use this stack space as volatile storage which is not preserved
+ across function calls."
+ ftp://ftp.penguinppc64.org/pub/people/amodra/PPC-elf64abi.txt.gz
+ */
+ hot = (ptr_t)(unsigned long)pinfo.__pi_ustk-288;
+ cold = (ptr_t)pinfo.__pi_stackend; /* more precise */
/* push the registers too, because they won't be on stack */
- GC_push_all_eager((ptr_t)&pinfo.__pi_context, (ptr_t)((&pinfo.__pi_context)+1));
- GC_push_all_eager((ptr_t)regbuf, (ptr_t)&regbuf[val]);
-#else
- hot = p -> stack_ptr;
-#endif
+ GC_push_all_eager((ptr_t)&pinfo.__pi_context,
+ (ptr_t)((&pinfo.__pi_context)+1));
+ GC_push_all_eager((ptr_t)regbuf, ((ptr_t)regbuf)+val);
+# else
+ hot = p -> stack_hot;
+# endif
}
- if (p -> stack_size != 0) {
-# ifdef STACK_GROWS_UP
- cold = p -> stack;
-# else
- cold = p -> stack + p -> stack_size;
-# endif
- } else {
- /* The original stack. */
- cold = GC_stackbottom;
- }
# ifdef STACK_GROWS_UP
GC_push_all_stack(cold, hot);
# else
@@ -520,9 +422,13 @@ void GC_thr_init()
struct sigaction act;
if (GC_thr_initialized) return;
+#if 0
+ /* unfortunately, GC_init_inner calls us without the lock, so
+ * this assertion is not always true. */
+ /* Why doesn't GC_init_inner hold the lock? - HB */
+ GC_ASSERT(I_HOLD_LOCK());
+#endif
GC_thr_initialized = TRUE;
- GC_min_stack_sz = HBLKSIZE;
- GC_page_sz = sysconf(_SC_PAGESIZE);
#ifndef GC_AIX_THREADS
(void) sigaction(SIG_SUSPEND, 0, &act);
if (act.sa_handler != SIG_DFL)
@@ -536,8 +442,10 @@ void GC_thr_init()
#endif
/* Add the initial thread, so we can stop it. */
t = GC_new_thread(pthread_self());
- t -> stack_size = 0;
- t -> stack_ptr = (ptr_t)(&t);
+ /* use '0' to indicate GC_stackbottom, since GC_init() has not
+ * completed by the time we are called (from GC_init_inner()) */
+ t -> stack_cold = 0; /* the original stack. */
+ t -> stack_hot = (ptr_t)(&t);
t -> flags = DETACHED;
}
@@ -561,8 +469,6 @@ struct start_info {
void *(*start_routine)(void *);
void *arg;
word flags;
- ptr_t stack;
- size_t stack_size;
pthread_mutex_t registeredlock;
pthread_cond_t registered;
int volatile registereddone;
@@ -574,10 +480,10 @@ void GC_thread_exit_proc(void *arg)
LOCK();
me = GC_lookup_thread(pthread_self());
+ me -> flags |= FINISHED;
+ /* reclaim DETACHED thread right away; otherwise wait until join() */
if (me -> flags & DETACHED) {
- GC_delete_thread(pthread_self());
- } else {
- me -> flags |= FINISHED;
+ GC_delete_gc_thread(pthread_self(), me);
}
UNLOCK();
}
@@ -592,10 +498,12 @@ int GC_pthread_join(pthread_t thread, void **retval)
/* This is guaranteed to be the intended one, since the thread id */
/* cant have been recycled by pthreads. */
UNLOCK();
+ GC_ASSERT(!(thread_gc_id->flags & DETACHED));
result = pthread_join(thread, retval);
/* Some versions of the Irix pthreads library can erroneously */
/* return EINTR when the call succeeds. */
if (EINTR == result) result = 0;
+ GC_ASSERT(thread_gc_id->flags & FINISHED);
LOCK();
/* Here the pthread thread id may have been recycled. */
GC_delete_gc_thread(thread, thread_gc_id);
@@ -605,6 +513,7 @@ int GC_pthread_join(pthread_t thread, void **retval)
void * GC_start_routine(void * arg)
{
+ int dummy;
struct start_info * si = arg;
void * result;
GC_thread me;
@@ -627,14 +536,8 @@ void * GC_start_routine(void * arg)
/* doesn't try to do a pthread_join before we're registered. */
me = GC_new_thread(my_pthread);
me -> flags = si -> flags;
- me -> stack = si -> stack;
- me -> stack_size = si -> stack_size;
-#ifdef STACK_GROWS_UP
- me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
-#else
- /* stack_ptr needs to point to the hot part of the stack (or conservatively, past it) */
- me -> stack_ptr = (ptr_t)si -> stack;
-#endif
+ me -> stack_cold = (ptr_t) &dummy; /* this now the 'start of stack' */
+ me -> stack_hot = me->stack_cold;/* this field should always be sensible */
UNLOCK();
start = si -> start_routine;
start_arg = si -> arg;
@@ -643,6 +546,7 @@ void * GC_start_routine(void * arg)
si->registereddone = 1;
pthread_cond_signal(&(si->registered));
pthread_mutex_unlock(&(si->registeredlock));
+ /* si went away as soon as we did this unlock */
pthread_cleanup_push(GC_thread_exit_proc, 0);
result = (*start)(start_arg);
@@ -654,44 +558,6 @@ void * GC_start_routine(void * arg)
return(result);
}
-# if defined(GC_AIX_THREADS)
- /* pthread_attr_t is not a structure, thus a simple structure copy */
- /* won't work. */
- static void copy_attr(pthread_attr_t * pa_ptr,
- const pthread_attr_t * source) {
- int tmp;
- size_t stmp;
- void * vtmp;
- struct sched_param sp_tmp;
-#ifndef GC_AIX_THREADS
- pthread_spu_t ps_tmp;
-#endif
- (void) pthread_attr_init(pa_ptr);
- (void) pthread_attr_getdetachstate(source, &tmp);
- (void) pthread_attr_setdetachstate(pa_ptr, tmp);
- (void) pthread_attr_getinheritsched(source, &tmp);
- (void) pthread_attr_setinheritsched(pa_ptr, tmp);
- (void) pthread_attr_getschedpolicy(source, &tmp);
- (void) pthread_attr_setschedpolicy(pa_ptr, tmp);
- (void) pthread_attr_getstacksize(source, &stmp);
- (void) pthread_attr_setstacksize(pa_ptr, stmp);
- (void) pthread_attr_getguardsize(source, &stmp);
- (void) pthread_attr_setguardsize(pa_ptr, stmp);
- (void) pthread_attr_getstackaddr(source, &vtmp);
- (void) pthread_attr_setstackaddr(pa_ptr, vtmp);
- (void) pthread_attr_getscope(source, &tmp);
- (void) pthread_attr_setscope(pa_ptr, tmp);
- (void) pthread_attr_getschedparam(source, &sp_tmp);
- (void) pthread_attr_setschedparam(pa_ptr, &sp_tmp);
-#ifndef GC_AIX_THREADS
- (void) pthread_attr_getprocessor_np(source, &ps_tmp, &tmp);
- (void) pthread_attr_setprocessor_np(pa_ptr, ps_tmp, tmp);
-#endif
- }
-# else
-# define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
-# endif
-
int
GC_pthread_create(pthread_t *new_thread,
const pthread_attr_t *attr,
@@ -699,85 +565,47 @@ GC_pthread_create(pthread_t *new_thread,
{
int result;
GC_thread t;
- void * stack;
- size_t stacksize;
- pthread_attr_t new_attr;
int detachstate;
word my_flags = 0;
- struct start_info * si = GC_malloc(sizeof(struct start_info));
-
- /* This is otherwise saved only in an area mmapped by the thread */
- /* library, which isn't visible to the collector. */
+ struct start_info * si;
+ /* This is otherwise saved only in an area mmapped by the thread */
+ /* library, which isn't visible to the collector. */
+ LOCK();
+ /* GC_INTERNAL_MALLOC implicitly calls GC_init() if required */
+ si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info),
+ NORMAL);
+ GC_ASSERT(GC_thr_initialized); /* initialized by GC_init() */
+ UNLOCK();
if (0 == si) return(ENOMEM);
pthread_mutex_init(&(si->registeredlock), NULL);
pthread_cond_init(&(si->registered),NULL);
+ pthread_mutex_lock(&(si->registeredlock));
si -> start_routine = start_routine;
si -> arg = arg;
- LOCK();
- if (!GC_thr_initialized) GC_thr_init();
-
- if (NULL == attr) {
- stack = 0;
- (void) pthread_attr_init(&new_attr);
- } else {
- copy_attr(&new_attr, attr);
- pthread_attr_getstackaddr(&new_attr, &stack);
- }
- pthread_attr_getstacksize(&new_attr, &stacksize);
- pthread_attr_getdetachstate(&new_attr, &detachstate);
-#ifdef GC_AIX_THREADS
- GC_min_stack_sz = 5*1048576;
- if (stacksize < GC_min_stack_sz) {
- stacksize = GC_min_stack_sz;
- }
- { int alignment = 16*1024; /* size must be multiple of 16KB greater than 56KB */
- int minval = 56*1024;
- if ((stacksize - minval) % alignment != 0) {
- stacksize = minval + alignment * ((stacksize-minval)/alignment + 1);
- }
- }
-#endif
- if (0 == stack) {
- stack = (void *)GC_stack_alloc(&stacksize);
- if (0 == stack) {
- UNLOCK();
- return(ENOMEM);
- }
- pthread_attr_setstacksize(&new_attr, stacksize);
-#ifdef GC_AIX_THREADS
- pthread_attr_setstackaddr(&new_attr, ((char *)stack)+stacksize);
-#else
- pthread_attr_setstackaddr(&new_attr, stack);
-#endif
- } else {
- my_flags |= CLIENT_OWNS_STACK;
- }
+ pthread_attr_getdetachstate(attr, &detachstate);
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
si -> flags = my_flags;
- si -> stack = stack;
- si -> stack_size = stacksize;
- result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
-
- if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) {
- GC_stack_free(stack, stacksize);
- }
- UNLOCK();
+ result = pthread_create(new_thread, attr, GC_start_routine, si);
+
/* Wait until child has been added to the thread table. */
/* This also ensures that we hold onto si until the child is done */
/* with it. Thus it doesn't matter whether it is otherwise */
/* visible to the collector. */
- si->registereddone = 0;
- pthread_mutex_lock(&(si->registeredlock));
- while (!si->registereddone)
- pthread_cond_wait(&(si->registered), &(si->registeredlock));
+ if (0 == result) {
+ si->registereddone = 0;
+ while (!si->registereddone)
+ pthread_cond_wait(&(si->registered), &(si->registeredlock));
+ }
pthread_mutex_unlock(&(si->registeredlock));
pthread_cond_destroy(&(si->registered));
pthread_mutex_destroy(&(si->registeredlock));
- pthread_attr_destroy(&new_attr);
+ LOCK();
+ GC_INTERNAL_FREE(si);
+ UNLOCK();
return(result);
}