Fix Cygwin support to handle fork() properly
authorIvan Maidanski <ivmai@mail.ru>
Fri, 24 Feb 2012 10:34:34 +0000 (14:34 +0400)
committerIvan Maidanski <ivmai@mail.ru>
Fri, 24 Feb 2012 10:34:34 +0000 (14:34 +0400)
* include/private/gcconfig.h (HANDLE_FORK): Define also for Cygwin
(but not for win32-pthreads and not if Win32 memory allocation used).
* win32_threads.c: Include unistd.h if HANDLE_FORK (for
pthread_atfork).
* win32_threads.c (GC_wait_for_gc_completion): Add wait_for_all
argument.
* win32_threads.c (GC_unregister_my_thread, GC_thread_exit_proc): Pass
FALSE ("wait_for_all" argument) to GC_wait_for_gc_completion.
* win32_threads.c (GC_remove_all_threads_but_me, GC_fork_prepare_proc,
GC_fork_parent_proc, GC_fork_child_proc): New functions (similar to
that in pthread_support.c) if HANDLE_FORK.
* win32_threads.c (GC_thr_init): Invoke pthread_atfork if HANDLE_FORK.

include/private/gcconfig.h
win32_threads.c

index a2ba20d..3597de2 100644 (file)
 # define IF_CANCEL(x) /* empty */
 #endif
 
-#if defined(GC_PTHREADS) && !defined(GC_WIN32_THREADS) \
-    && !defined(HANDLE_FORK) && !defined(NO_HANDLE_FORK) \
-    && !defined(HURD) && !defined(NACL) && !defined(PLATFORM_ANDROID)
+#if !defined(HANDLE_FORK) && !defined(NO_HANDLE_FORK) && defined(GC_PTHREADS) \
+    && !defined(HURD) && !defined(NACL) && !defined(PLATFORM_ANDROID) \
+    && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)
   /* Attempts (where supported) to make GC_malloc work in a child       */
   /* process fork'ed from a multi-threaded parent.                      */
 # define HANDLE_FORK
index 09bfbd4..1682b2c 100644 (file)
@@ -55,6 +55,9 @@
   STATIC void GC_thread_exit_proc(void *arg);
 
 # include <pthread.h>
+# ifdef HANDLE_FORK
+#   include <unistd.h>
+# endif
 
 #else
 
@@ -759,8 +762,8 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb)
   }
 }
 
-/* Similar to that in pthread_support.c (wait_for_all is always FALSE). */
-STATIC void GC_wait_for_gc_completion(void)
+/* Similar to that in pthread_support.c.        */
+STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all)
 {
   GC_ASSERT(I_HOLD_LOCK());
   if (GC_incremental && GC_collection_in_progress()) {
@@ -779,7 +782,7 @@ STATIC void GC_wait_for_gc_completion(void)
       Sleep(0); /* yield */
       LOCK();
     } while (GC_incremental && GC_collection_in_progress()
-             && old_gc_no == GC_gc_no);
+             && (wait_for_all || old_gc_no == GC_gc_no));
   }
 }
 
@@ -809,7 +812,7 @@ GC_API int GC_CALL GC_unregister_my_thread(void)
     DWORD thread_id = GetCurrentThreadId();
 
     LOCK();
-    GC_wait_for_gc_completion();
+    GC_wait_for_gc_completion(FALSE);
 #   if defined(THREAD_LOCAL_ALLOC) || defined(GC_PTHREADS)
       me = GC_lookup_thread_inner(thread_id);
       CHECK_LOOKUP_MY_THREAD(me);
@@ -982,6 +985,83 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
     }
   }
 
+# ifdef HANDLE_FORK
+    /* Similar to that in pthread_support.c.    */
+    STATIC void GC_remove_all_threads_but_me(void)
+    {
+      pthread_t id = pthread_self();
+      int hv;
+      GC_thread p, next, me;
+
+      for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
+        me = 0;
+        for (p = GC_threads[hv]; 0 != p; p = next) {
+          next = p -> tm.next;
+          if (THREAD_EQUAL(p -> pthread_id, id)) {
+            me = p;
+            p -> tm.next = 0;
+            /* Update Win32 thread Id and handle.       */
+            me -> id = GetCurrentThreadId();
+#           ifndef MSWINCE
+              if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                                   GetCurrentProcess(),
+                                   (HANDLE *)&me->handle, 0, FALSE,
+                                   DUPLICATE_SAME_ACCESS))
+                ABORT("DuplicateHandle failed");
+#           endif
+          } else {
+#           ifdef THREAD_LOCAL_ALLOC
+              if ((p -> flags & FINISHED) == 0) {
+                GC_destroy_thread_local(&p->tlfs);
+              }
+#           endif
+            if (&first_thread != p)
+              GC_INTERNAL_FREE(p);
+          }
+        }
+        GC_threads[hv] = me;
+      }
+    }
+
+    STATIC void GC_fork_prepare_proc(void)
+    {
+      LOCK();
+#     ifdef PARALLEL_MARK
+        if (GC_parallel)
+          GC_wait_for_reclaim();
+#     endif
+      GC_wait_for_gc_completion(TRUE);
+#     ifdef PARALLEL_MARK
+        if (GC_parallel)
+          GC_acquire_mark_lock();
+#     endif
+    }
+
+    STATIC void GC_fork_parent_proc(void)
+    {
+#     ifdef PARALLEL_MARK
+        if (GC_parallel)
+          GC_release_mark_lock();
+#     endif
+      UNLOCK();
+    }
+
+    STATIC void GC_fork_child_proc(void)
+    {
+#     ifdef PARALLEL_MARK
+        if (GC_parallel) {
+          GC_release_mark_lock();
+          GC_parallel = FALSE; /* or GC_markers_m1 = 0 */
+                /* Turn off parallel marking in the child, since we are */
+                /* probably just going to exec, and we would have to    */
+                /* restart mark threads.                                */
+        }
+#     endif
+      GC_remove_all_threads_but_me();
+      UNLOCK();
+    }
+# endif /* HANDLE_FORK */
+
 #endif /* GC_PTHREADS */
 
 void GC_push_thread_structures(void)
@@ -1593,7 +1673,7 @@ GC_INNER void GC_get_next_stack(char *start, char *limit,
 #   endif
 
     /* start_mark_threads() is the same as in pthread_support.c except for: */
-    /* - GC_markers_m1 value is adjusted already;                              */
+    /* - GC_markers_m1 value is adjusted already;                           */
     /* - thread stack is assumed to be large enough; and                    */
     /* - statistics about the number of marker threads is printed outside.  */
     static void start_mark_threads(void)
@@ -2280,6 +2360,13 @@ GC_INNER void GC_thr_init(void)
   GC_main_thread = GetCurrentThreadId();
   GC_thr_initialized = TRUE;
 
+# if defined(GC_PTHREADS) && defined(HANDLE_FORK)
+    /* Prepare for a possible fork.     */
+    if (pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
+                       GC_fork_child_proc) != 0)
+      ABORT("pthread_atfork failed");
+# endif
+
   /* Add the initial thread, so we can stop it. */
 # ifdef GC_ASSERTIONS
     sb_result =
@@ -2544,7 +2631,7 @@ GC_INNER void GC_thr_init(void)
 #   endif
 
     LOCK();
-    GC_wait_for_gc_completion();
+    GC_wait_for_gc_completion(FALSE);
 #   if defined(THREAD_LOCAL_ALLOC)
       GC_destroy_thread_local(&(me->tlfs));
 #   endif