]> granicus.if.org Git - gc/commitdiff
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 11:52:36 +0000 (15:52 +0400)
(Apply commit 11e95a1 from 'master' branch)

* include/private/gcconfig.h (HANDLE_FORK): Define also for Cygwin
(but not for win32-pthreads).
* 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 3ab71560bec7fbdb4cd52595d1ffadcdb6872b2a..1cfa8359e95cbbb9e8a324bb79fee09c023563f7 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)
   /* Attempts (where supported) to make GC_malloc work in a child       */
   /* process fork'ed from a multi-threaded parent.                      */
 # define HANDLE_FORK
index e6a20028964e205dd01c8e376641d8b970891534..6b891d38347a56dce7be50790be56bfe80825396 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
 
@@ -744,8 +747,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()) {
@@ -764,7 +767,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));
   }
 }
 
@@ -794,7 +797,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);
@@ -967,6 +970,84 @@ 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_markers = 1;
+          GC_parallel = FALSE;
+                /* 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)
@@ -2272,6 +2353,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 =
@@ -2536,7 +2624,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