about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--malloc/Makefile2
-rw-r--r--malloc/arena.c21
-rw-r--r--malloc/tst-mallocfork.c52
4 files changed, 81 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 86ae04ebdf..e4ef75de72 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2005-09-26  Ulrich Drepper  <drepper@redhat.com>
 
+	[BZ #838]
+	* malloc/arena.c (ptmalloc_lock_all): If global lock already taken
+	by the same thread, just bump the counter.
+	(ptmalloc_unlock_all): If counter for recursive locks hasn't reached
+	zero, don't do anything else.
+	* malloc/Makefile (tests): Add tst-mallocfork.
+	* malloc/tst-mallocfork.c: New file.
+
 	[BZ #808]
 	* malloc/malloc.c (_int_realloc): Make error message clearer.
 
diff --git a/malloc/Makefile b/malloc/Makefile
index ca427077f3..c479da39b7 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -27,7 +27,7 @@ all:
 dist-headers := malloc.h
 headers := $(dist-headers) obstack.h mcheck.h
 tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
-	 tst-mallocstate tst-mcheck
+	 tst-mallocstate tst-mcheck tst-mallocfork
 test-srcs = tst-mtrace
 
 distribute = thread-m.h mtrace.pl mcheck-init.c stackinfo.h memusage.h \
diff --git a/malloc/arena.c b/malloc/arena.c
index 8db255966d..a844392a6b 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -210,6 +210,10 @@ free_atfork(Void_t* mem, const Void_t *caller)
     (void)mutex_unlock(&ar_ptr->mutex);
 }
 
+
+/* Counter for number of times the list is locked by the same thread.  */
+static unsigned int atfork_recursive_cntr;
+
 /* The following two functions are registered via thread_atfork() to
    make sure that the mutexes remain in a consistent state in the
    fork()ed version of a thread.  Also adapt the malloc and free hooks
@@ -223,7 +227,18 @@ ptmalloc_lock_all (void)
 
   if(__malloc_initialized < 1)
     return;
-  (void)mutex_lock(&list_lock);
+  if (mutex_trylock(&list_lock))
+    {
+      Void_t *my_arena;
+      tsd_getspecific(arena_key, my_arena);
+      if (my_arena == ATFORK_ARENA_PTR)
+	/* This is the same thread which already locks the global list.
+	   Just bump the counter.  */
+	goto out;
+
+      /* This thread has to wait its turn.  */
+      (void)mutex_lock(&list_lock);
+    }
   for(ar_ptr = &main_arena;;) {
     (void)mutex_lock(&ar_ptr->mutex);
     ar_ptr = ar_ptr->next;
@@ -236,6 +251,8 @@ ptmalloc_lock_all (void)
   /* Only the current thread may perform malloc/free calls now. */
   tsd_getspecific(arena_key, save_arena);
   tsd_setspecific(arena_key, ATFORK_ARENA_PTR);
+ out:
+  ++atfork_recursive_cntr;
 }
 
 static void
@@ -245,6 +262,8 @@ ptmalloc_unlock_all (void)
 
   if(__malloc_initialized < 1)
     return;
+  if (--atfork_recursive_cntr != 0)
+    return;
   tsd_setspecific(arena_key, save_arena);
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
diff --git a/malloc/tst-mallocfork.c b/malloc/tst-mallocfork.c
new file mode 100644
index 0000000000..abbc9d83b6
--- /dev/null
+++ b/malloc/tst-mallocfork.c
@@ -0,0 +1,52 @@
+/* Derived from the test case in
+   http://sourceware.org/bugzilla/show_bug.cgi?id=838.  */
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static void
+sig_handler (int signum)
+{
+  pid_t child = fork ();
+  if (child == 0)
+    exit (0);
+  TEMP_FAILURE_RETRY (waitpid (child, NULL, 0));
+}
+
+static int
+do_test (void)
+{
+  pid_t parent = getpid ();
+
+  struct sigaction action;
+  sigemptyset (&action.sa_mask);
+  action.sa_handler = sig_handler;
+
+  malloc (sizeof (int));
+
+  if (sigaction (SIGALRM, &action, NULL) != 0)
+    {
+      puts ("sigaction failed");
+      return 1;
+    }
+
+  /* Create a child that sends the signal to be caught.  */
+  pid_t child = fork ();
+  if (child == 0)
+    {
+      if (kill (parent, SIGALRM) == -1)
+	perror ("kill");
+      exit (0);
+    }
+
+  TEMP_FAILURE_RETRY (waitpid (child, NULL, 0));
+
+  return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"