Add public GC_set_handle_fork to control forked child handling support
authorIvan Maidanski <ivmai@mail.ru>
Mon, 2 Apr 2012 16:18:12 +0000 (20:18 +0400)
committerIvan Maidanski <ivmai@mail.ru>
Mon, 2 Apr 2012 17:08:22 +0000 (21:08 +0400)
* include/gc.h (GC_set_handle_fork): New API function.
* misc.c (GC_set_handle_fork): Likewise.
* include/private/gc_priv.h (GC_handle_fork): New internal variable
declaration (only if CAN_HANDLE_FORK).
* misc.c (GC_handle_fork): New internal variable (defined only if
CAN_HANDLE_FORK); initialize to TRUE if HANDLE_FORK.
* include/private/gcconfig.h (HANDLE_FORK): Replace with
CAN_HANDLE_FORK.
* pthread_support.c (HANDLE_FORK): Likewise.
* win32_threads.c (HANDLE_FORK): Likewise.
* include/private/gcconfig.h (CAN_HANDLE_FORK): Always define macro if
HANDLE_FORK.
* pthread_support.c (GC_thr_init): Replace HANDLE_FORK with
CAN_HANDLE_FORK; call pthread_atfork only if GC_handle_fork; update
the comment.
* win32_threads.c (GC_thr_init): Likewise.
* tests/test.c (NO_TEST_HANDLE_FORK): Define new macro if fork
handling is not supported (or is a no-op) on the target.
* tests/test.c (INIT_FORK_SUPPORT): New macro (invoke
GC_set_handle_fork unless NO_TEST_HANDLE_FORK).
* tests/test.c (GC_OPT_INIT): New macro (defined to GC_INIT or empty).
* tests/test.c (GC_COND_INIT): Use INIT_FORK_SUPPORT and GC_OPT_INIT.
* tests/test.c (run_one_test): Test NO_TEST_HANDLE_FORK (instead of
target-specific macros).
* win32_threads.c (GC_remove_all_threads_but_me, GC_fork_prepare_proc,
GC_fork_parent_proc, GC_fork_child_proc): Do not test GC_PTHREADS.
* configure.ac (HANDLE_FORK, NO_HANDLE_FORK): Update message.

configure.ac
include/gc.h
include/private/gc_priv.h
include/private/gcconfig.h
misc.c
pthread_support.c
tests/test.c
win32_threads.c

index 9307218..b61cf81 100644 (file)
@@ -792,10 +792,10 @@ AC_ARG_ENABLE(handle-fork,
 
 if test "${enable_handle_fork}" = yes; then
     AC_DEFINE(HANDLE_FORK, 1,
-              [Define to install pthread_atfork() handlers if available.])
+              [Define to install pthread_atfork() handlers by default.])
 elif test "${enable_handle_fork}" = no; then
     AC_DEFINE(NO_HANDLE_FORK, 1,
-              [Define to inhibit installation of pthread_atfork() handlers.])
+              [Prohibit installation of pthread_atfork() handlers.])
 fi
 
 dnl This is something of a hack.  When cross-compiling we turn off
index 3902694..d2e13d5 100644 (file)
@@ -363,6 +363,14 @@ GC_API void GC_CALL GC_set_pages_executable(int);
 /* use or need synchronization (i.e. acquiring the allocator lock).     */
 GC_API int GC_CALL GC_get_pages_executable(void);
 
+/* Overrides the default handle-fork mode.  Non-zero value means GC     */
+/* should install proper pthread_atfork handlers.  Has effect only if   */
+/* called before GC_INIT.  Clients should invoke GC_set_handle_fork(1)  */
+/* only if going to use fork with GC functions called in the forked     */
+/* child.  (Note that such client and atfork handlers activities are    */
+/* not fully POSIX-compliant.)                                          */
+GC_API void GC_CALL GC_set_handle_fork(int);
+
 /* Initialize the collector.  Portable clients should call GC_INIT()    */
 /* from the main program instead.                                       */
 GC_API void GC_CALL GC_init(void);
index 4acf1fd..eeac7ec 100644 (file)
@@ -1935,6 +1935,10 @@ GC_EXTERN GC_bool GC_print_back_height;
                              size_t bytes2);
 #endif
 
+#ifdef CAN_HANDLE_FORK
+  GC_EXTERN GC_bool GC_handle_fork;
+#endif
+
 #ifndef GC_DISABLE_INCREMENTAL
   GC_EXTERN GC_bool GC_dirty_maintained;
                                 /* Dirty bits are being maintained,     */
index d128b83..ae3e4a5 100644 (file)
 # define IF_CANCEL(x) /* empty */
 #endif
 
-#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
+#if !defined(CAN_HANDLE_FORK) && !defined(NO_HANDLE_FORK) \
+    && ((defined(GC_PTHREADS) && !defined(HURD) && !defined(NACL) \
+         && !defined(PLATFORM_ANDROID) && !defined(GC_WIN32_PTHREADS) \
+         && !defined(USE_WINALLOC)) \
+        || defined(HANDLE_FORK))
+  /* Attempts (where supported and requested) to make GC_malloc work in */
+  /* a child process fork'ed from a multi-threaded parent.              */
+# define CAN_HANDLE_FORK
 #endif
 
 #if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \
diff --git a/misc.c b/misc.c
index 4ad474f..1989364 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -169,7 +169,31 @@ STATIC void * GC_CALLBACK GC_default_oom_fn(
 /* All accesses to it should be synchronized to avoid data races.       */
 GC_oom_func GC_oom_fn = GC_default_oom_fn;
 
-/* Set things up so that GC_size_map[i] >= granules(i),         */
+#ifdef CAN_HANDLE_FORK
+# ifdef HANDLE_FORK
+    GC_INNER GC_bool GC_handle_fork = TRUE;
+                        /* The value is examined by GC_thr_init.        */
+# else
+    GC_INNER GC_bool GC_handle_fork = FALSE;
+# endif
+#endif /* CAN_HANDLE_FORK */
+
+/* Overrides the default handle-fork mode.  Non-zero value means GC     */
+/* should install proper pthread_atfork handlers (or abort if not       */
+/* supported).  Has effect only if called before GC_INIT.               */
+GC_API void GC_CALL GC_set_handle_fork(int value GC_ATTR_UNUSED)
+{
+# ifdef CAN_HANDLE_FORK
+    if (!GC_is_initialized)
+      GC_handle_fork = (GC_bool)value;
+# elif defined(THREADS)
+    /* FIXME: Handle Darwin case. */
+    if (!GC_is_initialized && value)
+      ABORT("fork() handling disabled");
+# endif
+}
+
+/* Set things up so that GC_size_map[i] >= granules(i),                 */
 /* but not too much bigger                                              */
 /* and so that size_map contains relatively few distinct entries        */
 /* This was originally stolen from Russ Atkinson's Cedar                */
index 272a734..fb28bbe 100644 (file)
@@ -645,7 +645,7 @@ GC_API int GC_CALL GC_thread_is_registered(void)
     return me != NULL;
 }
 
-#ifdef HANDLE_FORK
+#ifdef CAN_HANDLE_FORK
 /* Remove all entries from the GC_threads table, except the     */
 /* one for the current thread.  We need to do this in the child */
 /* process after a fork(), since only the current thread        */
@@ -689,7 +689,7 @@ STATIC void GC_remove_all_threads_but_me(void)
       GC_threads[hv] = me;
     }
 }
-#endif /* HANDLE_FORK */
+#endif /* CAN_HANDLE_FORK */
 
 #ifdef USE_PROC_FOR_LIBRARIES
   GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo, ptr_t hi)
@@ -918,7 +918,7 @@ STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all)
     }
 }
 
-#ifdef HANDLE_FORK
+#ifdef CAN_HANDLE_FORK
 /* Procedures called before and after a fork.  The goal here is to make */
 /* it safe to call GC_malloc() in a forked child.  It's unclear that is */
 /* attainable, since the single UNIX spec seems to imply that one       */
@@ -995,7 +995,7 @@ STATIC void GC_fork_child_proc(void)
     RESTORE_CANCEL(fork_cancel_state);
     UNLOCK();
 }
-#endif /* HANDLE_FORK */
+#endif /* CAN_HANDLE_FORK */
 
 #ifdef INCLUDE_LINUX_THREAD_DESCR
   __thread int GC_dummy_thread_local;
@@ -1013,10 +1013,11 @@ GC_INNER void GC_thr_init(void)
   GC_thr_initialized = TRUE;
 
   GC_ASSERT((word)&GC_threads % sizeof(word) == 0);
-# ifdef HANDLE_FORK
-    /* Prepare for a possible fork.     */
-    if (pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
-                       GC_fork_child_proc) != 0)
+# ifdef CAN_HANDLE_FORK
+    /* Prepare for forks if requested.  */
+    if (GC_handle_fork
+        && pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
+                          GC_fork_child_proc) != 0)
       ABORT("pthread_atfork failed");
 # endif
 # ifdef INCLUDE_LINUX_THREAD_DESCR
index 5c302da..be294e3 100644 (file)
 #   include <pthread.h>
 # endif
 
-# if defined(THREADS) && defined(HANDLE_FORK)
+# if (!defined(THREADS) || !defined(HANDLE_FORK) \
+      || (defined(DARWIN) && defined(MPROTECT_VDB) \
+          && !defined(NO_INCREMENTAL) && !defined(MAKE_BACK_GRAPH))) \
+     && !defined(NO_TEST_HANDLE_FORK)
+#   define NO_TEST_HANDLE_FORK
+# endif
+
+# ifndef NO_TEST_HANDLE_FORK
 #   include <unistd.h>
+#   define INIT_FORK_SUPPORT GC_set_handle_fork(1)
+# else
+#   define INIT_FORK_SUPPORT /* empty */
 # endif
 
 # if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
 #if defined(CYGWIN32) || defined (AIX) || defined(DARWIN) \
         || defined(PLATFORM_ANDROID) || defined(THREAD_LOCAL_ALLOC) \
         || (defined(MSWINCE) && !defined(GC_WINMAIN_REDIRECT))
-#  define GC_COND_INIT() GC_INIT(); CHECH_GCLIB_VERSION; INIT_PRINT_STATS
+#  define GC_OPT_INIT GC_INIT()
 #else
-#  define GC_COND_INIT() CHECH_GCLIB_VERSION; INIT_PRINT_STATS
+#  define GC_OPT_INIT /* empty */
 #endif
 
+#define GC_COND_INIT() \
+    INIT_FORK_SUPPORT; GC_OPT_INIT; CHECH_GCLIB_VERSION; INIT_PRINT_STATS
+
 #define CHECK_OUT_OF_MEMORY(p) \
             if ((p) == NULL) { \
               GC_printf("Out of memory\n"); \
@@ -1286,11 +1299,7 @@ void run_one_test(void)
     /* GC_allocate_ml and GC_need_to_lock are no longer exported, and   */
     /* AO_fetch_and_add1() may be unavailable to update a counter.      */
     (void)GC_call_with_alloc_lock(inc_int_counter, &n_tests);
-#   if defined(THREADS) && defined(HANDLE_FORK) \
-       && (!defined(DARWIN) || !defined(MPROTECT_VDB) \
-           || defined(NO_INCREMENTAL) || defined(MAKE_BACK_GRAPH))
-           /* FIXME: fork() is not tested on Darwin if incremental mode */
-           /* is on for now (till it would be handled properly).        */
+#   ifndef NO_TEST_HANDLE_FORK
       if (fork() == 0) {
         GC_gcollect();
         tiny_reverse_test(0);
index 0fd65a5..bf682d1 100644 (file)
@@ -55,7 +55,7 @@
   STATIC void GC_thread_exit_proc(void *arg);
 
 # include <pthread.h>
-# ifdef HANDLE_FORK
+# ifdef CAN_HANDLE_FORK
 #   include <unistd.h>
 # endif
 
@@ -987,7 +987,9 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
     }
   }
 
-# ifdef HANDLE_FORK
+#endif /* GC_PTHREADS */
+
+#ifdef CAN_HANDLE_FORK
     /* Similar to that in pthread_support.c but also rehashes the table */
     /* since hash map key (thread_id) differs from that in the parent.  */
     STATIC void GC_remove_all_threads_but_me(void)
@@ -1079,9 +1081,7 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
       GC_remove_all_threads_but_me();
       UNLOCK();
     }
-# endif /* HANDLE_FORK */
-
-#endif /* GC_PTHREADS */
+#endif /* CAN_HANDLE_FORK */
 
 void GC_push_thread_structures(void)
 {
@@ -2383,10 +2383,11 @@ 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)
+# ifdef CAN_HANDLE_FORK
+    /* Prepare for forks if requested.  */
+    if (GC_handle_fork
+        && pthread_atfork(GC_fork_prepare_proc, GC_fork_parent_proc,
+                          GC_fork_child_proc) != 0)
       ABORT("pthread_atfork failed");
 # endif