summaryrefslogtreecommitdiff
path: root/pthread_support.c
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2017-08-22 11:23:27 +0300
committerIvan Maidanski <ivmai@mail.ru>2017-08-22 11:23:27 +0300
commit119a2a5e58d982ba2a6b10781b13bbcc9ccaf160 (patch)
treefb4971b75fdd0716d9378c0b0b2e0d44d0f042e8 /pthread_support.c
parent4abb9eb1684c0bc6387f96cc4470ed08dcdce478 (diff)
downloadbdwgc-119a2a5e58d982ba2a6b10781b13bbcc9ccaf160.tar.gz
Fix deadlock in GC_help_marker caused by use of mark_cv of parent process
The marker threads of the parent process are blocked on mark_cv at fork. So pthread_cond_wait() malfunction (hang) is possible in the child process without mark_cv state cleanup (reinitialization). * pthread_support.c [PARALLEL_MARK] (mark_cv): Move static variable definition upper to be before GC_start_mark_threads_inner). * win32_threads.c [GC_PTHREADS_PARAMARK] (mark_cv): Likewise. * pthread_support.c [PARALLEL_MARK && CAN_HANDLE_FORK] (mark_cv): Do not initialize statically; add comment. * win32_threads.c [GC_PTHREADS_PARAMARK && CAN_HANDLE_FORK] (mark_cv): Likewise. * pthread_support.c [PARALLEL_MARK && CAN_HANDLE_FORK] (GC_start_mark_threads_inner): Initialize mark_cv to PTHREAD_COND_INITIALIZER (unless available_markers_m1 <= 0 or GC_parallel); add comment. * win32_threads.c [GC_PTHREADS_PARAMARK && CAN_HANDLE_FORK] (GC_start_mark_threads_inner): Likewise. * pthread_support.c [PARALLEL_MARK] (GC_wait_marker, GC_notify_all_marker): Add assertion that GC_parallel is true (so mark_cv is initialized). * win32_threads.c [GC_PTHREADS_PARAMARK] (GC_wait_marker, GC_notify_all_marker): Likewise.
Diffstat (limited to 'pthread_support.c')
-rw-r--r--pthread_support.c19
1 files changed, 17 insertions, 2 deletions
diff --git a/pthread_support.c b/pthread_support.c
index 77e051c3..9c973161 100644
--- a/pthread_support.c
+++ b/pthread_support.c
@@ -385,8 +385,11 @@ STATIC pthread_t GC_mark_threads[MAX_MARKERS];
#ifdef CAN_HANDLE_FORK
static int available_markers_m1 = 0;
+ static pthread_cond_t mark_cv;
+ /* initialized by GC_start_mark_threads_inner */
#else
# define available_markers_m1 GC_markers_m1
+ static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
#endif
GC_INNER void GC_start_mark_threads_inner(void)
@@ -402,6 +405,18 @@ GC_INNER void GC_start_mark_threads_inner(void)
/* Skip if parallel markers disabled or already started. */
# ifdef CAN_HANDLE_FORK
if (GC_parallel) return;
+
+ /* Initialize mark_cv (for the first time), or cleanup its value */
+ /* after forking in the child process. All the marker threads in */
+ /* the parent process were blocked on this variable at fork, so */
+ /* pthread_cond_wait() malfunction (hang) is possible in the */
+ /* child process without such a cleanup. */
+ /* TODO: This is not portable, it is better to shortly unblock */
+ /* all marker threads in the parent process at fork. */
+ {
+ pthread_cond_t mark_cv_local = PTHREAD_COND_INITIALIZER;
+ BCOPY(&mark_cv_local, &mark_cv, sizeof(mark_cv));
+ }
# endif
GC_ASSERT(GC_fl_builder_count == 0);
@@ -2147,11 +2162,10 @@ GC_INNER void GC_notify_all_builder(void)
}
}
-static pthread_cond_t mark_cv = PTHREAD_COND_INITIALIZER;
-
GC_INNER void GC_wait_marker(void)
{
ASSERT_CANCEL_DISABLED();
+ GC_ASSERT(GC_parallel);
UNSET_MARK_LOCK_HOLDER;
if (pthread_cond_wait(&mark_cv, &mark_mutex) != 0) {
ABORT("pthread_cond_wait failed");
@@ -2162,6 +2176,7 @@ GC_INNER void GC_wait_marker(void)
GC_INNER void GC_notify_all_marker(void)
{
+ GC_ASSERT(GC_parallel);
if (pthread_cond_broadcast(&mark_cv) != 0) {
ABORT("pthread_cond_broadcast failed");
}