glibc/glibc-malloc-arena-max.diff

237 lines
6.5 KiB
Diff
Raw Normal View History

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.
Index: glibc-2.13/malloc/arena.c
===================================================================
--- glibc-2.13.orig/malloc/arena.c
+++ glibc-2.13/malloc/arena.c
@@ -78,8 +78,8 @@ extern int sanity_check_heap_info_alignm
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,10 @@ get_free_list (void)
return result;
}
-
-static mstate
-reused_arena (void)
+static int get_narenas_limit (void) __attribute__((pure));
+static int
+get_narenas_limit (void)
{
- if (narenas <= mp_.arena_test)
- return NULL;
-
static int narenas_limit;
if (narenas_limit == 0)
{
@@ -1006,10 +1001,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 +1036,6 @@ reused_arena (void)
return result;
}
-#endif
static mstate
internal_function
@@ -1048,10 +1048,15 @@ arena_get2(a_tsd, size) mstate a_tsd; si
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 +1098,14 @@ arena_get2(a_tsd, size) mstate a_tsd; si
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
Index: glibc-2.13/malloc/hooks.c
===================================================================
--- glibc-2.13.orig/malloc/hooks.c
+++ glibc-2.13/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);
Index: glibc-2.13/malloc/malloc.c
===================================================================
--- glibc-2.13.orig/malloc/malloc.c
+++ glibc-2.13/malloc/malloc.c
@@ -2405,9 +2405,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;
@@ -2445,13 +2446,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;
@@ -6111,12 +6105,12 @@ int mALLOPt(param_number, value) int par
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;