$OpenBSD: patch-openbsd_stop_world_c,v 1.1 2008/04/01 17:41:13 kurt Exp $
--- openbsd_stop_world.c.orig	Thu Feb  7 11:15:22 2008
+++ openbsd_stop_world.c	Thu Feb  7 11:38:22 2008
@@ -0,0 +1,162 @@
+#include "private/pthread_support.h"
+
+/* derived from pthread_stop_world.c */
+
+# if defined(GC_OPENBSD_THREADS)
+
+/* We hold allocation lock.  Should do exactly the right thing if the	*/
+/* world is stopped.  Should not fail if it isn't.			*/
+void GC_push_all_stacks()
+{
+    GC_bool found_me = FALSE;
+    size_t nthreads = 0;
+    int i;
+    GC_thread p;
+    ptr_t lo, hi;
+    pthread_t me = pthread_self();
+    
+    if (!GC_thr_initialized) GC_thr_init();
+#   if DEBUG_THREADS
+        GC_printf("Pushing stacks from thread 0x%x\n", (unsigned) me);
+#   endif
+    for (i = 0; i < THREAD_TABLE_SZ; i++) {
+      for (p = GC_threads[i]; p != 0; p = p -> next) {
+        if (p -> flags & FINISHED) continue;
+	++nthreads;
+        if (THREAD_EQUAL(p -> id, me)) {
+#  	    ifdef SPARC
+	        lo = (ptr_t)GC_save_regs_in_stack();
+#  	    else
+ 	        lo = GC_approx_sp();
+#           endif
+	    found_me = TRUE;
+	} else {
+	    lo = p -> stop_info.stack_ptr;
+	}
+        if ((p -> flags & MAIN_THREAD) == 0) {
+	    hi = p -> stack_end;
+        } else {
+            /* The original stack. */
+            hi = GC_stackbottom;
+        }
+#	if DEBUG_THREADS
+            GC_printf("Stack for thread 0x%x = [%p,%p)\n",
+    	              (unsigned)(p -> id), lo, hi);
+#	endif
+	if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n");
+#       ifdef STACK_GROWS_UP
+	  /* We got them backwards! */
+          GC_push_all_stack(hi, lo);
+#       else
+          GC_push_all_stack(lo, hi);
+#	endif
+      }
+    }
+    if (GC_print_stats == VERBOSE) {
+	GC_log_printf("Pushed %d thread stacks\n", nthreads);
+    }
+    if (!found_me && !GC_in_thread_creation)
+      ABORT("Collecting from unknown thread.");
+}
+
+/* We hold the allocation lock.  Suspend all threads that might	*/
+/* still be running. */ 
+void GC_suspend_all()
+{
+    int i;
+    GC_thread p;
+    int result;
+    pthread_t my_thread = pthread_self();
+    
+    for (i = 0; i < THREAD_TABLE_SZ; i++) {
+      for (p = GC_threads[i]; p != 0; p = p -> next) {
+        if (!THREAD_EQUAL(p -> id, my_thread)) {
+            if (p -> flags & FINISHED) continue;
+	    if (p -> thread_blocked) /* Will wait */ continue;
+#	    if DEBUG_THREADS
+	      GC_printf("Suspending thread 0x%x\n",
+			(unsigned)(p -> id));
+#	    endif
+        
+            if (pthread_suspend_np(p -> id) != 0)
+              ABORT("pthread_suspend_np failed");
+
+	   /*
+	    * This will only work for userland pthreads. It will
+	    * fail badly on rthreads. Perhaps we should consider
+	    * a pthread_sp_np() function that returns the stack
+	    * pointer for a suspended thread and implement in
+	    * both pthreads and rthreads.
+	    */
+	   p -> stop_info.stack_ptr = *(ptr_t*)((char *)p -> id + UTHREAD_SP_OFFSET);
+        }
+      }
+    }
+}
+
+void GC_stop_world()
+{
+    int i;
+
+    GC_ASSERT(I_HOLD_LOCK());
+#   if DEBUG_THREADS
+      GC_printf("Stopping the world from 0x%x\n", (unsigned)pthread_self());
+#   endif
+       
+    /* Make sure all free list construction has stopped before we start. */
+    /* No new construction can start, since free list construction is	*/
+    /* required to acquire and release the GC lock before it starts,	*/
+    /* and we have the lock.						*/
+#   ifdef PARALLEL_MARK
+      GC_acquire_mark_lock();
+      GC_ASSERT(GC_fl_builder_count == 0);
+      /* We should have previously waited for it to become zero. */
+#   endif /* PARALLEL_MARK */
+
+    GC_suspend_all();
+
+#   ifdef PARALLEL_MARK
+      GC_release_mark_lock();
+#   endif
+    #if DEBUG_THREADS
+      GC_printf("World stopped from 0x%x\n", (unsigned)pthread_self());
+    #endif
+}
+
+/* Caller holds allocation lock, and has held it continuously since	*/
+/* the world stopped.							*/
+void GC_start_world()
+{
+    pthread_t my_thread = pthread_self();
+    register int i;
+    register GC_thread p;
+    register int result;
+
+#   if DEBUG_THREADS
+      GC_printf("World starting\n");
+#   endif
+
+    for (i = 0; i < THREAD_TABLE_SZ; i++) {
+      for (p = GC_threads[i]; p != 0; p = p -> next) {
+        if (!THREAD_EQUAL(p -> id, my_thread)) {
+            if (p -> flags & FINISHED) continue;
+	    if (p -> thread_blocked) continue;
+	    #if DEBUG_THREADS
+	      GC_printf("Resuming thread 0x%x\n",
+			(unsigned)(p -> id));
+	    #endif
+        
+            if (pthread_resume_np(p -> id) != 0)
+              ABORT("pthread_kill failed");
+        }
+      }
+    }
+#    if DEBUG_THREADS
+      GC_printf("World started\n");
+#    endif
+}
+
+void GC_stop_init() {
+}
+
+#endif
