File glibc-malloc-arena-max.diff of Package glibc.import5554

Allow M_ARENA_MAX / MALLOC_ARENA_MAX limit even with PER_THREAD disabled

With the new PER_THREAD compile-time option, the allocator also offers
a way to limit the total number of arenas using MALLOC_ARENA_MAX
environment variable or mallopt(M_ARENA_MAX).

In principle, this feature is not tied to the PER_THREAD option. This
patch makes it possible to use it even with the default compilation
settings.

One motivation to limit the number of arenas may be libhugetlbfs users
that rely on its __morecore hook providing hugetlbfs-backed memory for
the allocator - this can work only with a single arena and multi-threaded
programs wishing to use this feature need a way to limit the allocator
to a single arena. Another motivation is avoiding pathological behavior
in extremely thread-intensive applications.


2011-02-04  Petr Baudis  <pasky@suse.cz>

	* malloc/arena.c: Define and manage narenas even ifndef
	PER_THREAD.
	* malloc/arena.c (ptmalloc_init_minimal): Likewise.
	* malloc/arena.c (_int_new_arena): Likewise.
	* malloc/arena.c (ptmalloc_init): Implement MALLOC_ARENA_MAX
	even ifndef PER_THREAD.
	* malloc/arena.c (reused_arena): Split off get_narenas_limit(),
	define even ifndef PER_THREAD.
	* malloc/arena.c (arena_get2): Adjust for get_narenas_limit()
	split, call reused_arena even ifndef PER_THREAD.
	* malloc/hooks.c (public_gET_STATe): Set arena_max, narenas
	even ifndef PER_THREAD.
	* malloc/hooks.c (public_sET_STATe): Likewise.
	* malloc/malloc.c (malloc_par): Define arena_max even ifndef
	PER_THREAD.
	* malloc/malloc.c (mALLOPt): Implement M_ARENA_MAX even ifndef
	PER_THREAD.
	* malloc/malloc.c: Remove redundant M_* defines.

diff --git a/malloc/arena.c b/malloc/arena.c
index 4d0deef..ea80724 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -78,8 +78,8 @@ extern int sanity_check_heap_info_alignment[(sizeof (heap_info)
 
 static tsd_key_t arena_key;
 static mutex_t list_lock;
-#ifdef PER_THREAD
 static size_t narenas;
+#ifdef PER_THREAD
 static mstate free_list;
 #endif
 
@@ -416,8 +416,8 @@ ptmalloc_init_minimal (void)
 #ifdef PER_THREAD
 # define NARENAS_FROM_NCORES(n) ((n) * (sizeof(long) == 4 ? 2 : 8))
   mp_.arena_test     = NARENAS_FROM_NCORES (1);
-  narenas = 1;
 #endif
+  narenas = 1;
 }
 
 
@@ -574,10 +574,8 @@ ptmalloc_init (void)
 		{
 		  if (memcmp (envline, "MMAP_MAX_", 9) == 0)
 		    mALLOPt(M_MMAP_MAX, atoi(&envline[10]));
-#ifdef PER_THREAD
 		  else if (memcmp (envline, "ARENA_MAX", 9) == 0)
 		    mALLOPt(M_ARENA_MAX, atoi(&envline[10]));
-#endif
 		}
 	      break;
 #ifdef PER_THREAD
@@ -946,9 +944,9 @@ _int_new_arena(size_t size)
   atomic_write_barrier ();
   main_arena.next = a;
 
-#ifdef PER_THREAD
   ++narenas;
 
+#ifdef PER_THREAD
   (void)mutex_unlock(&list_lock);
 #endif
 
@@ -982,13 +980,9 @@ get_free_list (void)
   return result;
 }
 
-
-static mstate
-reused_arena (void)
+static int
+get_narenas_limit (void) __attribute__((pure))
 {
-  if (narenas <= mp_.arena_test)
-    return NULL;
-
   static int narenas_limit;
   if (narenas_limit == 0)
     {
@@ -1006,10 +1000,16 @@ reused_arena (void)
 	    narenas_limit = NARENAS_FROM_NCORES (2);
 	}
     }
+  return narenas_limit;
+}
+#endif
 
-  if (narenas < narenas_limit)
-    return NULL;
 
+/* Reuse and return one of the existing arenas; if all arenas are busy,
+ * pick one in a round-robin fashion and block until it becomes available. */
+static mstate
+reused_arena (void)
+{
   mstate result;
   static mstate next_to_use;
   if (next_to_use == NULL)
@@ -1035,7 +1035,6 @@ reused_arena (void)
 
   return result;
 }
-#endif
 
 static mstate
 internal_function
@@ -1048,10 +1047,15 @@ arena_get2(a_tsd, size) mstate a_tsd; size_t size;
   mstate a;
 
 #ifdef PER_THREAD
-  if ((a = get_free_list ()) == NULL
-      && (a = reused_arena ()) == NULL)
-    /* Nothing immediately available, so generate a new arena.  */
-    a = _int_new_arena(size);
+  if ((a = get_free_list ()) == NULL)
+    {
+      if (narenas > mp_.arena_test && narenas >= get_narenas_limit())
+	a = reused_arena ();
+      else
+	/* Nothing immediately available, but we can still generate more
+	 * arenas, so get a new one.  */
+	a = _int_new_arena(size);
+    }
 #else
   if(!a_tsd)
     a = a_tsd = &main_arena;
@@ -1093,8 +1097,14 @@ arena_get2(a_tsd, size) mstate a_tsd; size_t size;
     goto repeat;
   }
 
-  /* Nothing immediately available, so generate a new arena.  */
-  a = _int_new_arena(size);
+  if (__builtin_expect(mp_.arena_max > 0, 0) && narenas >= mp_.arena_max)
+    /* Try again, this time blocking in case we are still unable to find
+     * a free arena. */
+    a = reused_arena();
+  else
+    /* Nothing immediately available, so generate a new arena.  */
+    a = _int_new_arena(size);
+
   (void)mutex_unlock(&list_lock);
 #endif
 
diff --git a/malloc/hooks.c b/malloc/hooks.c
index 28845ee..e938492 100644
--- a/malloc/hooks.c
+++ b/malloc/hooks.c
@@ -579,9 +579,9 @@ public_gET_STATe(void)
   ms->max_fast = get_max_fast();
 #ifdef PER_THREAD
   ms->arena_test = mp_.arena_test;
+#endif
   ms->arena_max = mp_.arena_max;
   ms->narenas = narenas;
-#endif
   (void)mutex_unlock(&main_arena.mutex);
   return (Void_t*)ms;
 }
@@ -683,9 +683,9 @@ public_sET_STATe(Void_t* msptr)
   if (ms->version >= 4) {
 #ifdef PER_THREAD
     mp_.arena_test = ms->arena_test;
+#endif
     mp_.arena_max = ms->arena_max;
     narenas = ms->narenas;
-#endif
   }
   check_malloc_state(&main_arena);
 
diff --git a/malloc/malloc.c b/malloc/malloc.c
index b1d43c6..8dbadfa 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -2406,9 +2406,10 @@ struct malloc_par {
   INTERNAL_SIZE_T  top_pad;
   INTERNAL_SIZE_T  mmap_threshold;
 #ifdef PER_THREAD
+  /* Lower bound for arena_max. */
   INTERNAL_SIZE_T  arena_test;
-  INTERNAL_SIZE_T  arena_max;
 #endif
+  INTERNAL_SIZE_T  arena_max;
 
   /* Memory map support */
   int              n_mmaps;
@@ -2446,13 +2447,6 @@ static struct malloc_state main_arena;
 static struct malloc_par mp_;
 
 
-#ifdef PER_THREAD
-/*  Non public mallopt parameters.  */
-#define M_ARENA_TEST -7
-#define M_ARENA_MAX  -8
-#endif
-
-
 /* Maximum size of memory handled in fastbins.  */
 static INTERNAL_SIZE_T global_max_fast;
 
@@ -6095,12 +6089,12 @@ int mALLOPt(param_number, value) int param_number; int value;
     if (value > 0)
       mp_.arena_test = value;
     break;
+#endif
 
   case M_ARENA_MAX:
     if (value > 0)
       mp_.arena_max = value;
     break;
-#endif
   }
   (void)mutex_unlock(&av->mutex);
   return res;
openSUSE Build Service is sponsored by