$OpenBSD$
--- pthread_support.c.orig	Thu Feb 16 23:07:50 2006
+++ pthread_support.c	Sun Sep  3 15:37:56 2006
@@ -49,6 +49,11 @@
 
 # include "private/pthread_support.h"
 
+#ifdef OPENBSD
+/* XXX unfortunately we need to peek into the internals */
+# include "/usr/src/lib/libpthread/uthread/pthread_private.h"
+#endif
+
 # if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
      && !defined(GC_WIN32_THREADS)
 
@@ -68,7 +73,7 @@
 
 # if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
       defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) || \
-      defined(GC_NETBSD_THREADS))			       \
+      defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_PTHREADS)) \
       && !defined(USE_PTHREAD_SPECIFIC)
 #   define USE_PTHREAD_SPECIFIC
 # endif
@@ -557,11 +562,24 @@ void GC_mark_thread_local_free_lists(voi
 
 static struct GC_Thread_Rep first_thread;
 
+#if !defined(HASH_SHIFT) && defined(GC_OPENBSD_PTHREADS)
+#define HASH_SHIFT	10	/* OpenBSD aligns the pthread structure widely */
+#endif
+
+#if !defined(HASH_SHIFT)
+#define HASH_SHIFT	2	/* Always safe because of pointer alignment in 32-bit machines */
+#endif
+
+/* NB: THREAD_TABLE_SZ must be a power of two */
+static inline word hashNo(word id) {
+	return (id >> HASH_SHIFT) & (THREAD_TABLE_SZ - 1);
+}
+
 /* 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;
+    int hv = hashNo((word)id);
     GC_thread result;
     static GC_bool first_thread_used = FALSE;
     
@@ -576,6 +594,9 @@ GC_thread GC_new_thread(pthread_t id)
     result -> id = id;
     result -> next = GC_threads[hv];
     GC_threads[hv] = result;
+#ifdef DEBUG_THREADS
+	GC_printf2("GC_new_thread pthread_t %lx hashes to GC_threads[%d]\n",id,hv);
+#endif
     GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0);
     return(result);
 }
@@ -585,7 +606,7 @@ GC_thread GC_new_thread(pthread_t id)
 /* Caller holds allocation lock.				*/
 void GC_delete_thread(pthread_t id)
 {
-    int hv = ((word)id) % THREAD_TABLE_SZ;
+    int hv = hashNo((word)id);
     register GC_thread p = GC_threads[hv];
     register GC_thread prev = 0;
     
@@ -607,7 +628,7 @@ void GC_delete_thread(pthread_t 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;
+    int hv = hashNo((word)id);
     register GC_thread p = GC_threads[hv];
     register GC_thread prev = 0;
 
@@ -631,7 +652,7 @@ void GC_delete_gc_thread(pthread_t id, G
 /* return the most recent one.					*/
 GC_thread GC_lookup_thread(pthread_t id)
 {
-    int hv = ((word)id) % THREAD_TABLE_SZ;
+    int hv = hashNo((word)id);
     register GC_thread p = GC_threads[hv];
     
     while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
@@ -906,6 +927,9 @@ void GC_thr_init()
 	  sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
 	  GC_nprocs = ncpus;
 #       endif
+#		if defined(GC_OPENBSD_PTHREADS)
+	  GC_nprocs = 1; /* FreeBSD code may work as well */
+#		endif
 #	if defined(GC_LINUX_THREADS) || defined(GC_DGUX386_THREADS)
           GC_nprocs = GC_get_nprocs();
 #	endif
@@ -1137,7 +1161,11 @@ GC_bool GC_in_thread_creation = FALSE;
 
 void * GC_start_routine(void * arg)
 {
+
+#ifndef OPENBSD
     int dummy;
+#endif
+
     struct start_info * si = arg;
     void * result;
     GC_thread me;
@@ -1145,6 +1173,11 @@ void * GC_start_routine(void * arg)
     void *(*start)(void *);
     void *start_arg;
 
+#ifdef OPENBSD
+	/* allocation position is different */
+	int dummy;
+#endif
+
     my_pthread = pthread_self();
 #   ifdef DEBUG_THREADS
         GC_printf1("Starting thread 0x%lx\n", my_pthread);
@@ -1278,6 +1311,14 @@ WRAP_FUNC(pthread_create)(pthread_t *new
 #   ifdef DEBUG_THREADS
         GC_printf1("Started thread 0x%X\n", *new_thread);
 #   endif
+
+#ifdef OPENBSD
+	if( 0 == result ) {
+		/* XXX pthread_resume_np(*new_thread); */
+		pthread_yield();
+	}
+#endif
+
     /* 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		*/
