From 1555aa8dd31db657c0ad5eed618757157783c2e5 Mon Sep 17 00:00:00 2001 From: Ivan Maidanski Date: Mon, 2 Apr 2012 20:18:12 +0400 Subject: [PATCH] Add public GC_set_handle_fork to control forked child handling support (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 | 4 ++-- include/gc.h | 8 ++++++++ include/private/gc_priv.h | 4 ++++ include/private/gcconfig.h | 13 +++++++------ misc.c | 27 ++++++++++++++++++++++++++- pthread_support.c | 17 +++++++++-------- tests/test.c | 25 +++++++++++++++++-------- win32_threads.c | 19 ++++++++++--------- 8 files changed, 83 insertions(+), 34 deletions(-) diff --git a/configure.ac b/configure.ac index b34e0c90..2438a360 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/include/gc.h b/include/gc.h index 7a6da5bb..59695b97 100644 --- a/include/gc.h +++ b/include/gc.h @@ -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); diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index c49aea4e..43e7f26e 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -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, */ diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index 1cfa8359..7ecc80f7 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -2591,12 +2591,13 @@ # 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 9d545677..fac069aa 100644 --- 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 */ diff --git a/pthread_support.c b/pthread_support.c index 26ef6e2d..41234ab1 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -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 diff --git a/tests/test.c b/tests/test.c index 4366369d..7912b06a 100644 --- a/tests/test.c +++ b/tests/test.c @@ -86,8 +86,18 @@ # include # 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 +# define INIT_FORK_SUPPORT GC_set_handle_fork(1) +# else +# define INIT_FORK_SUPPORT /* empty */ # endif # if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) @@ -113,11 +123,14 @@ #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); diff --git a/win32_threads.c b/win32_threads.c index 0d8d3a73..4017b509 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -55,7 +55,7 @@ STATIC void GC_thread_exit_proc(void *arg); # include -# ifdef HANDLE_FORK +# ifdef CAN_HANDLE_FORK # include # 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 -- 2.40.0