diff --git a/ChangeLog b/ChangeLog index 86ae04ebdf..e4ef75de72 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2005-09-26 Ulrich Drepper + [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 +#include +#include +#include +#include +#include +#include + +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"