Don't assert in the child after a threaded program does fork().
Fixes #255355 (helgrind part).


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11525 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c
index 1be4f13..6af1a73 100644
--- a/helgrind/hg_main.c
+++ b/helgrind/hg_main.c
@@ -52,6 +52,7 @@
 #include "pub_tool_debuginfo.h" // VG_(find_seginfo), VG_(seginfo_soname)
 #include "pub_tool_redir.h"     // sonames for the dynamic linkers
 #include "pub_tool_vki.h"       // VKI_PAGE_SIZE
+#include "pub_tool_libcproc.h"  // VG_(atfork)
 
 #include "hg_basics.h"
 #include "hg_wordset.h"
@@ -1679,6 +1680,8 @@
       - tell libhb the thread is gone
       - clear the map_threads entry, in order that the Valgrind core
         can re-use it. */
+   /* Cleanup actions (next 5 lines) copied in evh__atfork_child; keep
+      in sync. */
    tl_assert(thr_q->hbthr);
    libhb_async_exit(thr_q->hbthr);
    tl_assert(thr_q->coretid == quit_tid);
@@ -1689,6 +1692,35 @@
       all__sanity_check("evh__pre_thread_ll_exit-post");
 }
 
+/* This is called immediately after fork, for the child only.  'tid'
+   is the only surviving thread (as per POSIX rules on fork() in
+   threaded programs), so we have to clean up map_threads to remove
+   entries for any other threads. */
+static
+void evh__atfork_child ( ThreadId tid )
+{
+   UInt    i;
+   Thread* thr;
+   /* Slot 0 should never be used. */
+   thr = map_threads_maybe_lookup( 0/*INVALID*/ );
+   tl_assert(!thr);
+   /* Clean up all other slots except 'tid'. */
+   for (i = 1; i < VG_N_THREADS; i++) {
+      if (i == tid)
+         continue;
+      thr = map_threads_maybe_lookup(i);
+      if (!thr)
+         continue;
+      /* Cleanup actions (next 5 lines) copied from end of
+         evh__pre_thread_ll_exit; keep in sync. */
+      tl_assert(thr->hbthr);
+      libhb_async_exit(thr->hbthr);
+      tl_assert(thr->coretid == i);
+      thr->coretid = VG_INVALID_THREADID;
+      map_threads_delete(i);
+   }
+}
+
 
 static
 void evh__HG_PTHREAD_JOIN_POST ( ThreadId stay_tid, Thread* quit_thr )
@@ -4916,6 +4948,8 @@
    hg_mallocmeta_table
       = VG_(HT_construct)( "hg_malloc_metadata_table" );
 
+   // add a callback to clean up on (threaded) fork.
+   VG_(atfork)(NULL/*pre*/, NULL/*parent*/, evh__atfork_child/*child*/);
 }
 
 VG_DETERMINE_INTERFACE_VERSION(hg_pre_clo_init)