Subject: lib/37170: deadlock between atexit and jemalloc
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <yamt@mwd.biglobe.ne.jp>
List: netbsd-bugs
Date: 10/22/2007 02:45:00
>Number:         37170
>Category:       lib
>Synopsis:       deadlock between atexit and jemalloc
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Oct 22 02:45:00 +0000 2007
>Originator:     YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
>Release:        NetBSD 4.99.34
>Organization:
	
>Environment:
	NetBSd 4.99.34 amd64
>Description:
	jemalloc with MALLOC_OPTIONS=P uses atexit on initialization.
	atexit uses malloc.
>How-To-Repeat:
	run C++ programs with MALLOC_OPTIONS=P.
>Fix:
	while the following patch avoids the deadlock,
	i don't understand the comment in atexit_handler_alloc.

	    /*
	     * Either no static slot was free, or this is a cxa_atexit
	     * handler.  Allocate a new one.  We keep the atexit_mutex
	     * held to prevent handlers from being run while we (potentially)
	     * block in malloc().
	     */

Index: atexit.c
===================================================================
RCS file: /cvsroot/src/lib/libc/stdlib/atexit.c,v
retrieving revision 1.19
diff -u -p -r1.19 atexit.c
--- atexit.c	8 Aug 2007 01:05:34 -0000	1.19
+++ atexit.c	21 Oct 2007 22:33:37 -0000
@@ -96,13 +96,16 @@ static struct atexit_handler *
 atexit_handler_alloc(void *dso)
 {
 	struct atexit_handler *ah;
+	struct atexit_handler *ah_malloced = NULL;
 	int i;
 
+retry:
 	if (dso == NULL) {
 		for (i = 0; i < NSTATIC_HANDLERS; i++) {
 			ah = &atexit_handler0[i];
 			if (ah->ah_atexit == NULL && ah->ah_next == NULL) {
 				/* Slot is free. */
+				free(ah_malloced);
 				return (ah);
 			}
 		}
@@ -114,8 +117,13 @@ atexit_handler_alloc(void *dso)
 	 * held to prevent handlers from being run while we (potentially)
 	 * block in malloc().
 	 */
-	ah = malloc(sizeof(*ah));
-	return (ah);
+	if (ah_malloced == NULL) {
+		mutex_unlock(&atexit_mutex);
+		ah_malloced = malloc(sizeof(*ah));
+		mutex_lock(&atexit_mutex);
+		goto retry;
+	}
+	return (ah_malloced);
 }
 
 /*
Index: jemalloc.c
===================================================================
RCS file: /cvsroot/src/lib/libc/stdlib/jemalloc.c,v
retrieving revision 1.9
diff -u -p -r1.9 jemalloc.c
--- jemalloc.c	19 Oct 2007 19:28:57 -0000	1.9
+++ jemalloc.c	21 Oct 2007 22:33:37 -0000
@@ -3494,12 +3494,6 @@ malloc_init_hard(void)
 		}
 	}
 
-	/* Take care to call atexit() only once. */
-	if (opt_print_stats) {
-		/* Print statistics at exit. */
-		atexit(malloc_print_stats);
-	}
-
 	/* Set variables according to the value of opt_small_max_2pow. */
 	if (opt_small_max_2pow < opt_quantum_2pow)
 		opt_small_max_2pow = opt_quantum_2pow;
@@ -3642,6 +3636,13 @@ malloc_init_hard(void)
 	malloc_mutex_init(&arenas_mtx);
 
 	malloc_initialized = true;
+
+	/* Take care to call atexit() only once. */
+	if (opt_print_stats) {
+		/* Print statistics at exit. */
+		atexit(malloc_print_stats);
+	}
+
 	malloc_mutex_unlock(&init_lock);
 	return (false);
 }

>Unformatted: