Verified Commit 8249db36 authored by Gabriele Svelto's avatar Gabriele Svelto Committed by Pier Angelo Vendrame
Browse files

Bug 1670885 - Fix post-fork() handlers for PHC/LogAlloc to work on macOS using...

Bug 1670885 - Fix post-fork() handlers for PHC/LogAlloc to work on macOS using unfair locks r=glandium

macOS unfair locks enforce that a lock can only be released by the thread which locked it.

On macOS 11+ this caused the fork()'d child process to raise a SIGILL signal. Confusingly enough this behavior seems to be different on macOS 10.15 and possibly interacted in odd ways with our exception handler if it was installed before fork()-ing.

Differential Revision: https://phabricator.services.mozilla.com/D148287
parent 548f6ea5
Loading
Loading
Loading
Loading
+8 −5
Changes for memory/replace/logalloc/LogAlloc.cpp: 8 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@ static Mutex sMutex MOZ_UNANNOTATED;

#ifndef _WIN32
static void prefork() { sMutex.Lock(); }

static void postfork() { sMutex.Unlock(); }
static void postfork_parent() { sMutex.Unlock(); }
static void postfork_child() { sMutex.Init(); }
#endif

static size_t GetPid() { return size_t(getpid()); }
@@ -206,8 +206,11 @@ void replace_init(malloc_table_t* aTable, ReplaceMallocBridge** aBridge) {
   * in the child process, will never release it, leading to a dead-lock
   * whenever the child process gets the lock. We thus need to ensure no
   * other thread is holding the lock before forking, by acquiring it
   * ourselves, and releasing it after forking, both in the parent and child
   * processes.
   * ourselves, and releasing it after forking in the parent process and
   * resetting it to its initial state in the child process. The latter is
   * important because some implementations (notably macOS) prevent a lock from
   * being unlocked by a different thread than the one which locked it in the
   * first place.
   * Windows doesn't have this problem since there is no fork().
   * The real allocator, however, might be doing the same thing (jemalloc
   * does). But pthread_atfork `prepare` handlers (first argument) are
@@ -230,6 +233,6 @@ void replace_init(malloc_table_t* aTable, ReplaceMallocBridge** aBridge) {
   * So trick the real allocator into initializing itself without more side
   * effects by calling malloc with a size it can't possibly allocate. */
  sFuncs.malloc(-1);
  pthread_atfork(prefork, postfork, postfork);
  pthread_atfork(prefork, postfork_parent, postfork_child);
#endif
}
+3 −2
Changes for memory/replace/phc/PHC.cpp: 3 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -878,7 +878,8 @@ class GMut {
  }

  static void prefork() { sMutex.Lock(); }
  static void postfork() { sMutex.Unlock(); }
  static void postfork_parent() { sMutex.Unlock(); }
  static void postfork_child() { sMutex.Init(); }

  void IncPageAllocHits(GMutLock) { mPageAllocHits++; }
  void IncPageAllocMisses(GMutLock) { mPageAllocMisses++; }
@@ -1587,7 +1588,7 @@ void replace_init(malloc_table_t* aMallocTable, ReplaceMallocBridge** aBridge) {
  // Note: This must run after attempting an allocation so as to give the
  // system malloc a chance to insert its own atfork handler.
  sMallocTable.malloc(-1);
  pthread_atfork(GMut::prefork, GMut::postfork, GMut::postfork);
  pthread_atfork(GMut::prefork, GMut::postfork_parent, GMut::postfork_child);
#endif

  // gConst and gMut are never freed. They live for the life of the process.