mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-22 04:03:11 +00:00
* Allow resuming per-CPU arena selection via thread.arena With percpu_arena enabled, thread.arena control is one-directional. A thread can be bound to a manually created arena (an index at or above the per-CPU auto range) to route a bounded region of work to a dedicated, long-lived arena, but there is no way back: thread_arena_ctl returns EPERM for any index within the auto range, and arena_choose_impl only re-selects a per-CPU arena for threads whose current arena is already in that range. So once a thread is bound to a manual arena it stays pinned there forever, and its later allocations land there instead of following the CPU. Treat setting thread.arena to an index within the per-CPU range as a request to resume automatic per-CPU selection: hand the thread back to percpu management (rebinding it to the current CPU's arena via percpu_arena_update) and return 0 instead of EPERM. Binding to a manual arena is unchanged. The requested index is advisory; under percpu the thread is governed by its current CPU, so it resumes on the current CPU's arena regardless of the value passed. Add test/unit/percpu_arena_resume covering the manual-arena to resume round trip with allocs & dallocs happening meanwhile, and update test_thread_arena, which asserted the old EPERM. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
82 lines
2.9 KiB
C
82 lines
2.9 KiB
C
#include "test/jemalloc_test.h"
|
|
|
|
/*
|
|
* Under percpu_arena, binding a thread to a manual arena (an index at or above
|
|
* the per-CPU auto range) is one-way: percpu never reclaims it (see
|
|
* arena_choose_impl). Setting thread.arena back to an index in the auto range
|
|
* resumes per-CPU selection instead of failing with EPERM.
|
|
*/
|
|
TEST_BEGIN(test_thread_arena_resume_percpu) {
|
|
test_skip_if(!have_percpu_arena
|
|
|| !PERCPU_ARENA_ENABLED(opt_percpu_arena));
|
|
|
|
unsigned limit = percpu_arena_ind_limit(opt_percpu_arena);
|
|
/* Bypass the tcache so every allocation and free hits the arena. */
|
|
const int flags = MALLOCX_TCACHE_NONE;
|
|
|
|
void *warm = mallocx(1, 0);
|
|
expect_ptr_not_null(warm, "Unexpected mallocx() failure");
|
|
dallocx(warm, 0);
|
|
|
|
unsigned cur;
|
|
size_t sz = sizeof(cur);
|
|
expect_d_eq(mallctl("thread.arena", (void *)&cur, &sz, NULL, 0), 0,
|
|
"Unexpected mallctl() failure");
|
|
expect_u_lt(cur, limit, "Thread should start on a per-CPU arena");
|
|
|
|
unsigned manual;
|
|
sz = sizeof(manual);
|
|
expect_d_eq(mallctl("arenas.create", (void *)&manual, &sz, NULL, 0), 0,
|
|
"Unexpected arenas.create() failure");
|
|
expect_u_ge(manual, limit, "A manual arena is outside the per-CPU range");
|
|
|
|
unsigned old;
|
|
sz = sizeof(old);
|
|
expect_d_eq(mallctl("thread.arena", (void *)&old, &sz, (void *)&manual,
|
|
sizeof(manual)), 0, "Binding to a manual arena should be allowed");
|
|
sz = sizeof(cur);
|
|
expect_d_eq(mallctl("thread.arena", (void *)&cur, &sz, NULL, 0), 0,
|
|
"Unexpected mallctl() failure");
|
|
expect_u_eq(cur, manual, "Thread should be bound to the manual arena");
|
|
|
|
void *p_manual = mallocx(1024, flags);
|
|
expect_ptr_not_null(p_manual, "Unexpected mallocx() failure");
|
|
unsigned found;
|
|
sz = sizeof(found);
|
|
expect_d_eq(mallctl("arenas.lookup", (void *)&found, &sz,
|
|
(void *)&p_manual, sizeof(p_manual)), 0,
|
|
"Unexpected arenas.lookup() failure");
|
|
expect_u_eq(found, manual, "Allocation should come from the manual arena");
|
|
|
|
void *scratch = mallocx(1024, flags);
|
|
expect_ptr_not_null(scratch, "Unexpected mallocx() failure");
|
|
dallocx(scratch, flags);
|
|
|
|
unsigned resume = 0;
|
|
expect_d_eq(mallctl("thread.arena", NULL, NULL, (void *)&resume,
|
|
sizeof(resume)), 0, "Should resume per-CPU selection, not fail");
|
|
sz = sizeof(cur);
|
|
expect_d_eq(mallctl("thread.arena", (void *)&cur, &sz, NULL, 0), 0,
|
|
"Unexpected mallctl() failure");
|
|
expect_u_lt(cur, limit, "Thread should be back on a per-CPU arena");
|
|
|
|
void *p_percpu = mallocx(1024, flags);
|
|
expect_ptr_not_null(p_percpu, "Unexpected mallocx() failure");
|
|
sz = sizeof(found);
|
|
expect_d_eq(mallctl("arenas.lookup", (void *)&found, &sz,
|
|
(void *)&p_percpu, sizeof(p_percpu)), 0,
|
|
"Unexpected arenas.lookup() failure");
|
|
expect_u_lt(found, limit, "Allocation should come from a per-CPU arena");
|
|
dallocx(p_percpu, flags);
|
|
|
|
/* Free the manual-arena region while bound to a different arena. */
|
|
dallocx(p_manual, flags);
|
|
}
|
|
TEST_END
|
|
|
|
int
|
|
main(void) {
|
|
return test(
|
|
test_thread_arena_resume_percpu);
|
|
}
|