]> granicus.if.org Git - gc/commitdiff
Fix deadlock in GC_suspend_thread when thread is rebuilding free list
authorIvan Maidanski <ivmai@mail.ru>
Fri, 21 Jul 2017 17:27:12 +0000 (20:27 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Mon, 7 Aug 2017 21:50:10 +0000 (00:50 +0300)
(fix commits 62097c359e2bcf)

* pthread_stop_world.c [GC_ENABLE_SUSPEND_THREAD] (GC_suspend_thread):
Move DISABLE_CANCEL() upper to cover also GC_wait_for_reclaim() and
RAISE_SIGNAL() calls.
* pthread_stop_world.c [GC_ENABLE_SUSPEND_THREAD && PARALLEL_MARK]
(GC_suspend_thread): If GC_parallel then call GC_wait_for_reclaim()
holding the allocation lock before RAISE_SIGNAL() call; add comment.

pthread_stop_world.c

index 07a2309ea2867d3fef3b97cc95c3f01f2c121cb5..b866e4a275bf880af647597ecc7725c83ffd8e7a 100644 (file)
@@ -439,6 +439,20 @@ STATIC void GC_restart_handler(int sig)
         return;
       }
 
+      DISABLE_CANCEL(cancel_state);
+                /* GC_suspend_thread is not a cancellation point.   */
+#     ifdef PARALLEL_MARK
+        /* Ensure we do not suspend a thread while it is rebuilding */
+        /* a free list, otherwise such a dead-lock is possible:     */
+        /* thread 1 is blocked in GC_wait_for_reclaim holding       */
+        /* the allocation lock, thread 2 is suspended in            */
+        /* GC_reclaim_generic invoked from GC_generic_malloc_many   */
+        /* (with GC_fl_builder_count > 0), and thread 3 is blocked  */
+        /* acquiring the allocation lock in GC_resume_thread.       */
+        if (GC_parallel)
+          GC_wait_for_reclaim();
+#     endif
+
       /* TODO: Support GC_retry_signals */
       switch (RAISE_SIGNAL(t, GC_sig_suspend)) {
       /* ESRCH cannot happen as terminated threads are handled above.   */
@@ -451,8 +465,6 @@ STATIC void GC_restart_handler(int sig)
       /* Wait for the thread to complete threads table lookup and   */
       /* stack_ptr assignment.                                      */
       GC_ASSERT(GC_thr_initialized);
-      DISABLE_CANCEL(cancel_state);
-                /* GC_suspend_thread is not a cancellation point.   */
       while (sem_wait(&GC_suspend_ack_sem) != 0) {
         if (errno != EINTR)
           ABORT("sem_wait for handler failed (suspend_self)");