diff options
author | Ivan Maidanski <ivmai@mail.ru> | 2017-08-22 11:23:27 +0300 |
---|---|---|
committer | Ivan Maidanski <ivmai@mail.ru> | 2017-08-22 11:23:27 +0300 |
commit | 119a2a5e58d982ba2a6b10781b13bbcc9ccaf160 (patch) | |
tree | fb4971b75fdd0716d9378c0b0b2e0d44d0f042e8 /pthread_support.c | |
parent | 4abb9eb1684c0bc6387f96cc4470ed08dcdce478 (diff) | |
download | bdwgc-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.c | 19 |
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"); } |