]> granicus.if.org Git - gc/commitdiff
Fix GC allocation mutex in child after a fork
authorGustavo Giraldez <ggiraldez@manas.com.ar>
Thu, 18 Jan 2018 21:52:25 +0000 (00:52 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Thu, 18 Jan 2018 21:52:25 +0000 (00:52 +0300)
Even though after a fork the child only inherits the single thread
that called the fork(), if another thread in the parent was attempting
to lock the mutex while being held in fork_child_prepare(), the mutex
will be left in an inconsistent state in the child after the UNLOCK().
This is the case, at least, in Mac OS X and leads to an unusable GC in
the child which will block when attempting to perform any GC operation
that acquires the mutex.

* pthread_support.c [CAN_HANDLE_FORK && USE_PTHREAD_LOCKS]
(fork_child_proc): Add assertion (after UNLOCK) that the lock is not
held actually, and, then, re-initialize GC_allocate_ml; add comments.

pthread_support.c

index bb8a0d58e94e29b61161551dba9a82cc28d59281..f22570d1a78725bb463a0735f16f9a516a66ecee 100644 (file)
@@ -1117,6 +1117,26 @@ static void fork_child_proc(void)
 #   endif /* PARALLEL_MARK */
     RESTORE_CANCEL(fork_cancel_state);
     UNLOCK();
+    /* Even though after a fork the child only inherits the single      */
+    /* thread that called the fork(), if another thread in the parent   */
+    /* was attempting to lock the mutex while being held in             */
+    /* fork_child_prepare(), the mutex will be left in an inconsistent  */
+    /* state in the child after the UNLOCK.  This is the case, at       */
+    /* least, in Mac OS X and leads to an unusable GC in the child      */
+    /* which will block when attempting to perform any GC operation     */
+    /* that acquires the allocation mutex.                              */
+#   ifdef USE_PTHREAD_LOCKS
+      GC_ASSERT(I_DONT_HOLD_LOCK());
+      /* Reinitialize the mutex.  It should be safe since we are        */
+      /* running this in the child which only inherits a single thread. */
+      /* mutex_destroy() may return EBUSY, which makes no sense, but    */
+      /* that is the reason for the need of the reinitialization.       */
+      (void)pthread_mutex_destroy(&GC_allocate_ml);
+      /* TODO: Probably some targets might need the default mutex       */
+      /* attribute to be passed instead of NULL.                        */
+      if (0 != pthread_mutex_init(&GC_allocate_ml, NULL))
+        ABORT("pthread_mutex_init failed (in child)");
+#   endif
 }
 
   /* Routines for fork handling by client (no-op if pthread_atfork works). */