diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2012-11-21 22:26:29 +0400 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2012-11-21 22:31:30 +0400 |
commit | 0a420dc3f8b69a5675b504f7975d170ee79f1ee2 (patch) | |
tree | 21475965be9b38a050f22afbaf90b9cdf9b94026 | |
parent | ff93a74691be86ed6ae4f9572a028e7eb9e5c944 (diff) | |
download | bdwgc-0a420dc3f8b69a5675b504f7975d170ee79f1ee2.tar.gz |
Add manual POSIX fork handling support (Android)
* include/gc.h (GC_set_handle_fork): Update comment.
* include/gc.h (GC_atfork_prepare, GC_atfork_parent, GC_atfork_child):
New API proto.
* include/private/gc_priv.h (GC_handle_fork): Change type from GC_bool
to int (to hold a value of -1).
* misc.c (GC_handle_fork): Likewise.
* include/private/gc_priv.h (GC_handle_fork): Add comment.
* misc.c (GC_set_handle_fork): Likewise.
* include/private/gcconfig.h (CAN_HANDLE_FORK): Define also for HURD
and PLATFORM_ANDROID; do not define if HAVE_NO_FORK already defined.
* include/private/gcconfig.h (CAN_CALL_ATFORK): New macro (defined if
CAN_HANDLE_FORK but not HURD or PLATFORM_ANDROID).
* include/private/gcconfig.h (HAVE_NO_FORK): New macro (defined for
Win32, OS/2 and others).
* misc.c (GC_atfork_prepare, GC_atfork_parent, GC_atfork_child): New
API function definition (only if not CAN_HANDLE_FORK and not
HAVE_NO_FORK).
* misc.c (GC_handle_fork): Map all negative values of argument except
for -1 to a positive one stored to GC_handle_fork; call GC_init to
initialize GC_stderr before ABORT invocation (only if not
SMALL_CONFIG).
* pthread_support.c (GC_atfork_prepare, GC_atfork_parent,
GC_atfork_child): New API function definition (only if
CAN_HANDLE_FORK).
* win32_threads.c (GC_atfork_prepare, GC_atfork_parent,
GC_atfork_child): Likewise.
* pthread_support.c (GC_thr_init): No pthread_atfork call if not
CAN_CALL_ATFORK; adjust GC_handle_fork value if pthread_atfork
succeeds; do not about in case of pthread_atfork failure provided
GC_handle_fork is -1 (only if CAN_HANDLE_FORK).
* win32_threads.c (GC_thr_init): Likewise.
* tests/test.c (TEST_FORK_WITHOUT_ATFORK): Recognize new macro (do not
define NO_TEST_HANDLE_FORK in this case and set INIT_FORK_SUPPORT to
no-op).
* tests/test.c (INIT_FORK_SUPPORT): Define to GC_set_handle_fork(-1)
unless HANDLE_FORK, or NO_TEST_HANDLE_FORK or TEST_FORK_WITHOUT_ATFORK.
* tests/test.c (run_one_test): Surround fork() invocation with
GC_atfork_prepare, GC_atfork_parent, GC_atfork_child calls.
* win32_threads.c: Include unistd.h if CAN_CALL_ATFORK defined
(instead of CAN_HANDLE_FORK) to get pthread_atfork prototype.
-rw-r--r-- | include/gc.h | 25 | ||||
-rw-r--r-- | include/private/gc_priv.h | 15 | ||||
-rw-r--r-- | include/private/gcconfig.h | 18 | ||||
-rw-r--r-- | misc.c | 42 | ||||
-rw-r--r-- | pthread_support.c | 34 | ||||
-rw-r--r-- | tests/test.c | 18 | ||||
-rw-r--r-- | win32_threads.c | 36 |
7 files changed, 159 insertions, 29 deletions
diff --git a/include/gc.h b/include/gc.h index c7c4f00b..0a56dce1 100644 --- a/include/gc.h +++ b/include/gc.h @@ -365,12 +365,29 @@ 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.) */ +/* called before GC_INIT. Clients should invoke GC_set_handle_fork */ +/* with non-zero argument 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_set_handle_fork */ +/* instructs GC_init to setup GC fork handlers using pthread_atfork, */ +/* the latter might fail (or, even, absent on some targets) causing */ +/* abort at GC initialization. Starting from 7.3alpha3, problems with */ +/* missing (or failed) pthread_atfork() could be avoided by invocation */ +/* of GC_set_handle_fork(-1) at application start-up and surrounding */ +/* each fork() with the relevant GC_atfork_prepare/parent/child calls. */ GC_API void GC_CALL GC_set_handle_fork(int); +/* Routines to handle POSIX fork() manually (no-op if handled */ +/* automatically). GC_atfork_prepare should be called immediately */ +/* before fork(); GC_atfork_parent should be invoked just after fork in */ +/* the branch that corresponds to parent process (i.e., fork result is */ +/* non-zero); GC_atfork_child is to be called immediately in the child */ +/* branch (i.e., fork result is 0). Note that GC_atfork_child() call */ +/* should, of course, precede GC_start_mark_threads call (if any). */ +GC_API void GC_CALL GC_atfork_prepare(void); +GC_API void GC_CALL GC_atfork_parent(void); +GC_API void GC_CALL GC_atfork_child(void); + /* 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 ce9cb569..2d716d4b 100644 --- a/include/private/gc_priv.h +++ b/include/private/gc_priv.h @@ -1961,7 +1961,20 @@ GC_EXTERN GC_bool GC_print_back_height; #endif #ifdef CAN_HANDLE_FORK - GC_EXTERN GC_bool GC_handle_fork; + GC_EXTERN int GC_handle_fork; + /* Fork-handling mode: */ + /* 0 means no fork handling requested (but client could */ + /* anyway call fork() provided it is surrounded with */ + /* GC_atfork_prepare/parent/child calls); */ + /* -1 means GC tries to use pthread_at_fork if it is */ + /* available (if it succeeds then GC_handle_fork value */ + /* is changed to 1), client should nonetheless surround */ + /* fork() with GC_atfork_prepare/parent/child (for the */ + /* case of pthread_at_fork failure or absence); */ + /* 1 (or other values) means client fully relies on */ + /* pthread_at_fork (so if it is missing or failed then */ + /* abort occurs in GC_init), GC_atfork_prepare and the */ + /* accompanying routines are no-op in such a case. */ #endif #ifndef GC_DISABLE_INCREMENTAL diff --git a/include/private/gcconfig.h b/include/private/gcconfig.h index a35f4edb..c21c38ca 100644 --- a/include/private/gcconfig.h +++ b/include/private/gcconfig.h @@ -2656,15 +2656,27 @@ #endif #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(HAVE_NO_FORK) \ + && ((defined(GC_PTHREADS) && !defined(NACL) \ + && !defined(GC_WIN32_PTHREADS) && !defined(USE_WINALLOC)) \ || (defined(DARWIN) && defined(MPROTECT_VDB)) || 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(CAN_HANDLE_FORK) && !defined(CAN_CALL_ATFORK) \ + && !defined(HURD) && !defined(PLATFORM_ANDROID) + /* Have working pthread_atfork(). */ +# define CAN_CALL_ATFORK +#endif + +#if !defined(CAN_HANDLE_FORK) && !defined(HAVE_NO_FORK) \ + && (defined(MSWIN32) || defined(MSWINCE) || defined(DOS4GW) \ + || defined(OS2) || defined(SYMBIAN) /* and probably others ... */) +# define HAVE_NO_FORK +#endif + #if !defined(USE_MARK_BITS) && !defined(USE_MARK_BYTES) \ && defined(PARALLEL_MARK) /* Minimize compare-and-swap usage. */ @@ -171,24 +171,48 @@ GC_oom_func GC_oom_fn = GC_default_oom_fn; #ifdef CAN_HANDLE_FORK # ifdef HANDLE_FORK - GC_INNER GC_bool GC_handle_fork = TRUE; + GC_INNER int GC_handle_fork = 1; /* The value is examined by GC_thr_init. */ # else - GC_INNER GC_bool GC_handle_fork = FALSE; + GC_INNER int 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. */ +#elif !defined(HAVE_NO_FORK) + + /* Same as above but with GC_CALL calling conventions. */ + GC_API void GC_CALL GC_atfork_prepare(void) + { +# ifdef THREADS + ABORT("fork() handling unsupported"); +# endif + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + /* empty */ + } + + GC_API void GC_CALL GC_atfork_child(void) + { + /* empty */ + } +#endif /* !CAN_HANDLE_FORK && !HAVE_NO_FORK */ + +/* Overrides the default automatic handle-fork mode. 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; + GC_handle_fork = value >= -1 ? value : 1; + /* Map all negative values except for -1 to a positive one. */ # elif defined(THREADS) || (defined(DARWIN) && defined(MPROTECT_VDB)) - if (!GC_is_initialized && value) - ABORT("fork() handling disabled"); + if (!GC_is_initialized && value) { +# ifndef SMALL_CONFIG + GC_init(); /* just to initialize GC_stderr */ +# endif + ABORT("fork() handling unsupported"); + } # else /* No at-fork handler is needed in the single-threaded mode. */ # endif diff --git a/pthread_support.c b/pthread_support.c index 7b53fe0a..f270b59e 100644 --- a/pthread_support.c +++ b/pthread_support.c @@ -992,6 +992,25 @@ static void fork_child_proc(void) RESTORE_CANCEL(fork_cancel_state); UNLOCK(); } + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { + if (GC_handle_fork <= 0) + fork_prepare_proc(); + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + if (GC_handle_fork <= 0) + fork_parent_proc(); + } + + GC_API void GC_CALL GC_atfork_child(void) + { + if (GC_handle_fork <= 0) + fork_child_proc(); + } #endif /* CAN_HANDLE_FORK */ #ifdef INCLUDE_LINUX_THREAD_DESCR @@ -1009,10 +1028,17 @@ GC_INNER void GC_thr_init(void) GC_ASSERT((word)&GC_threads % sizeof(word) == 0); # ifdef CAN_HANDLE_FORK /* Prepare for forks if requested. */ - if (GC_handle_fork - && pthread_atfork(fork_prepare_proc, fork_parent_proc, - fork_child_proc) != 0) - ABORT("pthread_atfork failed"); + if (GC_handle_fork) { +# ifdef CAN_CALL_ATFORK + if (pthread_atfork(fork_prepare_proc, fork_parent_proc, + fork_child_proc) == 0) { + /* Handlers successfully registered. */ + GC_handle_fork = 1; + } else +# endif + /* else */ if (GC_handle_fork != -1) + ABORT("pthread_atfork failed"); + } # endif # ifdef INCLUDE_LINUX_THREAD_DESCR /* Explicitly register the region including the address */ diff --git a/tests/test.c b/tests/test.c index e2204af7..189aee33 100644 --- a/tests/test.c +++ b/tests/test.c @@ -89,15 +89,24 @@ # if (!defined(THREADS) || !defined(HANDLE_FORK) \ || (defined(DARWIN) && defined(MPROTECT_VDB) \ && !defined(NO_INCREMENTAL) && !defined(MAKE_BACK_GRAPH))) \ - && !defined(NO_TEST_HANDLE_FORK) && !defined(TEST_HANDLE_FORK) + && !defined(NO_TEST_HANDLE_FORK) && !defined(TEST_HANDLE_FORK) \ + && !defined(TEST_FORK_WITHOUT_ATFORK) # define NO_TEST_HANDLE_FORK # endif # ifndef NO_TEST_HANDLE_FORK # include <unistd.h> -# define INIT_FORK_SUPPORT GC_set_handle_fork(1) +# ifdef HANDLE_FORK +# define INIT_FORK_SUPPORT GC_set_handle_fork(1) /* Causes abort in GC_init on pthread_atfork failure. */ -# else +# elif !defined(TEST_FORK_WITHOUT_ATFORK) +# define INIT_FORK_SUPPORT GC_set_handle_fork(-1) + /* Passing -1 implies fork() should be as well manually */ + /* surrounded with GC_atfork_prepare/parent/child. */ +# endif +# endif + +# ifndef INIT_FORK_SUPPORT # define INIT_FORK_SUPPORT /* empty */ # endif @@ -1264,10 +1273,13 @@ void run_one_test(void) GC_free(GC_malloc(0)); GC_free(GC_malloc_atomic(0)); # ifndef NO_TEST_HANDLE_FORK + GC_atfork_prepare(); if (fork() != 0) { + GC_atfork_parent(); if (print_stats) GC_log_printf("Forked child process (or failed)\n"); } else { + GC_atfork_child(); if (print_stats) GC_log_printf("Started a child process\n"); # ifdef THREADS diff --git a/win32_threads.c b/win32_threads.c index 4e25bc71..83b2c420 100644 --- a/win32_threads.c +++ b/win32_threads.c @@ -60,7 +60,7 @@ STATIC void GC_thread_exit_proc(void *arg); # include <pthread.h> -# ifdef CAN_HANDLE_FORK +# ifdef CAN_CALL_ATFORK # include <unistd.h> # endif @@ -1085,6 +1085,25 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn, GC_remove_all_threads_but_me(); UNLOCK(); } + + /* Routines for fork handling by client (no-op if pthread_atfork works). */ + GC_API void GC_CALL GC_atfork_prepare(void) + { + if (GC_handle_fork <= 0) + fork_prepare_proc(); + } + + GC_API void GC_CALL GC_atfork_parent(void) + { + if (GC_handle_fork <= 0) + fork_parent_proc(); + } + + GC_API void GC_CALL GC_atfork_child(void) + { + if (GC_handle_fork <= 0) + fork_child_proc(); + } #endif /* CAN_HANDLE_FORK */ void GC_push_thread_structures(void) @@ -2404,10 +2423,17 @@ GC_INNER void GC_thr_init(void) # ifdef CAN_HANDLE_FORK /* Prepare for forks if requested. */ - if (GC_handle_fork - && pthread_atfork(fork_prepare_proc, fork_parent_proc, - fork_child_proc) != 0) - ABORT("pthread_atfork failed"); + if (GC_handle_fork) { +# ifdef CAN_CALL_ATFORK + if (pthread_atfork(fork_prepare_proc, fork_parent_proc, + fork_child_proc) == 0) { + /* Handlers successfully registered. */ + GC_handle_fork = 1; + } else +# endif + /* else */ if (GC_handle_fork != -1) + ABORT("pthread_atfork failed"); + } # endif /* Add the initial thread, so we can stop it. */ |