summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2012-04-02 18:18:12 +0200
committerIvan Maidanski <ivmai@mail.ru>2012-04-03 06:11:06 +0200
commit1555aa8dd31db657c0ad5eed618757157783c2e5 (patch)
treefb25fe4a88a764a58ade41cf7d0a858cdddc0e3c
parenta0e3b0d8444fcae8dbe6a4927f5dc626df81e0e9 (diff)
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.
-rw-r--r--configure.ac4
-rw-r--r--include/gc.h8
-rw-r--r--include/private/gc_priv.h4
-rw-r--r--include/private/gcconfig.h13
-rw-r--r--misc.c27
-rw-r--r--pthread_support.c17
-rw-r--r--tests/test.c25
-rw-r--r--win32_threads.c19
8 files changed, 83 insertions, 34 deletions
diff --git a/configure.ac b/configure.ac
index b34e0c9..2438a36 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 7a6da5b..59695b9 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 c49aea4..43e7f26 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 1cfa835..7ecc80f 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 9d54567..fac069a 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 26ef6e2..41234ab 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 4366369..7912b06 100644
--- a/tests/test.c
+++ b/tests/test.c
@@ -86,8 +86,18 @@
# 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)
@@ -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 0d8d3a7..4017b50 100644
--- a/win32_threads.c
+++ b/win32_threads.c
@@ -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