From: Ivan Maidanski Date: Mon, 2 Apr 2012 16:18:12 +0000 (+0400) Subject: Add public GC_set_handle_fork to control forked child handling support X-Git-Tag: gc7_3alpha2~30 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1e882b98c2cf9479a9cd08a67439dab7f9622924;p=platform%2Fupstream%2Flibgc.git Add public GC_set_handle_fork to control forked child handling support * 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. --- diff --git a/configure.ac b/configure.ac index 9307218..b61cf81 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/include/gc.h b/include/gc.h index 3902694..d2e13d5 100644 --- a/include/gc.h +++ b/include/gc.h @@ -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); diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h index 4acf1fd..eeac7ec 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -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, */ diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index d128b83..ae3e4a5 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -2638,12 +2638,14 @@ # 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 --- 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 */ diff --git a/pthread_support.c b/pthread_support.c index 272a734..fb28bbe 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -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 diff --git a/tests/test.c b/tests/test.c index 5c302da..be294e3 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(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); diff --git a/win32_threads.c b/win32_threads.c index 0fd65a5..bf682d1 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 @@ -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