summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2012-11-21 22:26:29 +0400
committerIvan Maidanski <ivmai@mail.ru>2012-11-21 22:31:30 +0400
commit0a420dc3f8b69a5675b504f7975d170ee79f1ee2 (patch)
tree21475965be9b38a050f22afbaf90b9cdf9b94026
parentff93a74691be86ed6ae4f9572a028e7eb9e5c944 (diff)
downloadbdwgc-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.h25
-rw-r--r--include/private/gc_priv.h15
-rw-r--r--include/private/gcconfig.h18
-rw-r--r--misc.c42
-rw-r--r--pthread_support.c34
-rw-r--r--tests/test.c18
-rw-r--r--win32_threads.c36
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. */
diff --git a/misc.c b/misc.c
index 861f00d7..7d3a1e9f 100644
--- a/misc.c
+++ b/misc.c
@@ -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. */