]> granicus.if.org Git - gc/commitdiff
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>
Tue, 3 Apr 2012 04:11:06 +0000 (08:11 +0400)
(Apply commit 1e882b9 from 'master' branch)

* 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 b34e0c9013a63f30c6c5aacfd22df811f1d2aebb..2438a360ddd2d369244d11cf3a44471f90dc2a76 100644 (file)
@@ -692,10 +692,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 7a6da5bbae1fe75afca1707308c0ff9afd33b768..59695b97dea89f0ee8fb405ab9bad7210e235dfc 100644 (file)
@@ -344,6 +344,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 c49aea4e6fb044c3f0cb02e6d3340ac5bc0927b8..43e7f26edbdd45c15421ce04f46d00327887ccfb 100644 (file)
@@ -1878,6 +1878,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 1cfa8359e95cbbb9e8a324bb79fee09c023563f7..7ecc80f7bc85f7f49387f8778fe01928767c2ab7 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)
-  /* 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(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 9d5456776f4d381e3b6b25772db0dd6b6afdf238..fac069aaa893b4a35e9df6aacd9dd23eb3419322 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -151,7 +151,32 @@ STATIC void * GC_CALLBACK GC_default_oom_fn(size_t bytes_requested)
 /* 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.               */
+/*ARGSUSED*/
+GC_API void GC_CALL GC_set_handle_fork(int value)
+{
+# 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 26ef6e2d147c2f3ca945594adbf4dfbd08e59922..41234ab1816184edeb69f2a6cb6756f7a1d95943 100644 (file)
@@ -613,7 +613,7 @@ GC_INNER unsigned char *GC_check_finalizer_nested(void)
   }
 #endif /* GC_ASSERTIONS && THREAD_LOCAL_ALLOC */
 
-#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        */
@@ -657,7 +657,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)
@@ -823,7 +823,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       */
@@ -901,7 +901,7 @@ STATIC void GC_fork_child_proc(void)
     RESTORE_CANCEL(fork_cancel_state);
     UNLOCK();
 }
-#endif /* HANDLE_FORK */
+#endif /* CAN_HANDLE_FORK */
 
 #if defined(GC_DGUX386_THREADS)
   /* Return the number of processors, or i<= 0 if it can't be determined. */
@@ -956,10 +956,11 @@ GC_INNER void GC_thr_init(void)
   if (GC_thr_initialized) return;
   GC_thr_initialized = TRUE;
 
-# 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 4366369d779d6ab6d2e41a9b8358f66f61e83008..7912b06ad246019cd87a6034795706c2f96b11cd 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(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"); \
@@ -1253,11 +1266,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 0d8d3a73492090f26bfac6108eec142e198c4e90..4017b509534b86348875c069f3940c7f54809e9e 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
 
@@ -974,7 +974,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)
@@ -1067,9 +1069,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)
 {
@@ -2374,10 +2374,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