* 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.
/* 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);
#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
#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. */
#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
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
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 */
# 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
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
STATIC void GC_thread_exit_proc(void *arg);
# include <pthread.h>
-# ifdef CAN_HANDLE_FORK
+# ifdef CAN_CALL_ATFORK
# include <unistd.h>
# endif
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)
# 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. */