mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-06-02 02:04:20 +03:00
176 lines
6.2 KiB
C
176 lines
6.2 KiB
C
#include "test/jemalloc_test.h"
|
|
|
|
#include "jemalloc/internal/eset.h"
|
|
|
|
#define ESET_TEST_ARENA_IND 111
|
|
#define ESET_TEST_ADDR_BASE ((uintptr_t)0x30000000U)
|
|
|
|
static void
|
|
test_edata_init(edata_t *edata, uintptr_t addr, size_t size, uint64_t sn,
|
|
extent_state_t state, bool pinned) {
|
|
memset(edata, 0, sizeof(*edata));
|
|
edata_init(edata, ESET_TEST_ARENA_IND, (void *)addr, size,
|
|
/* slab */ false, SC_NSIZES, sn, state, /* zeroed */ false,
|
|
/* committed */ true, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
|
|
edata_pinned_set(edata, pinned);
|
|
}
|
|
|
|
static void
|
|
test_eset_init(eset_t *eset, extent_state_t state) {
|
|
memset(eset, 0, sizeof(*eset));
|
|
eset_init(eset, state);
|
|
}
|
|
|
|
TEST_BEGIN(test_eset_insert_remove_fit) {
|
|
eset_t eset;
|
|
test_eset_init(&eset, extent_state_dirty);
|
|
|
|
edata_t a;
|
|
edata_t b;
|
|
edata_t c;
|
|
edata_t pinned;
|
|
test_edata_init(&a, ESET_TEST_ADDR_BASE, 2 * PAGE, 20,
|
|
extent_state_dirty, false);
|
|
test_edata_init(&b, ESET_TEST_ADDR_BASE + HUGEPAGE, 2 * PAGE, 10,
|
|
extent_state_dirty, false);
|
|
test_edata_init(&c, ESET_TEST_ADDR_BASE + 2 * HUGEPAGE, 4 * PAGE, 5,
|
|
extent_state_dirty, false);
|
|
test_edata_init(&pinned, ESET_TEST_ADDR_BASE + 3 * HUGEPAGE, PAGE, 1,
|
|
extent_state_dirty, true);
|
|
|
|
eset_insert(&eset, &a);
|
|
eset_insert(&eset, &b);
|
|
eset_insert(&eset, &c);
|
|
eset_insert(&eset, &pinned);
|
|
|
|
expect_zu_eq(9, eset_npages_get(&eset),
|
|
"Unexpected page count after inserts");
|
|
if (config_stats) {
|
|
pszind_t pind_2p = sz_psz2ind(
|
|
sz_psz_quantize_floor(2 * PAGE));
|
|
expect_zu_eq(2, eset_nextents_get(&eset, pind_2p),
|
|
"Unexpected extent count in 2-page bin");
|
|
expect_zu_eq(4 * PAGE, eset_nbytes_get(&eset, pind_2p),
|
|
"Unexpected byte count in 2-page bin");
|
|
}
|
|
|
|
expect_ptr_eq(&a, edata_list_inactive_first(&eset.lru),
|
|
"Non-pinned extents should keep insertion LRU order");
|
|
expect_ptr_eq(&b, edata_list_inactive_next(&eset.lru, &a),
|
|
"Non-pinned extents should keep insertion LRU order");
|
|
expect_ptr_eq(&c, edata_list_inactive_next(&eset.lru, &b),
|
|
"Pinned extents should be excluded from the LRU");
|
|
|
|
edata_t *fit = eset_fit(&eset, 2 * PAGE, PAGE,
|
|
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ false);
|
|
expect_ptr_eq(&c, fit,
|
|
"Default first-fit should choose the oldest fitting extent across "
|
|
"larger bins");
|
|
fit = eset_fit(&eset, 2 * PAGE, PAGE,
|
|
/* exact_only */ true, SC_PTR_BITS, /* prefer_small */ false);
|
|
expect_ptr_eq(&b, fit,
|
|
"Exact fit should choose the lowest serial number in the size bin");
|
|
|
|
eset_remove(&eset, &b);
|
|
expect_zu_eq(7, eset_npages_get(&eset),
|
|
"Unexpected page count after remove");
|
|
fit = eset_fit(&eset, 2 * PAGE, PAGE, /* exact_only */ true,
|
|
SC_PTR_BITS, /* prefer_small */ false);
|
|
expect_ptr_eq(&a, fit,
|
|
"Removing the heap min should refresh the bin summary");
|
|
|
|
eset_remove(&eset, &pinned);
|
|
expect_zu_eq(6, eset_npages_get(&eset),
|
|
"Pinned removal should still update page counts");
|
|
expect_ptr_eq(&a, edata_list_inactive_first(&eset.lru),
|
|
"Pinned removal should not disturb LRU contents");
|
|
|
|
eset_t prefer_eset;
|
|
test_eset_init(&prefer_eset, extent_state_dirty);
|
|
edata_t small_new;
|
|
edata_t large_old;
|
|
test_edata_init(&small_new, ESET_TEST_ADDR_BASE + 4 * HUGEPAGE,
|
|
4 * PAGE, 100, extent_state_dirty, false);
|
|
test_edata_init(&large_old, ESET_TEST_ADDR_BASE + 5 * HUGEPAGE,
|
|
8 * PAGE, 1, extent_state_dirty, false);
|
|
eset_insert(&prefer_eset, &small_new);
|
|
eset_insert(&prefer_eset, &large_old);
|
|
|
|
fit = eset_fit(&prefer_eset, 3 * PAGE, PAGE,
|
|
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ false);
|
|
expect_ptr_eq(&large_old, fit,
|
|
"Default first-fit should prefer the oldest suitable extent");
|
|
fit = eset_fit(&prefer_eset, 3 * PAGE, PAGE,
|
|
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ true);
|
|
expect_ptr_eq(&small_new, fit,
|
|
"prefer_small should stop at the smallest fitting bin");
|
|
}
|
|
TEST_END
|
|
|
|
TEST_BEGIN(test_eset_alignment_and_large_class_fallback) {
|
|
eset_t eset;
|
|
test_eset_init(&eset, extent_state_dirty);
|
|
|
|
edata_t aligned_candidate;
|
|
test_edata_init(&aligned_candidate,
|
|
ESET_TEST_ADDR_BASE + 2 * PAGE, 4 * PAGE, 1, extent_state_dirty,
|
|
false);
|
|
eset_insert(&eset, &aligned_candidate);
|
|
|
|
edata_t *fit = eset_fit(&eset, 2 * PAGE, 4 * PAGE,
|
|
/* exact_only */ false, SC_PTR_BITS, /* prefer_small */ false);
|
|
expect_ptr_eq(&aligned_candidate, fit,
|
|
"Alignment fallback should find a smaller extent that crosses the "
|
|
"requested alignment");
|
|
|
|
eset_t max_fit_eset;
|
|
test_eset_init(&max_fit_eset, extent_state_dirty);
|
|
edata_t too_large_old;
|
|
edata_t bounded_new;
|
|
test_edata_init(&too_large_old, ESET_TEST_ADDR_BASE + 6 * HUGEPAGE,
|
|
64 * PAGE, 1, extent_state_dirty, false);
|
|
test_edata_init(&bounded_new, ESET_TEST_ADDR_BASE + 7 * HUGEPAGE,
|
|
4 * PAGE, 100, extent_state_dirty, false);
|
|
eset_insert(&max_fit_eset, &too_large_old);
|
|
eset_insert(&max_fit_eset, &bounded_new);
|
|
fit = eset_fit(&max_fit_eset, 2 * PAGE, PAGE,
|
|
/* exact_only */ false, /* lg_max_fit */ 1,
|
|
/* prefer_small */ false);
|
|
expect_ptr_eq(&bounded_new, fit,
|
|
"lg_max_fit should reject excessively large older extents");
|
|
}
|
|
TEST_END
|
|
|
|
TEST_BEGIN(test_eset_exact_fit_large_class_disabled) {
|
|
test_skip_if(!sz_large_size_classes_disabled());
|
|
|
|
eset_t exact_eset;
|
|
test_eset_init(&exact_eset, extent_state_dirty);
|
|
size_t request = SC_LARGE_MINCLASS + PAGE;
|
|
edata_t exact;
|
|
edata_t larger;
|
|
test_edata_init(&exact, ESET_TEST_ADDR_BASE + 8 * HUGEPAGE,
|
|
request, 2, extent_state_dirty, false);
|
|
test_edata_init(&larger, ESET_TEST_ADDR_BASE + 9 * HUGEPAGE,
|
|
request + PAGE, 1, extent_state_dirty, false);
|
|
eset_insert(&exact_eset, &larger);
|
|
eset_insert(&exact_eset, &exact);
|
|
|
|
edata_t *fit = eset_fit(&exact_eset, request, PAGE,
|
|
/* exact_only */ true, SC_PTR_BITS, /* prefer_small */ false);
|
|
expect_ptr_eq(&exact, fit,
|
|
"Exact search should enumerate the floor bin when large size "
|
|
"classes are disabled");
|
|
fit = eset_fit(&exact_eset, request - PAGE, PAGE,
|
|
/* exact_only */ true, SC_PTR_BITS, /* prefer_small */ false);
|
|
expect_ptr_null(fit,
|
|
"Exact search should not return merely larger extents");
|
|
}
|
|
TEST_END
|
|
|
|
int
|
|
main(void) {
|
|
return test_no_reentrancy(test_eset_insert_remove_fit,
|
|
test_eset_alignment_and_large_class_fallback,
|
|
test_eset_exact_fit_large_class_disabled);
|
|
}
|