$OpenBSD$
--- src/boehm-gc/openbsd_pthreads.c.orig	Wed Apr 26 18:58:28 2006
+++ src/boehm-gc/openbsd_pthreads.c	Wed Apr 26 19:04:55 2006
@@ -0,0 +1,462 @@
+#include "config.h"
+#include "private/gc_priv.h"
+
+#if 0
+#if defined(GC_OPENBSD_PTHREADS) 
+
+#include <errno.h>
+#include <pthread_np.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <sys/signal.h>
+
+#undef GLOBAL_PTHREAD_PRIVATE
+#include "pthread_private.h"
+
+#define THREAD_TABLE_SZ	128
+
+GC_bool GC_thr_initialized = 0;
+
+static GC_bool parallel_initialized = 0;
+
+extern ptr_t GC_find_limit(ptr_t p, GC_bool up);
+
+#ifdef DEBUG_THREADS
+#define	DEBUG_GC_printf0(a)	GC_printf0(a)
+#define	DEBUG_GC_printf1(a,b)	GC_printf1(a,b)
+#define	DEBUG_GC_printf3(a,b,c,d)	GC_printf3(a,b,c,d)
+#else
+#define	DEBUG_GC_printf0(a)
+#define	DEBUG_GC_printf1(a,b)
+#define	DEBUG_GC_printf3(a,b,c,d)
+#endif
+
+/* cacao callbacks */
+void lock_stopworld(int);
+void unlock_stopworld();
+
+volatile sig_atomic_t GC_stop_count;
+			/* Incremented at the beginning of GC_stop_world. */
+
+volatile sig_atomic_t GC_world_is_stopped = FALSE;
+			/* FALSE ==> it is safe for threads to restart, i.e. */
+			/* they will see another suspend signal before they  */
+			/* are expected to stop (unless they have voluntarily */
+			/* stopped).					     */
+
+long GC_nprocs = 1;	/* Number of processors. */
+
+void GC_stop_world()
+{
+	DEBUG_GC_printf0("[GC_stop_world]\n");
+	lock_stopworld(1);
+	pthread_suspend_all_np();
+	GC_world_is_stopped = TRUE;
+	GC_stop_count++;
+	if( GC_nprocs > 1 ) {
+		/* FIXME: */
+		ABORT("GC_stop_world is not multi-processor enabled");
+	}
+}
+
+void GC_start_world()
+{
+	DEBUG_GC_printf0("[GC_start_world]\n");
+	GC_world_is_stopped = FALSE;
+	pthread_resume_all_np();
+	unlock_stopworld();
+}
+
+extern void GC_push_all(ptr_t bot, ptr_t top);
+
+static void GC_push_all_stacks_inner(pthread_t pthread)
+{
+	ptr_t lo, hi;
+	struct sigaltstack stack;
+	if( pthread_stackseg_np( pthread, &stack ) ) {
+		ABORT("pthread_stackseg_np");
+	}
+	if( pthread->sig_saved ) {
+		lo = (ptr_t) pthread->saved_sigcontext.sc_esp;
+	} else {
+		lo = (ptr_t) pthread->_machdep.esp;
+	}
+	if( pthread_equal( pthread_self(), pthread ) ) {
+		lo = (ptr_t) &lo;
+	}
+	if( lo == 0 ) {
+		ABORT("lo == 0 -- stack bottom undefined");
+	}
+	hi = GC_find_limit(lo,1);
+	DEBUG_GC_printf3("GC_push_all_stacks_inner pthread 0x%x lo=0x%x, hi=0x%x\n",pthread,lo,hi);
+	GC_push_all(lo,hi);
+#if 1
+	if( pthread->specific_data ) {
+		/* also push the pthread specific data, if there is any */
+		GC_push_all( (char*) pthread->specific_data, (char*)(pthread->specific_data + PTHREAD_KEYS_MAX) );
+	}
+#endif
+}
+
+void GC_push_all_stacks()
+{
+	pthread_t that;
+	DEBUG_GC_printf0("[GC_push_all_stacks]\n");
+	TAILQ_FOREACH(that, &_thread_list, tle) {
+#ifdef DEBUG_THREADS
+		if( (that->flags & PTHREAD_FLAGS_PRIVATE) == 0 ) {
+			DEBUG_GC_printf1("GC_push_all_stacks 0x%x\n", that );
+		} else {
+			DEBUG_GC_printf1("GC_push_all_stacks 0x%x PTHREAD_FLAGS_PRIVATE\n", that );
+		}
+#endif
+		if( (that->flags & PTHREAD_FLAGS_PRIVATE) == 0 ) {
+			GC_push_all_stacks_inner(that);
+		}
+	}
+}
+
+void GC_thr_init()
+{
+	DEBUG_GC_printf0("[GC_thr_init]\n");
+    GC_thr_initialized = 1;
+}
+
+void GC_init_parallel()
+{
+    if (parallel_initialized) return;
+    parallel_initialized = 1;
+    if (!GC_is_initialized) GC_init();
+}
+
+void GC_pause()
+{
+    int i;
+    for (i = 0; i < 10; ++i) {
+        __asm__ __volatile__ (" " : : : "memory");
+    }
+}
+
+volatile GC_bool GC_collecting = 0;
+volatile unsigned int GC_allocate_lock = 0;
+volatile GC_bool GC_in_thread_creation = 0;
+
+#if 0
+inline static int GC_test_and_set(volatile unsigned int *addr) {
+
+	int oldval;
+
+	__asm__ __volatile__("xchgl %0, %1"
+			: "=r"(oldval), "=m"(*(addr))
+			: "0"(1), "m"(*(addr)) : "memory");
+	return oldval;
+}
+#endif
+
+void GC_lock()
+{
+    static unsigned spin_max = 30;
+    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 = 128;
+            return;
+        }
+    }
+
+    spin_max = 30;
+yield:
+    for (i = 0;; ++i) {
+        if (!GC_test_and_set(&GC_allocate_lock)) {
+            return;
+        }
+        if (i < 12) {
+            sched_yield();
+        } else {
+            struct timespec ts;
+            if (i > 24) i = 24;
+            ts.tv_sec = 0;
+            ts.tv_nsec = 1 << i;
+            nanosleep(&ts, 0);
+        }
+    }
+}
+
+struct thread_stop_info {
+    word last_stop_count;
+    ptr_t stack_ptr;
+};
+
+typedef struct GC_Thread_Rep {
+    struct GC_Thread_Rep * next;
+    pthread_t id;
+    struct thread_stop_info stop_info;
+    short flags;
+    short thread_blocked;
+    ptr_t stack_end;
+    void * status;
+} * GC_thread;
+
+volatile GC_thread GC_threads[THREAD_TABLE_SZ];
+
+void GC_push_thread_structures (void)
+{
+	DEBUG_GC_printf0("[GC_push_thread_structures]\n");
+    GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
+}
+
+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 = 0;
+
+	if (!first_thread_used) {
+		int i;
+		for( i=0; i<THREAD_TABLE_SZ; i++ ) {
+			GC_threads[i] = NULL;
+		}
+		result = &first_thread;
+		first_thread_used = 1;
+	} else {
+		result = (struct GC_Thread_Rep *)
+			GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), 1);
+	}
+
+	if (result == NULL) return(NULL);
+
+	result -> id = id;
+	result -> next = GC_threads[hv];
+	GC_threads[hv] = result;
+
+	return(result);
+}
+
+void GC_delete_thread(pthread_t id)
+{
+    int hv = ((word)id) % 128;
+    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_free_inner(p);
+}
+
+void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
+{
+    int hv = ((word)id) % 128;
+    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_free_inner(p);
+}
+
+GC_thread GC_lookup_thread(pthread_t id)
+{
+    int hv = ((word)id) % 128;
+    register GC_thread p = GC_threads[hv];
+
+    while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
+    return(p);
+}
+
+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)) {
+            GC_collecting = 1;;
+            GC_in_thread_creation = 1;
+            GC_collect_a_little_inner(1);
+            GC_in_thread_creation = 0;
+            GC_collecting = 0;;
+            GC_clear(&GC_allocate_lock);
+            sched_yield();
+            { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); };
+        }
+    }
+}
+
+struct start_info {
+    void *(*start_routine)(void *);
+    void *arg;
+    word flags;
+    sem_t registered;
+};
+
+void GC_thread_exit_proc(void *arg)
+{
+	GC_thread me;
+/* <<<< Enter critical section >>>> */
+	{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); };
+	me = GC_lookup_thread(pthread_self());
+	if (me -> flags & 2) {
+		GC_delete_thread(pthread_self());
+	} else {
+		me -> flags |= 1;
+	}
+	GC_wait_for_gc_completion(0);
+	GC_clear(&GC_allocate_lock);
+/* <<<< Leave critical section >>>> */
+}
+
+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();
+
+	GC_printf("Starting thread %p\n", (long)my_pthread, 0l, 0l, 0l, 0l, 0l);
+	GC_printf("sp = %p\n", (long)&arg, 0l, 0l, 0l, 0l, 0l);
+
+/* <<<< Enter critical section >>>> */
+	{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); };
+	GC_in_thread_creation = 1;
+	me = GC_new_thread(my_pthread);
+	GC_in_thread_creation = 0;
+	me -> stop_info.stack_ptr = 0;
+	me -> flags = si -> flags;
+	me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1))
+								& ~(GC_page_size - 1));
+	me -> stop_info.stack_ptr = me -> stack_end - 0x10;
+	GC_clear(&GC_allocate_lock);
+/* <<<< Leave critical section >>>> */
+
+	start = si -> start_routine;
+	GC_printf("start_routine = %p\n", (long)start, 0l, 0l, 0l, 0l, 0l);
+	start_arg = si -> arg;
+	sem_post(&(si -> registered));
+
+	pthread_cleanup_push(GC_thread_exit_proc, 0);
+	result = (*start)(start_arg);
+	GC_printf("Finishing thread %p\n", (long)pthread_self(), 0l, 0l, 0l, 0l, 0l);
+
+	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;
+	int detachstate;
+	word my_flags = 0;
+	struct start_info * si;
+
+/* <<<< Enter critical section >>>> */
+	{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); };
+	si = (struct start_info *)GC_generic_malloc_inner(sizeof(struct start_info),
+												 1);
+	GC_clear(&GC_allocate_lock);
+/* <<<< Leave critical section >>>> */
+
+	if (!parallel_initialized) GC_init_parallel();
+
+	if (0 == si) return(12);
+
+	sem_init(&(si -> registered), 0, 0);
+	si -> start_routine = start_routine;
+	si -> arg = arg;
+
+/* <<<< Enter critical section >>>> */
+	{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); };
+	if (!GC_thr_initialized) GC_thr_init();
+
+	if (0L == attr) {
+		detachstate = 0;
+	} else {
+		pthread_attr_getdetachstate(attr, &detachstate);
+	}
+
+	if (0x1 == detachstate) my_flags |= 2;
+	si -> flags = my_flags;
+	GC_clear(&GC_allocate_lock);
+/* <<<< Leave critical section >>>>*/
+
+	GC_printf("About to start new thread from thread %p\n", (long)pthread_self(), 0l, 0l, 0l, 0l, 0l);
+
+	result = pthread_create(new_thread, attr, GC_start_routine, si);
+
+	GC_printf("Started thread %p\n", (long)*new_thread, 0l, 0l, 0l, 0l, 0l);
+
+	if (0 == result) {
+		while (0 != sem_wait(&(si -> registered))) {
+			if (4 != (*__errno())) GC_abort("sem_wait failed");;
+		}
+	}
+	sem_destroy(&(si -> registered));
+
+/* <<<< Enter critical section >>>> */
+	{ if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); };
+	GC_free_inner(si);
+	GC_clear(&GC_allocate_lock);
+/* <<<< Leave critical section >>>>*/
+
+	return(result);
+}
+
+/* Added for cacao */
+/* These signals are not used in openbsd */
+
+int GC_signum1()
+{
+	DEBUG_GC_printf0("[GC_signum1]\n");
+    return SIG_SUSPEND;
+}
+
+int GC_signum2()
+{
+	DEBUG_GC_printf0("[GC_signum2]\n");
+    return SIG_THR_RESTART;
+}
+
+/* cacao END */
+
+#endif /* GC_OPENBSD_THREADS */
+#endif
