# include "private/pthread_support.h"
# if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
&& !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
&& !defined(GC_AIX_THREADS)
# if defined(GC_HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \
&& !defined(USE_HPUX_TLS)
# define USE_HPUX_TLS
# endif
# if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
defined(GC_DARWIN_THREADS)) && !defined(USE_PTHREAD_SPECIFIC)
# define USE_PTHREAD_SPECIFIC
# endif
# if defined(GC_DGUX386_THREADS) && !defined(_POSIX4A_DRAFT10_SOURCE)
# define _POSIX4A_DRAFT10_SOURCE 1
# endif
# if defined(GC_DGUX386_THREADS) && !defined(_USING_POSIX4A_DRAFT10)
# define _USING_POSIX4A_DRAFT10 1
# endif
# ifdef THREAD_LOCAL_ALLOC
# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_HPUX_TLS)
# include "private/specific.h"
# endif
# if defined(USE_PTHREAD_SPECIFIC)
# define GC_getspecific pthread_getspecific
# define GC_setspecific pthread_setspecific
# define GC_key_create pthread_key_create
typedef pthread_key_t GC_key_t;
# endif
# if defined(USE_HPUX_TLS)
# define GC_getspecific(x) (x)
# define GC_setspecific(key, v) ((key) = (v), 0)
# define GC_key_create(key, d) 0
typedef void * GC_key_t;
# endif
# endif
# include <stdlib.h>
# include <pthread.h>
# include <sched.h>
# include <time.h>
# include <errno.h>
# include <unistd.h>
# include <sys/mman.h>
# include <sys/time.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
#if defined(GC_DARWIN_THREADS)
# include "private/darwin_semaphore.h"
#else
# include <semaphore.h>
#endif
#if defined(GC_DARWIN_THREADS)
# include <sys/sysctl.h>
#endif
#if defined(GC_DGUX386_THREADS)
# include <sys/dg_sys_info.h>
# include <sys/_int_psem.h>
typedef unsigned int sem_t;
#endif
#ifndef __GNUC__
# define __inline__
#endif
#ifdef GC_USE_LD_WRAP
# define WRAP_FUNC(f) __wrap_##f
# define REAL_FUNC(f) __real_##f
#else
# define WRAP_FUNC(f) GC_##f
# if !defined(GC_DGUX386_THREADS)
# define REAL_FUNC(f) f
# else
# define REAL_FUNC(f) __d10_##f
# endif
# undef pthread_create
# if !defined(GC_DARWIN_THREADS)
# undef pthread_sigmask
# endif
# undef pthread_join
# undef pthread_detach
# if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \
&& !defined(_PTHREAD_USE_PTDNAM_)
# define pthread_create __pthread_create
# define pthread_join __pthread_join
# define pthread_detach __pthread_detach
# endif
#endif
void GC_thr_init();
static GC_bool parallel_initialized = FALSE;
void GC_init_parallel();
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
#ifdef USE_HPUX_TLS
__thread
#endif
GC_key_t GC_thread_key;
static GC_bool keys_initialized;
static void return_freelists(ptr_t *fl, ptr_t *gfl)
{
int i;
ptr_t q, *qptr;
size_t nwords;
for (i = 1; i < NFREELISTS; ++i) {
nwords = i * (GRANULARITY/sizeof(word));
qptr = fl + i;
q = *qptr;
if ((word)q >= HBLKSIZE) {
if (gfl[nwords] == 0) {
gfl[nwords] = q;
} else {
for (; (word)q >= HBLKSIZE; qptr = &(obj_link(q)), q = *qptr);
GC_ASSERT(0 == q);
*qptr = gfl[nwords];
gfl[nwords] = fl[i];
}
}
fl[i] = (ptr_t)HBLKSIZE;
}
}
static ptr_t size_zero_object = (ptr_t)(&size_zero_object);
void GC_init_thread_local(GC_thread p)
{
int i;
if (!keys_initialized) {
if (0 != GC_key_create(&GC_thread_key, 0)) {
ABORT("Failed to create key for local allocator");
}
keys_initialized = TRUE;
}
if (0 != GC_setspecific(GC_thread_key, p)) {
ABORT("Failed to set thread specific allocation pointers");
}
for (i = 1; i < NFREELISTS; ++i) {
p -> ptrfree_freelists[i] = (ptr_t)1;
p -> normal_freelists[i] = (ptr_t)1;
# ifdef GC_GCJ_SUPPORT
p -> gcj_freelists[i] = (ptr_t)1;
# endif
}
p -> ptrfree_freelists[0] = (ptr_t)(&size_zero_object);
p -> normal_freelists[0] = (ptr_t)(&size_zero_object);
# ifdef GC_GCJ_SUPPORT
p -> gcj_freelists[0] = (ptr_t)(-1);
# endif
}
#ifdef GC_GCJ_SUPPORT
extern ptr_t * GC_gcjobjfreelist;
#endif
void GC_destroy_thread_local(GC_thread p)
{
# ifndef HANDLE_FORK
GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);
# endif
return_freelists(p -> ptrfree_freelists, GC_aobjfreelist);
return_freelists(p -> normal_freelists, GC_objfreelist);
# ifdef GC_GCJ_SUPPORT
return_freelists(p -> gcj_freelists, GC_gcjobjfreelist);
# endif
}
extern GC_PTR GC_generic_malloc_many();
GC_PTR GC_local_malloc(size_t bytes)
{
if (EXPECT(!SMALL_ENOUGH(bytes),0)) {
return(GC_malloc(bytes));
} else {
int index = INDEX_FROM_BYTES(bytes);
ptr_t * my_fl;
ptr_t my_entry;
# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC)
GC_key_t k = GC_thread_key;
# endif
void * tsd;
# if defined(REDIRECT_MALLOC) && !defined(USE_PTHREAD_SPECIFIC)
if (EXPECT(0 == k, 0)) {
GC_init_parallel();
k = GC_thread_key;
}
# endif
tsd = GC_getspecific(GC_thread_key);
# ifdef GC_ASSERTIONS
LOCK();
GC_ASSERT(tsd == (void *)GC_lookup_thread(pthread_self()));
UNLOCK();
# endif
my_fl = ((GC_thread)tsd) -> normal_freelists + index;
my_entry = *my_fl;
if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
ptr_t next = obj_link(my_entry);
GC_PTR result = (GC_PTR)my_entry;
*my_fl = next;
obj_link(my_entry) = 0;
PREFETCH_FOR_WRITE(next);
return result;
} else if ((word)my_entry - 1 < DIRECT_GRANULES) {
*my_fl = my_entry + index + 1;
return GC_malloc(bytes);
} else {
GC_generic_malloc_many(BYTES_FROM_INDEX(index), NORMAL, my_fl);
if (*my_fl == 0) return GC_oom_fn(bytes);
return GC_local_malloc(bytes);
}
}
}
GC_PTR GC_local_malloc_atomic(size_t bytes)
{
if (EXPECT(!SMALL_ENOUGH(bytes), 0)) {
return(GC_malloc_atomic(bytes));
} else {
int index = INDEX_FROM_BYTES(bytes);
ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key))
-> ptrfree_freelists + index;
ptr_t my_entry = *my_fl;
if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
GC_PTR result = (GC_PTR)my_entry;
*my_fl = obj_link(my_entry);
return result;
} else if ((word)my_entry - 1 < DIRECT_GRANULES) {
*my_fl = my_entry + index + 1;
return GC_malloc_atomic(bytes);
} else {
GC_generic_malloc_many(BYTES_FROM_INDEX(index), PTRFREE, my_fl);
if (*my_fl == 0) return GC_oom_fn(bytes);
return GC_local_malloc_atomic(bytes);
}
}
}
#ifdef GC_GCJ_SUPPORT
#include "include/gc_gcj.h"
#ifdef GC_ASSERTIONS
extern GC_bool GC_gcj_malloc_initialized;
#endif
extern int GC_gcj_kind;
GC_PTR GC_local_gcj_malloc(size_t bytes,
void * ptr_to_struct_containing_descr)
{
GC_ASSERT(GC_gcj_malloc_initialized);
if (EXPECT(!SMALL_ENOUGH(bytes), 0)) {
return GC_gcj_malloc(bytes, ptr_to_struct_containing_descr);
} else {
int index = INDEX_FROM_BYTES(bytes);
ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key))
-> gcj_freelists + index;
ptr_t my_entry = *my_fl;
if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
GC_PTR result = (GC_PTR)my_entry;
GC_ASSERT(!GC_incremental);
*(volatile ptr_t *)my_fl = obj_link(my_entry);
GC_ASSERT(((void * volatile *)result)[1] == 0);
*(void * volatile *)result = ptr_to_struct_containing_descr;
return result;
} else if ((word)my_entry - 1 < DIRECT_GRANULES) {
if (!GC_incremental) *my_fl = my_entry + index + 1;
return GC_gcj_malloc(bytes, ptr_to_struct_containing_descr);
} else {
GC_generic_malloc_many(BYTES_FROM_INDEX(index), GC_gcj_kind, my_fl);
if (*my_fl == 0) return GC_oom_fn(bytes);
return GC_local_gcj_malloc(bytes, ptr_to_struct_containing_descr);
}
}
}
#endif
# else
# define GC_destroy_thread_local(t)
# endif
#if 0
void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np;
#endif
long GC_nprocs = 1;
#ifdef PARALLEL_MARK
# ifndef MAX_MARKERS
# define MAX_MARKERS 16
# endif
static ptr_t marker_sp[MAX_MARKERS] = {0};
void * GC_mark_thread(void * id)
{
word my_mark_no = 0;
marker_sp[(word)id] = GC_approx_sp();
for (;; ++my_mark_no) {
if (my_mark_no < GC_mark_no || my_mark_no > GC_mark_no + 2) {
my_mark_no = GC_mark_no;
}
# ifdef DEBUG_THREADS
GC_printf1("Starting mark helper for mark number %ld\n", my_mark_no);
# endif
GC_help_marker(my_mark_no);
}
}
extern long GC_markers;
pthread_t GC_mark_threads[MAX_MARKERS];
#define PTHREAD_CREATE REAL_FUNC(pthread_create)
static void start_mark_threads()
{
unsigned i;
pthread_attr_t attr;
if (GC_markers > MAX_MARKERS) {
WARN("Limiting number of mark threads\n", 0);
GC_markers = MAX_MARKERS;
}
if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed");
if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))
ABORT("pthread_attr_setdetachstate failed");
# if defined(HPUX) || defined(GC_DGUX386_THREADS)
# define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word))
{
size_t old_size;
int code;
if (pthread_attr_getstacksize(&attr, &old_size) != 0)
ABORT("pthread_attr_getstacksize failed\n");
if (old_size < MIN_STACK_SIZE) {
if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0)
ABORT("pthread_attr_setstacksize failed\n");
}
}
# endif
# ifdef CONDPRINT
if (GC_print_stats) {
GC_printf1("Starting %ld marker threads\n", GC_markers - 1);
}
# endif
for (i = 0; i < GC_markers - 1; ++i) {
if (0 != PTHREAD_CREATE(GC_mark_threads + i, &attr,
GC_mark_thread, (void *)(word)i)) {
WARN("Marker thread creation failed, errno = %ld.\n", errno);
}
}
}
#else
static __inline__ void start_mark_threads()
{
}
#endif
#ifdef INSTALL_LOOPING_SEGV_HANDLER
void GC_looping_handler(int sig)
{
GC_printf3("Signal %ld in thread %lx, pid %ld\n",
sig, pthread_self(), getpid());
for (;;);
}
#endif
GC_bool GC_thr_initialized = FALSE;
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));
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
GC_push_all((ptr_t)(&GC_thread_key),
(ptr_t)(&GC_thread_key)+sizeof(&GC_thread_key));
# endif
}
#ifdef THREAD_LOCAL_ALLOC
void GC_mark_thread_local_free_lists(void)
{
int i, j;
GC_thread p;
ptr_t q;
for (i = 0; i < THREAD_TABLE_SZ; ++i) {
for (p = GC_threads[i]; 0 != p; p = p -> next) {
for (j = 1; j < NFREELISTS; ++j) {
q = p -> ptrfree_freelists[j];
if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
q = p -> normal_freelists[j];
if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
# ifdef GC_GCJ_SUPPORT
q = p -> gcj_freelists[j];
if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
# endif
}
}
}
}
#endif
static struct GC_Thread_Rep first_thread;
GC_thread GC_new_thread(pthread_t id)
{
int hv = ((word)id) % THREAD_TABLE_SZ;
GC_thread result;
static GC_bool first_thread_used = FALSE;
if (!first_thread_used) {
result = &first_thread;
first_thread_used = TRUE;
} 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;
GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0);
return(result);
}
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;
}
GC_INTERNAL_FREE(p);
}
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;
}
GC_INTERNAL_FREE(p);
}
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);
}
#ifdef HANDLE_FORK
void GC_remove_all_threads_but_me(void)
{
pthread_t self = pthread_self();
int hv;
GC_thread p, next, me;
for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
me = 0;
for (p = GC_threads[hv]; 0 != p; p = next) {
next = p -> next;
if (p -> id == self) {
me = p;
p -> next = 0;
} else {
# ifdef THREAD_LOCAL_ALLOC
if (!(p -> flags & FINISHED)) {
GC_destroy_thread_local(p);
}
# endif
if (p != &first_thread) GC_INTERNAL_FREE(p);
}
}
GC_threads[hv] = me;
}
}
#endif
#ifdef USE_PROC_FOR_LIBRARIES
int GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
{
int i;
GC_thread p;
# ifdef PARALLEL_MARK
for (i = 0; i < GC_markers; ++i) {
if (marker_sp[i] > lo & marker_sp[i] < hi) return 1;
}
# endif
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (0 != p -> stack_end) {
# ifdef STACK_GROWS_UP
if (p -> stack_end >= lo && p -> stack_end < hi) return 1;
# else
if (p -> stack_end > lo && p -> stack_end <= hi) return 1;
# endif
}
}
}
return 0;
}
#endif
#ifdef GC_LINUX_THREADS
int GC_get_nprocs()
{
# define STAT_BUF_SIZE 4096
# define STAT_READ read
char stat_buf[STAT_BUF_SIZE];
int f;
word result = 1;
size_t i, len = 0;
f = open("/proc/stat", O_RDONLY);
if (f < 0 || (len = STAT_READ(f, stat_buf, STAT_BUF_SIZE)) < 100) {
WARN("Couldn't read /proc/stat\n", 0);
return -1;
}
for (i = 0; i < len - 100; ++i) {
if (stat_buf[i] == '\n' && stat_buf[i+1] == 'c'
&& stat_buf[i+2] == 'p' && stat_buf[i+3] == 'u') {
int cpu_no = atoi(stat_buf + i + 4);
if (cpu_no >= result) result = cpu_no + 1;
}
}
close(f);
return result;
}
#endif
extern GC_bool GC_collection_in_progress();
void GC_wait_for_gc_completion(GC_bool wait_for_all)
{
if (GC_incremental && GC_collection_in_progress()) {
int old_gc_no = GC_gc_no;
while (GC_incremental && GC_collection_in_progress()
&& (wait_for_all || old_gc_no == GC_gc_no)) {
ENTER_GC();
GC_collect_a_little_inner(1);
EXIT_GC();
UNLOCK();
sched_yield();
LOCK();
}
}
}
#ifdef HANDLE_FORK
void GC_fork_prepare_proc(void)
{
LOCK();
# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
GC_wait_for_reclaim();
# endif
GC_wait_for_gc_completion(TRUE);
# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
GC_acquire_mark_lock();
# endif
}
void GC_fork_parent_proc(void)
{
# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
GC_release_mark_lock();
# endif
UNLOCK();
}
void GC_fork_child_proc(void)
{
# if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
GC_release_mark_lock();
# endif
GC_remove_all_threads_but_me();
# ifdef PARALLEL_MARK
GC_markers = 1;
GC_parallel = FALSE;
# endif
UNLOCK();
}
#endif
#if defined(GC_DGUX386_THREADS)
int GC_get_nprocs()
{
int numCpus;
struct dg_sys_info_pm_info pm_sysinfo;
int status =0;
status = dg_sys_info((long int *) &pm_sysinfo,
DG_SYS_INFO_PM_INFO_TYPE, DG_SYS_INFO_PM_CURRENT_VERSION);
if (status < 0)
numCpus = -1;
else
numCpus = pm_sysinfo.idle_vp_count;
# ifdef DEBUG_THREADS
GC_printf1("Number of active CPUs in this system: %d\n", numCpus);
# endif
return(numCpus);
}
#endif
void GC_thr_init()
{
# ifndef GC_DARWIN_THREADS
int dummy;
# endif
GC_thread t;
if (GC_thr_initialized) return;
GC_thr_initialized = TRUE;
# ifdef HANDLE_FORK
pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
GC_fork_child_proc);
# endif
t = GC_new_thread(pthread_self());
# ifdef GC_DARWIN_THREADS
t -> stop_info.mach_thread = mach_thread_self();
# else
t -> stop_info.stack_ptr = (ptr_t)(&dummy);
# endif
t -> flags = DETACHED | MAIN_THREAD;
GC_stop_init();
{
char * nprocs_string = GETENV("GC_NPROCS");
GC_nprocs = -1;
if (nprocs_string != NULL) GC_nprocs = atoi(nprocs_string);
}
if (GC_nprocs <= 0) {
# if defined(GC_HPUX_THREADS)
GC_nprocs = pthread_num_processors_np();
# endif
# if defined(GC_OSF1_THREADS)
GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN);
if (GC_nprocs <= 0) GC_nprocs = 1;
# endif
# if defined(GC_FREEBSD_THREADS)
GC_nprocs = 1;
# endif
# if defined(GC_DARWIN_THREADS)
int ncpus = 1;
size_t len = sizeof(ncpus);
sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
GC_nprocs = ncpus;
# endif
# if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
GC_nprocs = GC_get_nprocs();
# endif
}
if (GC_nprocs <= 0) {
WARN("GC_get_nprocs() returned %ld\n", GC_nprocs);
GC_nprocs = 2;
# ifdef PARALLEL_MARK
GC_markers = 1;
# endif
} else {
# ifdef PARALLEL_MARK
{
char * markers_string = GETENV("GC_MARKERS");
if (markers_string != NULL) {
GC_markers = atoi(markers_string);
} else {
GC_markers = GC_nprocs;
}
}
# endif
}
# ifdef PARALLEL_MARK
# ifdef CONDPRINT
if (GC_print_stats) {
GC_printf2("Number of processors = %ld, "
"number of marker threads = %ld\n", GC_nprocs, GC_markers);
}
# endif
if (GC_markers == 1) {
GC_parallel = FALSE;
# ifdef CONDPRINT
if (GC_print_stats) {
GC_printf0("Single marker thread, turning off parallel marking\n");
}
# endif
} else {
GC_parallel = TRUE;
GC_time_limit = GC_TIME_UNLIMITED;
}
# endif
}
void GC_init_parallel()
{
if (parallel_initialized) return;
parallel_initialized = TRUE;
if (!GC_is_initialized) GC_init();
# ifdef PARALLEL_MARK
if (GC_parallel) start_mark_threads();
# endif
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
LOCK();
GC_init_thread_local(GC_lookup_thread(pthread_self()));
UNLOCK();
# endif
}
#if !defined(GC_DARWIN_THREADS)
int WRAP_FUNC(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(REAL_FUNC(pthread_sigmask)(how, set, oset));
}
#endif
void GC_start_blocking(void) {
# define SP_SLOP 128
GC_thread me;
LOCK();
me = GC_lookup_thread(pthread_self());
GC_ASSERT(!(me -> thread_blocked));
# ifdef SPARC
me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack();
# else
# ifndef GC_DARWIN_THREADS
me -> stop_info.stack_ptr = (ptr_t)GC_approx_sp();
# endif
# endif
# ifdef IA64
me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack() + SP_SLOP;
# endif
# ifndef GC_DARWIN_THREADS
# ifdef STACK_GROWS_UP
me -> stop_info.stack_ptr += SP_SLOP;
# else
me -> stop_info.stack_ptr -= SP_SLOP;
# endif
# endif
me -> thread_blocked = TRUE;
UNLOCK();
}
void GC_end_blocking(void) {
GC_thread me;
LOCK();
me = GC_lookup_thread(pthread_self());
GC_ASSERT(me -> thread_blocked);
me -> thread_blocked = FALSE;
UNLOCK();
}
#if defined(GC_DGUX386_THREADS)
#define __d10_sleep sleep
#endif
int WRAP_FUNC(sleep) (unsigned int seconds)
{
int result;
GC_start_blocking();
result = REAL_FUNC(sleep)(seconds);
GC_end_blocking();
return result;
}
struct start_info {
void *(*start_routine)(void *);
void *arg;
word flags;
sem_t registered;
};
void GC_thread_exit_proc(void *arg)
{
GC_thread me;
LOCK();
me = GC_lookup_thread(pthread_self());
GC_destroy_thread_local(me);
if (me -> flags & DETACHED) {
GC_delete_thread(pthread_self());
} else {
me -> flags |= FINISHED;
}
# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_SPECIFIC) \
&& !defined(USE_HPUX_TLS) && !defined(DBG_HDRS_ALL)
GC_remove_specific(GC_thread_key);
# endif
GC_wait_for_gc_completion(FALSE);
UNLOCK();
}
int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval)
{
int result;
GC_thread thread_gc_id;
LOCK();
thread_gc_id = GC_lookup_thread(thread);
UNLOCK();
result = REAL_FUNC(pthread_join)(thread, retval);
# if defined (GC_FREEBSD_THREADS)
if (result == EINTR) result = 0;
# endif
if (result == 0) {
LOCK();
GC_delete_gc_thread(thread, thread_gc_id);
UNLOCK();
}
return result;
}
int
WRAP_FUNC(pthread_detach)(pthread_t thread)
{
int result;
GC_thread thread_gc_id;
LOCK();
thread_gc_id = GC_lookup_thread(thread);
UNLOCK();
result = REAL_FUNC(pthread_detach)(thread);
if (result == 0) {
LOCK();
thread_gc_id -> flags |= DETACHED;
if (thread_gc_id -> flags & FINISHED) {
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();
# 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();
me = GC_new_thread(my_pthread);
#ifdef GC_DARWIN_THREADS
me -> stop_info.mach_thread = mach_thread_self();
#else
me -> stop_info.stack_ptr = 0;
#endif
me -> flags = si -> flags;
# ifdef STACK_GROWS_DOWN
me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1))
& ~(GC_page_size - 1));
# ifndef GC_DARWIN_THREADS
me -> stop_info.stack_ptr = me -> stack_end - 0x10;
# endif
# else
me -> stack_end = (ptr_t)((word)(&dummy) & ~(GC_page_size - 1));
me -> stop_info.stack_ptr = me -> stack_end + 0x10;
# endif
# ifdef IA64
me -> backing_store_end = (ptr_t)
(GC_save_regs_in_stack() & ~(GC_page_size - 1));
# endif
UNLOCK();
start = si -> start_routine;
# ifdef DEBUG_THREADS
GC_printf1("start_routine = 0x%lx\n", start);
# endif
start_arg = si -> arg;
sem_post(&(si -> registered));
pthread_cleanup_push(GC_thread_exit_proc, 0);
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
LOCK();
GC_init_thread_local(me);
UNLOCK();
# endif
result = (*start)(start_arg);
#if DEBUG_THREADS
GC_printf1("Finishing thread 0x%x\n", pthread_self());
#endif
me -> status = result;
pthread_cleanup_pop(1);
return(result);
}
int
WRAP_FUNC(pthread_create)(pthread_t *new_thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg)
{
int result;
int detachstate;
word my_flags = 0;
struct start_info * si;
LOCK();
si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info),
NORMAL);
UNLOCK();
if (!parallel_initialized) GC_init_parallel();
if (0 == si) return(ENOMEM);
sem_init(&(si -> registered), 0, 0);
si -> start_routine = start_routine;
si -> arg = arg;
LOCK();
if (!GC_thr_initialized) GC_thr_init();
# ifdef GC_ASSERTIONS
{
int stack_size;
if (NULL == attr) {
pthread_attr_t my_attr;
pthread_attr_init(&my_attr);
pthread_attr_getstacksize(&my_attr, &stack_size);
} else {
pthread_attr_getstacksize(attr, &stack_size);
}
GC_ASSERT(stack_size >= (8*HBLKSIZE*sizeof(word)));
}
# endif
if (NULL == attr) {
detachstate = PTHREAD_CREATE_JOINABLE;
} else {
pthread_attr_getdetachstate(attr, &detachstate);
}
if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
si -> flags = my_flags;
UNLOCK();
# 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, attr, GC_start_routine, si);
# ifdef DEBUG_THREADS
GC_printf1("Started thread 0x%X\n", *new_thread);
# endif
if (0 == result) {
while (0 != sem_wait(&(si -> registered))) {
if (EINTR != errno) ABORT("sem_wait failed");
}
}
sem_destroy(&(si -> registered));
LOCK();
GC_INTERNAL_FREE(si);
UNLOCK();
return(result);
}
#ifdef GENERIC_COMPARE_AND_SWAP
pthread_mutex_t GC_compare_and_swap_lock = PTHREAD_MUTEX_INITIALIZER;
GC_bool GC_compare_and_exchange(volatile GC_word *addr,
GC_word old, GC_word new_val)
{
GC_bool result;
pthread_mutex_lock(&GC_compare_and_swap_lock);
if (*addr == old) {
*addr = new_val;
result = TRUE;
} else {
result = FALSE;
}
pthread_mutex_unlock(&GC_compare_and_swap_lock);
return result;
}
GC_word GC_atomic_add(volatile GC_word *addr, GC_word how_much)
{
GC_word old;
pthread_mutex_lock(&GC_compare_and_swap_lock);
old = *addr;
*addr = old + how_much;
pthread_mutex_unlock(&GC_compare_and_swap_lock);
return old;
}
#endif
void GC_pause()
{
int i;
# ifndef __GNUC__
volatile word dummy = 0;
# endif
for (i = 0; i < 10; ++i) {
# ifdef __GNUC__
__asm__ __volatile__ (" " : : : "memory");
# else
GC_noop(++dummy);
# endif
}
}
#define SPIN_MAX 1024
VOLATILE GC_bool GC_collecting = 0;
#if !defined(USE_SPIN_LOCK) || defined(PARALLEL_MARK)
void GC_generic_lock(pthread_mutex_t * lock)
{
#ifndef NO_PTHREAD_TRYLOCK
unsigned pause_length = 1;
unsigned i;
if (0 == pthread_mutex_trylock(lock)) return;
for (; pause_length <= SPIN_MAX; pause_length <<= 1) {
for (i = 0; i < pause_length; ++i) {
GC_pause();
}
switch(pthread_mutex_trylock(lock)) {
case 0:
return;
case EBUSY:
break;
default:
ABORT("Unexpected error from pthread_mutex_trylock");
}
}
#endif
pthread_mutex_lock(lock);
}
#endif
#if defined(USE_SPIN_LOCK)
volatile unsigned int GC_allocate_lock = 0;
void GC_lock()
{
# define low_spin_max 30
# define high_spin_max SPIN_MAX
static unsigned spin_max = low_spin_max;
unsigned my_spin_max;
static unsigned last_spins = 0;
unsigned my_last_spins;
int i;
if (!GC_test_and_set(&GC_allocate_lock)) {
return;
}
my_spin_max = spin_max;
my_last_spins = last_spins;
for (i = 0; i < my_spin_max; i++) {
if (GC_collecting || GC_nprocs == 1) goto yield;
if (i < my_last_spins/2 || GC_allocate_lock) {
GC_pause();
continue;
}
if (!GC_test_and_set(&GC_allocate_lock)) {
last_spins = i;
spin_max = high_spin_max;
return;
}
}
spin_max = low_spin_max;
yield:
for (i = 0;; ++i) {
if (!GC_test_and_set(&GC_allocate_lock)) {
return;
}
# define SLEEP_THRESHOLD 12
if (i < SLEEP_THRESHOLD) {
sched_yield();
} else {
struct timespec ts;
if (i > 24) i = 24;
ts.tv_sec = 0;
ts.tv_nsec = 1 << i;
nanosleep(&ts, 0);
}
}
}
#else
void GC_lock()
{
#ifndef NO_PTHREAD_TRYLOCK
if (1 == GC_nprocs || GC_collecting) {
pthread_mutex_lock(&GC_allocate_ml);
} else {
GC_generic_lock(&GC_allocate_ml);
}
#else
pthread_mutex_lock(&GC_allocate_ml);
#endif
}
#endif
#if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC)
#ifdef GC_ASSERTIONS
pthread_t GC_mark_lock_holder = NO_THREAD;
#endif
#if 0
static pthread_mutex_t mark_mutex =
{0, 0, 0, PTHREAD_MUTEX_ERRORCHECK_NP, {0, 0}};
#else
static pthread_mutex_t mark_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
static pthread_cond_t builder_cv = PTHREAD_COND_INITIALIZER;
void GC_acquire_mark_lock()
{
GC_generic_lock(&mark_mutex);
# ifdef GC_ASSERTIONS
GC_mark_lock_holder = pthread_self();
# endif
}
void GC_release_mark_lock()
{
GC_ASSERT(GC_mark_lock_holder == pthread_self());
# ifdef GC_ASSERTIONS
GC_mark_lock_holder = NO_THREAD;
# endif
if (pthread_mutex_unlock(&mark_mutex) != 0) {
ABORT("pthread_mutex_unlock failed");
}
}
void GC_wait_builder()
{
GC_ASSERT(GC_mark_lock_holder == pthread_self());
# ifdef GC_ASSERTIONS
GC_mark_lock_holder = NO_THREAD;
# endif
if (pthread_cond_wait(&builder_cv, &mark_mutex) != 0) {
ABORT("pthread_cond_wait failed");
}
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
# ifdef GC_ASSERTIONS
GC_mark_lock_holder = pthread_self();
# endif
}
void GC_wait_for_reclaim()
{
GC_acquire_mark_lock();
while (GC_fl_builder_count > 0) {
GC_wait_builder();
}
GC_release_mark_lock();
}
void GC_notify_all_builder()
{
GC_ASSERT(GC_mark_lock_holder == pthread_self());
if (pthread_cond_broadcast(&builder_cv) != 0) {
ABORT("pthread_cond_broadcast failed");
}
}
#endif
#ifdef PARALLEL_MARK
static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
void GC_wait_marker()
{
GC_ASSERT(GC_mark_lock_holder == pthread_self());
# ifdef GC_ASSERTIONS
GC_mark_lock_holder = NO_THREAD;
# endif
if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) {
ABORT("pthread_cond_wait failed");
}
GC_ASSERT(GC_mark_lock_holder == NO_THREAD);
# ifdef GC_ASSERTIONS
GC_mark_lock_holder = pthread_self();
# endif
}
void GC_notify_all_marker()
{
if (pthread_cond_broadcast(&mark_cv) != 0) {
ABORT("pthread_cond_broadcast failed");
}
}
#endif
# endif