Bug 1361258 - Initial implementation for jemalloc_thread_local_arena. r=erahm
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 11 May 2017 10:19:51 +0900
changeset 358065 751e131baa5d2074b6fd2649d10707ab3352cbf0
parent 358064 1290ff3502724245fdab50e85d3f93f86536818c
child 358066 bacbf98fc81812fa190b80d03e92d1d85b0422be
push id31808
push usercbook@mozilla.com
push dateFri, 12 May 2017 12:37:49 +0000
treeherdermozilla-central@030c0a7c8781 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserahm
bugs1361258
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1361258 - Initial implementation for jemalloc_thread_local_arena. r=erahm The function, when passed `true`, creates a new arena with no attachment in the global list of arenas, and assigns it to the current thread. When passed `false`, it restores the default arena. Some details are left out because they don't matter yet, as the sole initial use of the API is going to invoke the function when stylo rayon threads start up, which happens exactly once per thread, and at thread exit time, which happens at shutdown, if ever. This simplifies things, and leaves those details to followup(s): - Arenas can't simply be killed when the function is called with `false` again (or when the thread dies) because they may still contain valid allocations that could have been passed to other threads. Those arenas should be kept until they are empty. - jemalloc_stats doesn't know about them and will under-report memory usage. - pre/post fork hooks don't know about them and will not force-unlock their locks. In practice, until those arenas are used for something else than the style system, this can't lead to the dead-locks that these hooks help prevent because nothing should be touching pointers allocated through them after fork.
memory/mozjemalloc/jemalloc.c
--- a/memory/mozjemalloc/jemalloc.c
+++ b/memory/mozjemalloc/jemalloc.c
@@ -1436,16 +1436,17 @@ static void	arena_dalloc_large(arena_t *
 static void	arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk,
     void *ptr, size_t size, size_t oldsize);
 static bool	arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk,
     void *ptr, size_t size, size_t oldsize);
 static bool	arena_ralloc_large(void *ptr, size_t size, size_t oldsize);
 static void	*arena_ralloc(void *ptr, size_t size, size_t oldsize);
 static bool	arena_new(arena_t *arena);
 static arena_t	*arenas_extend(unsigned ind);
+#define NO_INDEX ((unsigned) -1)
 static void	*huge_malloc(size_t size, bool zero);
 static void	*huge_palloc(size_t size, size_t alignment, bool zero);
 static void	*huge_ralloc(void *ptr, size_t size, size_t oldsize);
 static void	huge_dalloc(void *ptr);
 static void	malloc_print_stats(void);
 #ifndef MOZ_MEMORY_WINDOWS
 static
 #endif
@@ -3046,16 +3047,35 @@ chunk_dealloc(void *chunk, size_t size)
 /******************************************************************************/
 /*
  * Begin arena.
  */
 
 MOZ_JEMALLOC_API void
 jemalloc_thread_local_arena_impl(bool enabled)
 {
+#ifndef NO_TLS
+	arena_t *arena;
+
+	if (enabled) {
+		/* The arena will essentially be leaked if this function is
+		 * called with `false`, but it doesn't matter at the moment.
+		 * because in practice nothing actually calls this function
+		 * with `false`, except maybe at shutdown. */
+		arena = arenas_extend(NO_INDEX);
+	} else {
+		arena = arenas[0];
+	}
+#ifdef MOZ_MEMORY_WINDOWS
+	TlsSetValue(tlsIndex, arena);
+#else
+	arenas_map = arena;
+#endif
+
+#endif
 }
 
 /*
  * Choose an arena based on a per-thread value (fast-path code, calls slow-path
  * code if necessary).
  */
 static inline arena_t *
 choose_arena(void)
@@ -5004,17 +5024,19 @@ static arena_t *
 arenas_extend(unsigned ind)
 {
 	arena_t *ret;
 
 	/* Allocate enough space for trailing bins. */
 	ret = (arena_t *)base_alloc(sizeof(arena_t)
 	    + (sizeof(arena_bin_t) * (ntbins + nqbins + nsbins - 1)));
 	if (ret != NULL && arena_new(ret) == false) {
-		arenas[ind] = ret;
+		if (ind != NO_INDEX) {
+			arenas[ind] = ret;
+		}
 		return (ret);
 	}
 	/* Only reached if there is an OOM error. */
 
 	/*
 	 * OOM here is quite inconvenient to propagate, since dealing with it
 	 * would require a check for failure in the fast path.  Instead, punt
 	 * by using arenas[0].  In practice, this is an extremely unlikely