aix_irix_threads.c [plain text]
# include "private/gc_priv.h"
# if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)
# include <pthread.h>
# include <assert.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
#if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)
#define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
#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
typedef struct GC_Thread_Rep {
struct GC_Thread_Rep * next;
pthread_t id;
word stop;
# define NOT_STOPPED 0
# define PLEASE_STOP 1
# define STOPPED 2
word flags;
# define FINISHED 1
# define DETACHED 2
ptr_t stack_cold;
ptr_t stack_hot;
void * status;
} * GC_thread;
GC_thread GC_lookup_thread(pthread_t id);
#if 0
# if defined(GC_AIX_THREADS)
# define SIG_SUSPEND SIGUSR1
# else
# define SIG_SUSPEND (SIGRTMIN + 6)
# endif
#endif
pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
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());
if (PLEASE_STOP != me -> stop) {
pthread_mutex_unlock(&GC_suspend_lock);
return;
}
pthread_mutex_lock(&GC_suspend_lock);
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);
pthread_mutex_unlock(&GC_suspend_lock);
}
GC_bool GC_thr_initialized = FALSE;
# define THREAD_TABLE_SZ 128
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));
}
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;
GC_ASSERT(I_HOLD_LOCK());
if (!first_thread_used) {
result = &first_thread;
first_thread_used = TRUE;
} else {
result = (struct GC_Thread_Rep *)
GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
}
if (result == 0) return(0);
result -> id = id;
result -> next = GC_threads[hv];
GC_threads[hv] = result;
return(result);
}
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;
GC_ASSERT(I_HOLD_LOCK());
while (p != gc_id) {
prev = p;
p = p -> next;
}
if (prev == 0) {
GC_threads[hv] = p -> next;
} else {
prev -> next = p -> next;
}
}
GC_thread GC_lookup_thread(pthread_t id)
{
int hv = ((word)id) % THREAD_TABLE_SZ;
register GC_thread p = GC_threads[hv];
#if defined(GC_AIX_THREADS)
GC_ASSERT(I_HOLD_LOCK());
#endif
while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
return(p);
}
#if defined(GC_AIX_THREADS)
void GC_stop_world()
{
pthread_t my_thread = pthread_self();
register int i;
register GC_thread p;
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) {
pthread_suspend_np(p->id);
}
}
}
}
void GC_start_world()
{
GC_thread p;
unsigned i;
pthread_t my_thread = pthread_self();
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) {
pthread_continue_np(p->id);
}
}
}
}
#else
void GC_stop_world()
{
pthread_t my_thread = pthread_self();
register int i;
register GC_thread p;
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) {
if (p -> flags & FINISHED) {
p -> stop = STOPPED;
continue;
}
p -> stop = PLEASE_STOP;
result = pthread_kill(p -> id, SIG_SUSPEND);
switch(result) {
case ESRCH:
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;
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) {
result = pthread_kill(p -> id, SIG_SUSPEND);
}
}
}
}
pthread_mutex_unlock(&GC_suspend_lock);
}
void GC_start_world()
{
GC_thread p;
unsigned i;
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;
}
}
pthread_mutex_lock(&GC_suspend_lock);
pthread_mutex_unlock(&GC_suspend_lock);
pthread_cond_broadcast(&GC_continue_cv);
}
#endif
void GC_push_all_stacks()
{
register int i;
register GC_thread p;
register ptr_t hot, cold;
pthread_t me = pthread_self();
GC_ASSERT(GC_thr_initialized);
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;
if (pthread_equal(p -> id, me)) {
hot = GC_approx_sp();
} else {
# ifdef GC_AIX_THREADS
pthread_t id = p -> id;
struct __pthrdsinfo pinfo;
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();
}
hot = (ptr_t)(unsigned long)pinfo.__pi_ustk-288;
cold = (ptr_t)pinfo.__pi_stackend;
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
}
# ifdef STACK_GROWS_UP
GC_push_all_stack(cold, hot);
# else
GC_push_all_stack(hot, cold);
# endif
}
}
}
void GC_thr_init()
{
GC_thread t;
struct sigaction act;
if (GC_thr_initialized) return;
GC_ASSERT(I_HOLD_LOCK());
GC_thr_initialized = TRUE;
#ifndef GC_AIX_THREADS
(void) sigaction(SIG_SUSPEND, 0, &act);
if (act.sa_handler != SIG_DFL)
ABORT("Previously installed SIG_SUSPEND 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");
#endif
t = GC_new_thread(pthread_self());
t -> stack_cold = 0;
t -> stack_hot = (ptr_t)(&t);
t -> flags = DETACHED;
}
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
{
sigset_t fudged_set;
#ifdef GC_AIX_THREADS
return(pthread_sigmask(how, set, oset));
#endif
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;
pthread_mutex_t registeredlock;
pthread_cond_t registered;
int volatile registereddone;
};
void GC_thread_exit_proc(void *arg)
{
GC_thread me;
LOCK();
me = GC_lookup_thread(pthread_self());
me -> flags |= FINISHED;
if (me -> flags & DETACHED) {
GC_delete_gc_thread(pthread_self(), me);
}
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);
UNLOCK();
GC_ASSERT(!(thread_gc_id->flags & DETACHED));
result = pthread_join(thread, retval);
if (EINTR == result) result = 0;
GC_ASSERT(thread_gc_id->flags & FINISHED);
LOCK();
GC_delete_gc_thread(thread, thread_gc_id);
UNLOCK();
return result;
}
void * GC_start_routine(void * arg)
{
int dummy;
struct start_info * si = arg;
void * result;
GC_thread me;
pthread_t my_pthread;
void *(*start)(void *);
void *start_arg;
my_pthread = pthread_self();
LOCK();
me = GC_new_thread(my_pthread);
me -> flags = si -> flags;
me -> stack_cold = (ptr_t) &dummy;
me -> stack_hot = me->stack_cold;
UNLOCK();
start = si -> start_routine;
start_arg = si -> arg;
pthread_mutex_lock(&(si->registeredlock));
si->registereddone = 1;
pthread_cond_signal(&(si->registered));
pthread_mutex_unlock(&(si->registeredlock));
pthread_cleanup_push(GC_thread_exit_proc, 0);
result = (*start)(start_arg);
me -> status = result;
pthread_cleanup_pop(1);
return(result);
}
int
GC_pthread_create(pthread_t *new_thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
int result;
GC_thread t;
int detachstate;
word my_flags = 0;
struct start_info * si;
LOCK();
si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info),
NORMAL);
GC_ASSERT(GC_thr_initialized);
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;
pthread_attr_getdetachstate(attr, &detachstate);
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
si -> flags = my_flags;
result = pthread_create(new_thread, attr, GC_start_routine, si);
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));
LOCK();
GC_INTERNAL_FREE(si);
UNLOCK();
return(result);
}
VOLATILE GC_bool GC_collecting = 0;
#define SLEEP_THRESHOLD 3
volatile unsigned int 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
# define high_spin_max 1000
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()) {
last_spins = i;
spin_max = high_spin_max;
return;
}
}
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;
ts.tv_sec = 0;
ts.tv_nsec = 1 << i;
nanosleep(&ts, 0);
}
}
}
# else
#ifndef LINT
int GC_no_Irix_threads;
#endif
# endif