Add support for the internal boehm-gc on OpenBSD
authorRobert Nagy <robert@openbsd.org>
Mon, 29 Nov 2010 14:53:41 +0000 (15:53 +0100)
committerZoltan Varga <vargaz@gmail.com>
Mon, 29 Nov 2010 16:05:46 +0000 (17:05 +0100)
libgc/include/private/openbsd_stop_world.h [new file with mode: 0644]
libgc/openbsd_stop_world.c [new file with mode: 0644]

diff --git a/libgc/include/private/openbsd_stop_world.h b/libgc/include/private/openbsd_stop_world.h
new file mode 100644 (file)
index 0000000..7f423ad
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef GC_OPENBSD_STOP_WORLD_H
+#define GC_OPENBSD_STOP_WORLD_H
+
+#if !defined(GC_OPENBSD_THREADS)
+#error openbsd_stop_world.h included without GC_OPENBSD_THREADS defined
+#endif
+
+struct thread_stop_info {
+    ptr_t stack_ptr;           /* Valid only when stopped.             */
+};
+    
+#endif
diff --git a/libgc/openbsd_stop_world.c b/libgc/openbsd_stop_world.c
new file mode 100644 (file)
index 0000000..a4c927e
--- /dev/null
@@ -0,0 +1,161 @@
+#include "private/pthread_support.h"
+
+/* derived from pthread_stop_world.c */
+
+# if defined(GC_OPENBSD_THREADS)
+
+#define THREAD_EQUAL(id1, id2) pthread_equal(id1, id2)
+
+/* 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 (!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