aboutsummaryrefslogtreecommitdiffstats
path: root/gc/irix_threads.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gc/irix_threads.c730
1 files changed, 0 insertions, 730 deletions
diff --git a/gc/irix_threads.c b/gc/irix_threads.c
deleted file mode 100644
index 48173e4..0000000
--- a/gc/irix_threads.c
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
- * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
- * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
- *
- * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
- * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
- *
- * Permission is hereby granted to use or copy this program
- * for any purpose, provided the above notices are retained on all copies.
- * Permission to modify the code and to distribute modified code is granted,
- * provided the above notices are retained, and a notice that the code was
- * modified is included with the above copyright notice.
- */
-/*
- * Support code for Irix (>=6.2) Pthreads. This relies on properties
- * not guaranteed by the Pthread standard. It may or may not be portable
- * to other implementations.
- *
- * This now also includes an initial attempt at thread support for
- * HP/UX 11.
- *
- * Note that there is a lot of code duplication between linux_threads.c
- * and irix_threads.c; any changes made here may need to be reflected
- * there too.
- */
-
-# if defined(GC_IRIX_THREADS)
-
-# include "private/gc_priv.h"
-# include <pthread.h>
-# include <semaphore.h>
-# include <time.h>
-# include <errno.h>
-# include <unistd.h>
-# include <sys/mman.h>
-# include <sys/time.h>
-
-#undef pthread_create
-#undef pthread_sigmask
-#undef pthread_join
-#undef pthread_detach
-
-#ifdef HANDLE_FORK
- --> Not yet supported. Try porting the code from linux_threads.c.
-#endif
-
-void GC_thr_init();
-
-#if 0
-void GC_print_sig_mask()
-{
- sigset_t blocked;
- int i;
-
- if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
- ABORT("pthread_sigmask");
- GC_printf0("Blocked: ");
- for (i = 1; i <= MAXSIG; i++) {
- if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
- }
- GC_printf0("\n");
-}
-#endif
-
-/* We use the allocation lock to protect thread-related data structures. */
-
-/* The set of all known threads. We intercept thread creation and */
-/* joins. We never actually create detached threads. We allocate all */
-/* new thread stacks ourselves. These allow us to maintain this */
-/* data structure. */
-/* Protected by GC_thr_lock. */
-/* Some of this should be declared volatile, but that's incosnsistent */
-/* with some library routine declarations. */
-typedef struct GC_Thread_Rep {
- struct GC_Thread_Rep * next; /* More recently allocated threads */
- /* with a given pthread id come */
- /* first. (All but the first are */
- /* guaranteed to be dead, but we may */
- /* not yet have registered the join.) */
- pthread_t id;
- word stop;
-# define NOT_STOPPED 0
-# define PLEASE_STOP 1
-# define STOPPED 2
- 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. */
- /* 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. */
-} * GC_thread;
-
-GC_thread GC_lookup_thread(pthread_t id);
-
-/*
- * The only way to suspend threads given the pthread interface is to send
- * signals. Unfortunately, this means we have to reserve
- * a signal, and intercept client calls to change the signal mask.
- * We use SIG_SUSPEND, defined in gc_priv.h.
- */
-
-pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
- /* Number of threads stopped so far */
-pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
-pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
-
-void GC_suspend_handler(int sig)
-{
- int dummy;
- GC_thread me;
- sigset_t all_sigs;
- sigset_t old_sigs;
- int i;
-
- if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
- me = GC_lookup_thread(pthread_self());
- /* The lookup here is safe, since I'm doing this on behalf */
- /* of a thread which holds the allocation lock in order */
- /* to stop the world. Thus concurrent modification of the */
- /* data structure is impossible. */
- if (PLEASE_STOP != me -> stop) {
- /* Misdirected signal. */
- pthread_mutex_unlock(&GC_suspend_lock);
- return;
- }
- pthread_mutex_lock(&GC_suspend_lock);
- me -> stack_ptr = (ptr_t)(&dummy);
- me -> stop = STOPPED;
- pthread_cond_signal(&GC_suspend_ack_cv);
- pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
- pthread_mutex_unlock(&GC_suspend_lock);
- /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
-}
-
-
-GC_bool GC_thr_initialized = FALSE;
-
-size_t GC_min_stack_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_size);
- result = (ptr_t)(((word)result + GC_page_size) & ~(GC_page_size - 1));
- /* Protect hottest page to detect overflow. */
-# ifdef STACK_GROWS_UP
- /* mprotect(result + search_sz, GC_page_size, PROT_NONE); */
-# else
- /* mprotect(result, GC_page_size, PROT_NONE); */
- result += GC_page_size;
-# 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];
-
-void GC_push_thread_structures GC_PROTO((void))
-{
- GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
-}
-
-/* Add a thread to GC_threads. We assume it wasn't already there. */
-/* Caller holds allocation lock. */
-GC_thread GC_new_thread(pthread_t id)
-{
- int hv = ((word)id) % THREAD_TABLE_SZ;
- GC_thread result;
- static struct GC_Thread_Rep first_thread;
- static GC_bool first_thread_used = FALSE;
-
- if (!first_thread_used) {
- result = &first_thread;
- first_thread_used = TRUE;
- /* Dont acquire allocation lock, since we may already hold it. */
- } else {
- result = (struct GC_Thread_Rep *)
- GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
- }
- if (result == 0) return(0);
- result -> id = id;
- result -> next = GC_threads[hv];
- GC_threads[hv] = result;
- /* result -> flags = 0; */
- /* result -> stop = 0; */
- return(result);
-}
-
-/* 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 */
-/* 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. */
-void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
-{
- int hv = ((word)id) % THREAD_TABLE_SZ;
- register GC_thread p = GC_threads[hv];
- register GC_thread prev = 0;
-
- while (p != gc_id) {
- prev = p;
- p = p -> next;
- }
- if (prev == 0) {
- GC_threads[hv] = p -> next;
- } else {
- prev -> next = p -> next;
- }
-}
-
-/* Return a GC_thread corresponding to a given thread_t. */
-/* Returns 0 if it's not there. */
-/* Caller holds allocation lock or otherwise inhibits */
-/* updates. */
-/* If there is more than one thread with the given id we */
-/* return the most recent one. */
-GC_thread GC_lookup_thread(pthread_t id)
-{
- int hv = ((word)id) % THREAD_TABLE_SZ;
- register GC_thread p = GC_threads[hv];
-
- while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
- return(p);
-}
-
-
-/* Caller holds allocation lock. */
-void GC_stop_world()
-{
- pthread_t my_thread = pthread_self();
- register int i;
- register GC_thread p;
- register int result;
- struct timespec timeout;
-
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> id != my_thread) {
- if (p -> flags & FINISHED) {
- p -> stop = STOPPED;
- continue;
- }
- p -> stop = PLEASE_STOP;
- result = pthread_kill(p -> id, SIG_SUSPEND);
- /* GC_printf1("Sent signal to 0x%x\n", p -> id); */
- switch(result) {
- case ESRCH:
- /* Not really there anymore. Possible? */
- p -> stop = STOPPED;
- break;
- case 0:
- break;
- default:
- ABORT("pthread_kill failed");
- }
- }
- }
- }
- pthread_mutex_lock(&GC_suspend_lock);
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- while (p -> id != my_thread && p -> stop != STOPPED) {
- clock_gettime(CLOCK_REALTIME, &timeout);
- timeout.tv_nsec += 50000000; /* 50 msecs */
- if (timeout.tv_nsec >= 1000000000) {
- timeout.tv_nsec -= 1000000000;
- ++timeout.tv_sec;
- }
- result = pthread_cond_timedwait(&GC_suspend_ack_cv,
- &GC_suspend_lock,
- &timeout);
- if (result == ETIMEDOUT) {
- /* Signal was lost or misdirected. Try again. */
- /* Duplicate signals should be benign. */
- result = pthread_kill(p -> id, SIG_SUSPEND);
- }
- }
- }
- }
- pthread_mutex_unlock(&GC_suspend_lock);
- /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
-}
-
-/* Caller holds allocation lock. */
-void GC_start_world()
-{
- GC_thread p;
- unsigned i;
-
- /* GC_printf0("World starting\n"); */
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- p -> stop = NOT_STOPPED;
- }
- }
- pthread_mutex_lock(&GC_suspend_lock);
- /* All other threads are at pthread_cond_wait in signal handler. */
- /* Otherwise we couldn't have acquired the lock. */
- pthread_mutex_unlock(&GC_suspend_lock);
- pthread_cond_broadcast(&GC_continue_cv);
-}
-
-# 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. */
-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_printf1("Pushing stacks from thread 0x%x\n", me); */
- for (i = 0; i < THREAD_TABLE_SZ; i++) {
- for (p = GC_threads[i]; p != 0; p = p -> next) {
- if (p -> flags & FINISHED) continue;
- if (pthread_equal(p -> id, me)) {
- hot = GC_approx_sp();
- } else {
- hot = p -> stack_ptr;
- }
- 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
- GC_push_all_stack(hot, cold);
-# endif
- }
- }
-}
-
-
-/* We hold the allocation lock. */
-void GC_thr_init()
-{
- GC_thread t;
- struct sigaction act;
-
- if (GC_thr_initialized) return;
- GC_thr_initialized = TRUE;
- GC_min_stack_sz = HBLKSIZE;
- (void) sigaction(SIG_SUSPEND, 0, &act);
- if (act.sa_handler != SIG_DFL)
- ABORT("Previously installed SIG_SUSPEND handler");
- /* Install handler. */
- act.sa_handler = GC_suspend_handler;
- act.sa_flags = SA_RESTART;
- (void) sigemptyset(&act.sa_mask);
- if (0 != sigaction(SIG_SUSPEND, &act, 0))
- ABORT("Failed to install SIG_SUSPEND handler");
- /* 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);
- t -> flags = DETACHED;
-}
-
-int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
-{
- sigset_t fudged_set;
-
- if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
- fudged_set = *set;
- sigdelset(&fudged_set, SIG_SUSPEND);
- set = &fudged_set;
- }
- return(pthread_sigmask(how, set, oset));
-}
-
-struct start_info {
- void *(*start_routine)(void *);
- void *arg;
- word flags;
- ptr_t stack;
- size_t stack_size;
- sem_t registered; /* 1 ==> in our thread table, but */
- /* parent hasn't yet noticed. */
-};
-
-void GC_thread_exit_proc(void *arg)
-{
- GC_thread me;
-
- LOCK();
- me = GC_lookup_thread(pthread_self());
- if (me -> flags & DETACHED) {
- GC_delete_thread(pthread_self());
- } else {
- me -> flags |= FINISHED;
- }
- UNLOCK();
-}
-
-int GC_pthread_join(pthread_t thread, void **retval)
-{
- int result;
- GC_thread thread_gc_id;
-
- LOCK();
- thread_gc_id = GC_lookup_thread(thread);
- /* This is guaranteed to be the intended one, since the thread id */
- /* cant have been recycled by pthreads. */
- UNLOCK();
- 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;
- if (result == 0) {
- LOCK();
- /* Here the pthread thread id may have been recycled. */
- GC_delete_gc_thread(thread, thread_gc_id);
- UNLOCK();
- }
- return result;
-}
-
-int GC_pthread_detach(pthread_t thread)
-{
- int result;
- GC_thread thread_gc_id;
-
- LOCK();
- thread_gc_id = GC_lookup_thread(thread);
- UNLOCK();
- result = pthread_detach(thread);
- if (result == 0) {
- LOCK();
- thread_gc_id -> flags |= DETACHED;
- /* Here the pthread thread id may have been recycled. */
- if (thread_gc_id -> flags & FINISHED) {
- GC_delete_gc_thread(thread, thread_gc_id);
- }
- UNLOCK();
- }
- return result;
-}
-
-void * GC_start_routine(void * arg)
-{
- struct start_info * si = arg;
- void * result;
- GC_thread me;
- pthread_t my_pthread;
- void *(*start)(void *);
- void *start_arg;
-
- my_pthread = pthread_self();
- /* If a GC occurs before the thread is registered, that GC will */
- /* ignore this thread. That's fine, since it will block trying to */
- /* acquire the allocation lock, and won't yet hold interesting */
- /* pointers. */
- LOCK();
- /* We register the thread here instead of in the parent, so that */
- /* we don't need to hold the allocation lock during pthread_create. */
- /* Holding the allocation lock there would make REDIRECT_MALLOC */
- /* impossible. It probably still doesn't work, but we're a little */
- /* closer ... */
- /* This unfortunately means that we have to be careful the parent */
- /* 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;
- me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
- UNLOCK();
- start = si -> start_routine;
- start_arg = si -> arg;
- sem_post(&(si -> registered));
- pthread_cleanup_push(GC_thread_exit_proc, 0);
- result = (*start)(start_arg);
- me -> status = result;
- me -> flags |= FINISHED;
- pthread_cleanup_pop(1);
- /* This involves acquiring the lock, ensuring that we can't exit */
- /* while a collection that thinks we're alive is trying to stop */
- /* us. */
- return(result);
-}
-
-# define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
-
-int
-GC_pthread_create(pthread_t *new_thread,
- const pthread_attr_t *attr,
- void *(*start_routine)(void *), void *arg)
-{
- 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. */
-
- if (0 == si) return(ENOMEM);
- if (0 != sem_init(&(si -> registered), 0, 0)) {
- ABORT("sem_init failed");
- }
- si -> start_routine = start_routine;
- si -> arg = arg;
- LOCK();
- if (!GC_is_initialized) GC_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);
- if (stacksize < GC_min_stack_sz) ABORT("Stack too small");
- if (0 == stack) {
- stack = (void *)GC_stack_alloc(&stacksize);
- if (0 == stack) {
- UNLOCK();
- return(ENOMEM);
- }
- pthread_attr_setstackaddr(&new_attr, stack);
- } else {
- my_flags |= CLIENT_OWNS_STACK;
- }
- 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();
- /* 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. */
- while (0 != sem_wait(&(si -> registered))) {
- if (errno != EINTR) {
- GC_printf1("Sem_wait: errno = %ld\n", (unsigned long) errno);
- ABORT("sem_wait failed");
- }
- }
- sem_destroy(&(si -> registered));
- pthread_attr_destroy(&new_attr); /* Probably unnecessary under Irix */
- return(result);
-}
-
-VOLATILE GC_bool GC_collecting = 0;
- /* A hint that we're in the collector and */
- /* holding the allocation lock for an */
- /* extended period. */
-
-/* Reasonably fast spin locks. Basically the same implementation */
-/* as STL alloc.h. */
-
-#define SLEEP_THRESHOLD 3
-
-unsigned long GC_allocate_lock = 0;
-# define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
-# define GC_LOCK_TAKEN GC_allocate_lock
-
-void GC_lock()
-{
-# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
-# define high_spin_max 1000 /* spin cycles for multiprocessor */
- static unsigned spin_max = low_spin_max;
- unsigned my_spin_max;
- static unsigned last_spins = 0;
- unsigned my_last_spins;
- volatile unsigned junk;
-# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
- int i;
-
- if (GC_TRY_LOCK()) {
- return;
- }
- junk = 0;
- my_spin_max = spin_max;
- my_last_spins = last_spins;
- for (i = 0; i < my_spin_max; i++) {
- if (GC_collecting) goto yield;
- if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
- PAUSE;
- continue;
- }
- if (GC_TRY_LOCK()) {
- /*
- * got it!
- * Spinning worked. Thus we're probably not being scheduled
- * against the other process with which we were contending.
- * Thus it makes sense to spin longer the next time.
- */
- last_spins = i;
- spin_max = high_spin_max;
- return;
- }
- }
- /* We are probably being scheduled against the other process. Sleep. */
- spin_max = low_spin_max;
-yield:
- for (i = 0;; ++i) {
- if (GC_TRY_LOCK()) {
- return;
- }
- if (i < SLEEP_THRESHOLD) {
- sched_yield();
- } else {
- struct timespec ts;
-
- if (i > 26) i = 26;
- /* Don't wait for more than about 60msecs, even */
- /* under extreme contention. */
- ts.tv_sec = 0;
- ts.tv_nsec = 1 << i;
- nanosleep(&ts, 0);
- }
- }
-}
-
-# else
-
-#ifndef LINT
- int GC_no_Irix_threads;
-#endif
-
-# endif /* GC_IRIX_THREADS */
-