$OpenBSD: patch-boehm-gc_openbsd_stop_world_c,v 1.2 2011/11/22 21:46:39 pascal Exp $
--- boehm-gc/openbsd_stop_world.c.orig	Sun Nov 20 19:18:24 2011
+++ boehm-gc/openbsd_stop_world.c	Mon Nov 21 01:15:08 2011
@@ -0,0 +1,193 @@
+#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_printf1("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 (pthread_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_printf3("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) {
+	GC_printf1("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 (!pthread_equal(p -> id, my_thread)) {
+            if (p -> flags & FINISHED) continue;
+	    if (p -> thread_blocked) /* Will wait */ continue;
+#	    if DEBUG_THREADS
+	      GC_printf1("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_printf1("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_printf1("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_printf0("World starting\n");
+#   endif
+
+    for (i = 0; i < THREAD_TABLE_SZ; i++) {
+      for (p = GC_threads[i]; p != 0; p = p -> next) {
+        if (!pthread_equal(p -> id, my_thread)) {
+            if (p -> flags & FINISHED) continue;
+	    if (p -> thread_blocked) continue;
+	    #if DEBUG_THREADS
+	      GC_printf1("Resuming thread 0x%x\n",
+			(unsigned)(p -> id));
+	    #endif
+        
+            if (pthread_resume_np(p -> id) != 0)
+              ABORT("pthread_kill failed");
+        }
+      }
+    }
+#    if DEBUG_THREADS
+      GC_printf0("World started\n");
+#    endif
+}
+
+void GC_stop_init() {
+}
+
+void GC_suspend_thread(pthread_t thread) {
+  if (thread == pthread_self()) {
+    ABORT("attempting to suspend self");
+  } else {
+    GC_thread t = GC_lookup_thread(thread);
+    if (t == NULL)
+      ABORT("attempting to suspend unknown thread");
+
+    if (pthread_suspend_np(t -> id) != 0)
+      ABORT("pthread_suspend_np failed");
+  }
+}
+
+void GC_resume_thread(pthread_t thread) {
+  GC_thread t = GC_lookup_thread(thread);
+  if (t == NULL)
+    ABORT("attempting to resume unknown thread");
+
+  if (pthread_resume_np(t -> id) != 0)
+    ABORT("pthread_resume_np failed");
+}
+
+int GC_is_thread_suspended(pthread_t thread) {
+  GC_thread t = GC_lookup_thread(thread);
+  if (t == NULL)
+    ABORT("querying suspension state of unknown thread");
+
+  return (t -> flags & SUSPENDED);
+}
+
+
+#endif
