(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.
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
/* 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);
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, */
# 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) \
/* 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 */
}
#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 */
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)
}
}
-#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 */
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. */
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
# 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)
#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"); \
/* 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);
STATIC void GC_thread_exit_proc(void *arg);
# include <pthread.h>
-# ifdef HANDLE_FORK
+# ifdef CAN_HANDLE_FORK
# include <unistd.h>
# endif
}
}
-# 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)
GC_remove_all_threads_but_me();
UNLOCK();
}
-# endif /* HANDLE_FORK */
-
-#endif /* GC_PTHREADS */
+#endif /* CAN_HANDLE_FORK */
void GC_push_thread_structures(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