2008-04-15 Jonathan Chambers <joncham@gmail.com>
[mono.git] / libgc / pthread_stop_world.c
index 001a9e05c5c158b42ed6073fc8978df9df96ea89..04eb3712c263d9831af128e93c174c61e813635c 100644 (file)
@@ -9,6 +9,18 @@
 #include <errno.h>
 #include <unistd.h>
 
+/* work around a dlopen issue (bug #75390), undefs to avoid warnings with redefinitions */
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include "mono/utils/mono-compiler.h"
+
+#ifdef MONO_DEBUGGER_SUPPORTED
+#include "include/libgc-mono-debugger.h"
+#endif
+
 #if DEBUG_THREADS
 
 #ifndef NSIG
@@ -101,7 +113,7 @@ word GC_stop_count; /* Incremented at the beginning of GC_stop_world. */
 
 sem_t GC_suspend_ack_sem;
 
-void GC_suspend_handler(int sig)
+static void _GC_suspend_handler(int sig)
 {
     int dummy;
     pthread_t my_thread = pthread_self();
@@ -163,12 +175,25 @@ void GC_suspend_handler(int sig)
     /* to accidentally leave a RESTART signal pending, thus causing us to   */
     /* continue prematurely in a future round.                             */ 
 
+    /* Tell the thread that wants to start the world that this  */
+    /* thread has been started.  Note that sem_post() is       */
+    /* the only async-signal-safe primitive in LinuxThreads.    */
+    sem_post(&GC_suspend_ack_sem);
+
+
 #if DEBUG_THREADS
     GC_printf1("Continuing 0x%lx\n", my_thread);
 #endif
 }
 
-void GC_restart_handler(int sig)
+void GC_suspend_handler(int sig)
+{
+       int old_errno = errno;
+       _GC_suspend_handler(sig);
+       errno = old_errno;
+}
+
+static void _GC_restart_handler(int sig)
 {
     pthread_t my_thread = pthread_self();
     GC_thread me;
@@ -270,11 +295,18 @@ static void pthread_push_all_stacks()
       ABORT("Collecting from unknown thread.");
 }
 
+void GC_restart_handler(int sig)
+{
+       int old_errno = errno;
+       _GC_restart_handler (sig);
+       errno = old_errno;
+}
+
 /* 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_thread_vtable->push_all_stacks();
+    pthread_push_all_stacks();
 }
 
 /* There seems to be a very rare thread stopping problem.  To help us  */
@@ -333,7 +365,7 @@ static void pthread_stop_world()
     #if DEBUG_THREADS
     GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
     #endif
-       
+
     n_live_threads = GC_suspend_all();
 
       if (GC_retry_signals) {
@@ -366,9 +398,11 @@ static void pthread_stop_world()
          }
       }
     for (i = 0; i < n_live_threads; i++) {
-         if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
-             GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
-             ABORT("sem_wait for handler failed");
+         while (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
+             if (errno != EINTR) {
+                GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
+                ABORT("sem_wait for handler failed");
+             }
          }
     }
     #if DEBUG_THREADS
@@ -380,6 +414,8 @@ static void pthread_stop_world()
 /* Caller holds allocation lock.       */
 void GC_stop_world()
 {
+    if (GC_notify_event)
+        GC_notify_event (GC_EVENT_PRE_STOP_WORLD);
     /* 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,   */
@@ -390,10 +426,17 @@ void GC_stop_world()
       /* We should have previously waited for it to become zero. */
 #   endif /* PARALLEL_MARK */
     ++GC_stop_count;
-    gc_thread_vtable->stop_world ();
+#ifdef MONO_DEBUGGER_SUPPORTED
+    if (gc_thread_vtable && gc_thread_vtable->stop_world)
+       gc_thread_vtable->stop_world ();
+    else
+#endif
+       pthread_stop_world ();
 #   ifdef PARALLEL_MARK
       GC_release_mark_lock();
 #   endif
+    if (GC_notify_event)
+        GC_notify_event (GC_EVENT_POST_STOP_WORLD);
 }
 
 /* Caller holds allocation lock, and has held it continuously since    */
@@ -405,10 +448,13 @@ static void pthread_start_world()
     register GC_thread p;
     register int n_live_threads = 0;
     register int result;
+    int code;
 
 #   if DEBUG_THREADS
       GC_printf0("World starting\n");
 #   endif
+    if (GC_notify_event)
+        GC_notify_event (GC_EVENT_PRE_START_WORLD);
 
     for (i = 0; i < THREAD_TABLE_SZ; i++) {
       for (p = GC_threads[i]; p != 0; p = p -> next) {
@@ -434,6 +480,22 @@ static void pthread_start_world()
         }
       }
     }
+
+    #if DEBUG_THREADS
+    GC_printf0 ("All threads signaled");
+    #endif
+
+    for (i = 0; i < n_live_threads; i++) {
+       while (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
+           if (errno != EINTR) {
+               GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
+               ABORT("sem_wait for handler failed");
+           }
+       }
+    }
+  
+    if (GC_notify_event)
+        GC_notify_event (GC_EVENT_POST_START_WORLD);
     #if DEBUG_THREADS
       GC_printf0("World started\n");
     #endif
@@ -441,7 +503,12 @@ static void pthread_start_world()
 
 void GC_start_world()
 {
-    gc_thread_vtable->start_world();
+#ifdef MONO_DEBUGGER_SUPPORTED
+    if (gc_thread_vtable && gc_thread_vtable->start_world)
+       gc_thread_vtable->start_world();
+    else
+#endif
+       pthread_start_world ();
 }
 
 static void pthread_stop_init() {
@@ -490,20 +557,27 @@ static void pthread_stop_init() {
 /* We hold the allocation lock.        */
 void GC_stop_init()
 {
-    gc_thread_vtable->initialize ();
+#ifdef MONO_DEBUGGER_SUPPORTED
+    if (gc_thread_vtable && gc_thread_vtable->initialize)
+       gc_thread_vtable->initialize ();
+    else
+#endif
+       pthread_stop_init ();
 }
 
-/*
- * This is used by the Mono Debugger to stop/start the world.
- */
-GCThreadFunctions pthread_thread_vtable = {
-    pthread_stop_init,
-    pthread_stop_world,
-    pthread_push_all_stacks,
-    pthread_start_world
-};
-
-GCThreadFunctions *gc_thread_vtable = &pthread_thread_vtable;
+#ifdef MONO_DEBUGGER_SUPPORTED
+
+GCThreadFunctions *gc_thread_vtable = NULL;
+
+void *
+GC_mono_debugger_get_stack_ptr (void)
+{
+       GC_thread me;
+
+       me = GC_lookup_thread (pthread_self ());
+       return &me->stop_info.stack_ptr;
+}
+
+#endif
 
 #endif