$OpenBSD$
--- pthread_stop_world.c.orig	Fri Mar  3 15:01:48 2006
+++ pthread_stop_world.c	Sun Sep  3 18:11:32 2006
@@ -13,6 +13,11 @@
   /* Doesn't exist on HP/UX 11.11. */
 #endif
 
+#if defined(OPENBSD)
+#include <pthread_np.h>
+#include <pthread_private.h>
+#endif
+
 #if DEBUG_THREADS
 
 #ifndef NSIG
@@ -140,11 +145,73 @@ void GC_suspend_handler(int sig)
 }
 
 #else
-/* We believe that in all other cases the full context is already	*/
-/* in the signal handler frame.						*/
+/* We believe that in all other cases the full context is already     */
+/* in the signal handler frame.                                               */
+
+
+#ifdef OPENBSD
+typedef int (*GC_SUSPEND_HANDLER)(void*);
+
+/* default suspend_handler callback */
+int GC_suspend_handler_default(void* uctx)
+{
+	return 0; /* we didn't handle it */
+}
+
+/* default lock_stopworld callback */
+void GC_lock_stopworld_default(int x)
+{
+	return;
+}
+
+/* default unlock_stopworld callback */
+void GC_unlock_stopworld_default()
+{
+	return;
+}
+
+static GC_SUSPEND_HANDLER GC_suspend_handler_callback =
+	GC_suspend_handler_default;
+
+static void (*GC_lock_stopworld_callback)(int) =
+	GC_lock_stopworld_default;
+
+static void (*GC_unlock_stopworld_callback)() =
+	GC_unlock_stopworld_default;
+
+/* this is an extension to the GC_ api */
+/* allows clients to hook into signal suspension */
+
+#define setvalue(var,val,default) \
+	if( val == (void*)-1 )		\
+		var = default ; \
+	else if( val != (void*)0 ) \
+		var = val;
+
+void GC_callback(void *suspend, void *locksw, void *unlocksw)
+{
+	setvalue(GC_suspend_handler_callback,suspend,GC_suspend_handler_default);
+	setvalue(GC_lock_stopworld_callback,locksw,GC_lock_stopworld_default);
+	setvalue(GC_unlock_stopworld_callback,unlocksw,GC_unlock_stopworld_default);
+	return;
+}
+
+#endif
+
+#ifdef OPENBSD
+void GC_suspend_handler(int sig, siginfo_t *info, void *uctx)
+#else
 void GC_suspend_handler(int sig)
+#endif
 {
   int old_errno = errno;
+
+#ifdef OPENBSD
+	/* give client first crack at the signal */
+	if ((*GC_suspend_handler_callback)(uctx))
+		return;
+#endif
+
   GC_suspend_handler_inner((ptr_t)(word)sig);
   errno = old_errno;
 }
@@ -300,6 +367,17 @@ void GC_push_all_stacks()
     	        (unsigned long) p -> id,
 		(unsigned long) lo, (unsigned long) hi);
         #endif
+#ifdef OPENBSD
+/* push the thread specific data areas to assure they are collected */
+		if( p->id->specific_data ) {
+			ptr_t bot = (ptr_t)  p->id->specific_data ;
+			ptr_t top = (ptr_t) (p->id->specific_data + PTHREAD_KEYS_MAX) ;
+#  ifdef DEBUG_THREADS
+			GC_printf2("TLS at [0x%X,0x%X)\n", bot, top );
+#  endif
+			GC_push_all_eager( bot, top );
+		}
+#endif
 	if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n");
 #       ifdef STACK_GROWS_UP
 	  /* We got them backwards! */
@@ -345,6 +423,28 @@ int GC_suspend_all()
     
     GC_stopping_thread = my_thread;    /* debugging only.      */
     GC_stopping_pid = getpid();                /* debugging only.      */
+
+#if defined(OPENBSD)
+
+	pthread_suspend_all_np();
+	/* update stack top address in struct GC_thread */
+	/* this code is similiar to code in GC_push_all_stacks */
+	for (i = 0; i < THREAD_TABLE_SZ; i++) {
+		GC_thread p;
+		ptr_t lo;
+		for (p = GC_threads[i]; p != 0; p = p -> next) {
+			if (p -> flags & FINISHED) continue;
+			if (pthread_equal(p -> id, my_thread)) {
+				lo = GC_approx_sp();
+			} else {
+				/* very OpenBSD dependent */
+				lo = (ptr_t)(p->id->sig_saved ? p->id->saved_sigcontext.sc_esp : p->id->_machdep.esp);
+			}
+			p->stop_info.stack_ptr = lo;
+		}
+	}
+#else
+
     for (i = 0; i < THREAD_TABLE_SZ; i++) {
       for (p = GC_threads[i]; p != 0; p = p -> next) {
         if (p -> id != my_thread) {
@@ -370,6 +470,7 @@ int GC_suspend_all()
         }
       }
     }
+#endif
     return n_live_threads;
 }
 
@@ -383,6 +484,11 @@ void GC_stop_world()
     #if DEBUG_THREADS
     GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
     #endif
+
+#ifdef OPENBSD
+	/* XXX cacao likes to know this */
+    (*GC_lock_stopworld_callback)(1);
+#endif
        
     /* Make sure all free list construction has stopped before we start. */
     /* No new construction can start, since free list construction is	*/
@@ -397,6 +503,7 @@ void GC_stop_world()
     GC_world_is_stopped = TRUE;
     n_live_threads = GC_suspend_all();
 
+#if !defined(OPENBSD) /* no need for this */
       if (GC_retry_signals) {
 	  unsigned long wait_usecs = 0;  /* Total wait since retry.	*/
 #	  define WAIT_UNIT 3000
@@ -434,6 +541,8 @@ void GC_stop_world()
 	      }
 	  }
     }
+#endif
+
 #   ifdef PARALLEL_MARK
       GC_release_mark_lock();
 #   endif
@@ -448,6 +557,10 @@ void GC_stop_world()
 void GC_start_world()
 {
     pthread_t my_thread = pthread_self();
+
+#if defined(OPENBSD)
+	pthread_resume_all_np();
+#else
     register int i;
     register GC_thread p;
     register int n_live_threads = 0;
@@ -495,6 +608,13 @@ void GC_start_world()
     #if DEBUG_THREADS
       GC_printf0("World started\n");
     #endif
+#endif
+
+#ifdef OPENBSD
+	/* XXX cacao wants to know this */
+	(*GC_unlock_stopworld_callback)();
+#endif
+
 }
 
 void GC_stop_init() {
@@ -507,14 +627,18 @@ void GC_stop_init() {
 	ABORT("sem_init failed");
 #endif
 
-    act.sa_flags = SA_RESTART;
+    act.sa_flags = 
+#if defined(OPENBSD) /* XXX We don't use signals */
+		SA_ONSTACK | SA_SIGINFO |
+#endif
+		SA_RESTART;
     if (sigfillset(&act.sa_mask) != 0) {
     	ABORT("sigfillset() failed");
     }
     GC_remove_allowed_signals(&act.sa_mask);
     /* SIG_THR_RESTART is set in the resulting mask.		*/
     /* It is unmasked by the handler when necessary. 		*/
-    act.sa_handler = GC_suspend_handler;
+    act.sa_handler = GC_suspend_handler; /* XXX compiler warning here */
     if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
     	ABORT("Cannot set SIG_SUSPEND handler");
     }
@@ -543,5 +667,20 @@ void GC_stop_init() {
 	  }
 #     endif
 }
+
+#ifdef OPENBSD
+/* Added for cacao */
+/* XXX These signals are not used at all in this port */
+int GC_signum1()
+{
+    return SIG_SUSPEND;
+}
+
+int GC_signum2()
+{
+    return SIG_THR_RESTART;
+}
+/* cacao END */
+#endif
 
 #endif
