aboutsummaryrefslogtreecommitdiff
path: root/examples/redis-unstable/deps/jemalloc/test/unit/hpa.c
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
commitdcacc00e3750300617ba6e16eb346713f91a783a (patch)
tree38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/redis-unstable/deps/jemalloc/test/unit/hpa.c
parent58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff)
downloadcrep-dcacc00e3750300617ba6e16eb346713f91a783a.tar.gz
Remove testing data
Diffstat (limited to 'examples/redis-unstable/deps/jemalloc/test/unit/hpa.c')
-rw-r--r--examples/redis-unstable/deps/jemalloc/test/unit/hpa.c459
1 files changed, 0 insertions, 459 deletions
diff --git a/examples/redis-unstable/deps/jemalloc/test/unit/hpa.c b/examples/redis-unstable/deps/jemalloc/test/unit/hpa.c
deleted file mode 100644
index dfd57f3..0000000
--- a/examples/redis-unstable/deps/jemalloc/test/unit/hpa.c
+++ /dev/null
@@ -1,459 +0,0 @@
1#include "test/jemalloc_test.h"
2
3#include "jemalloc/internal/hpa.h"
4#include "jemalloc/internal/nstime.h"
5
6#define SHARD_IND 111
7
8#define ALLOC_MAX (HUGEPAGE / 4)
9
10typedef struct test_data_s test_data_t;
11struct test_data_s {
12 /*
13 * Must be the first member -- we convert back and forth between the
14 * test_data_t and the hpa_shard_t;
15 */
16 hpa_shard_t shard;
17 hpa_central_t central;
18 base_t *base;
19 edata_cache_t shard_edata_cache;
20
21 emap_t emap;
22};
23
24static hpa_shard_opts_t test_hpa_shard_opts_default = {
25 /* slab_max_alloc */
26 ALLOC_MAX,
27 /* hugification threshold */
28 HUGEPAGE,
29 /* dirty_mult */
30 FXP_INIT_PERCENT(25),
31 /* deferral_allowed */
32 false,
33 /* hugify_delay_ms */
34 10 * 1000,
35};
36
37static hpa_shard_t *
38create_test_data(hpa_hooks_t *hooks, hpa_shard_opts_t *opts) {
39 bool err;
40 base_t *base = base_new(TSDN_NULL, /* ind */ SHARD_IND,
41 &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
42 assert_ptr_not_null(base, "");
43
44 test_data_t *test_data = malloc(sizeof(test_data_t));
45 assert_ptr_not_null(test_data, "");
46
47 test_data->base = base;
48
49 err = edata_cache_init(&test_data->shard_edata_cache, base);
50 assert_false(err, "");
51
52 err = emap_init(&test_data->emap, test_data->base, /* zeroed */ false);
53 assert_false(err, "");
54
55 err = hpa_central_init(&test_data->central, test_data->base, hooks);
56 assert_false(err, "");
57
58 err = hpa_shard_init(&test_data->shard, &test_data->central,
59 &test_data->emap, test_data->base, &test_data->shard_edata_cache,
60 SHARD_IND, opts);
61 assert_false(err, "");
62
63 return (hpa_shard_t *)test_data;
64}
65
66static void
67destroy_test_data(hpa_shard_t *shard) {
68 test_data_t *test_data = (test_data_t *)shard;
69 base_delete(TSDN_NULL, test_data->base);
70 free(test_data);
71}
72
73TEST_BEGIN(test_alloc_max) {
74 test_skip_if(!hpa_supported());
75
76 hpa_shard_t *shard = create_test_data(&hpa_hooks_default,
77 &test_hpa_shard_opts_default);
78 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
79
80 edata_t *edata;
81
82 /* Small max */
83 bool deferred_work_generated = false;
84 edata = pai_alloc(tsdn, &shard->pai, ALLOC_MAX, PAGE, false, false,
85 false, &deferred_work_generated);
86 expect_ptr_not_null(edata, "Allocation of small max failed");
87 edata = pai_alloc(tsdn, &shard->pai, ALLOC_MAX + PAGE, PAGE, false,
88 false, false, &deferred_work_generated);
89 expect_ptr_null(edata, "Allocation of larger than small max succeeded");
90
91 destroy_test_data(shard);
92}
93TEST_END
94
95typedef struct mem_contents_s mem_contents_t;
96struct mem_contents_s {
97 uintptr_t my_addr;
98 size_t size;
99 edata_t *my_edata;
100 rb_node(mem_contents_t) link;
101};
102
103static int
104mem_contents_cmp(const mem_contents_t *a, const mem_contents_t *b) {
105 return (a->my_addr > b->my_addr) - (a->my_addr < b->my_addr);
106}
107
108typedef rb_tree(mem_contents_t) mem_tree_t;
109rb_gen(static, mem_tree_, mem_tree_t, mem_contents_t, link,
110 mem_contents_cmp);
111
112static void
113node_assert_ordered(mem_contents_t *a, mem_contents_t *b) {
114 assert_zu_lt(a->my_addr, a->my_addr + a->size, "Overflow");
115 assert_zu_le(a->my_addr + a->size, b->my_addr, "");
116}
117
118static void
119node_check(mem_tree_t *tree, mem_contents_t *contents) {
120 edata_t *edata = contents->my_edata;
121 assert_ptr_eq(contents, (void *)contents->my_addr, "");
122 assert_ptr_eq(contents, edata_base_get(edata), "");
123 assert_zu_eq(contents->size, edata_size_get(edata), "");
124 assert_ptr_eq(contents->my_edata, edata, "");
125
126 mem_contents_t *next = mem_tree_next(tree, contents);
127 if (next != NULL) {
128 node_assert_ordered(contents, next);
129 }
130 mem_contents_t *prev = mem_tree_prev(tree, contents);
131 if (prev != NULL) {
132 node_assert_ordered(prev, contents);
133 }
134}
135
136static void
137node_insert(mem_tree_t *tree, edata_t *edata, size_t npages) {
138 mem_contents_t *contents = (mem_contents_t *)edata_base_get(edata);
139 contents->my_addr = (uintptr_t)edata_base_get(edata);
140 contents->size = edata_size_get(edata);
141 contents->my_edata = edata;
142 mem_tree_insert(tree, contents);
143 node_check(tree, contents);
144}
145
146static void
147node_remove(mem_tree_t *tree, edata_t *edata) {
148 mem_contents_t *contents = (mem_contents_t *)edata_base_get(edata);
149 node_check(tree, contents);
150 mem_tree_remove(tree, contents);
151}
152
153TEST_BEGIN(test_stress) {
154 test_skip_if(!hpa_supported());
155
156 hpa_shard_t *shard = create_test_data(&hpa_hooks_default,
157 &test_hpa_shard_opts_default);
158
159 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
160
161 const size_t nlive_edatas_max = 500;
162 size_t nlive_edatas = 0;
163 edata_t **live_edatas = calloc(nlive_edatas_max, sizeof(edata_t *));
164 /*
165 * Nothing special about this constant; we're only fixing it for
166 * consistency across runs.
167 */
168 size_t prng_state = (size_t)0x76999ffb014df07c;
169
170 mem_tree_t tree;
171 mem_tree_new(&tree);
172
173 bool deferred_work_generated = false;
174
175 for (size_t i = 0; i < 100 * 1000; i++) {
176 size_t operation = prng_range_zu(&prng_state, 2);
177 if (operation == 0) {
178 /* Alloc */
179 if (nlive_edatas == nlive_edatas_max) {
180 continue;
181 }
182
183 /*
184 * We make sure to get an even balance of small and
185 * large allocations.
186 */
187 size_t npages_min = 1;
188 size_t npages_max = ALLOC_MAX / PAGE;
189 size_t npages = npages_min + prng_range_zu(&prng_state,
190 npages_max - npages_min);
191 edata_t *edata = pai_alloc(tsdn, &shard->pai,
192 npages * PAGE, PAGE, false, false, false,
193 &deferred_work_generated);
194 assert_ptr_not_null(edata,
195 "Unexpected allocation failure");
196 live_edatas[nlive_edatas] = edata;
197 nlive_edatas++;
198 node_insert(&tree, edata, npages);
199 } else {
200 /* Free. */
201 if (nlive_edatas == 0) {
202 continue;
203 }
204 size_t victim = prng_range_zu(&prng_state, nlive_edatas);
205 edata_t *to_free = live_edatas[victim];
206 live_edatas[victim] = live_edatas[nlive_edatas - 1];
207 nlive_edatas--;
208 node_remove(&tree, to_free);
209 pai_dalloc(tsdn, &shard->pai, to_free,
210 &deferred_work_generated);
211 }
212 }
213
214 size_t ntreenodes = 0;
215 for (mem_contents_t *contents = mem_tree_first(&tree); contents != NULL;
216 contents = mem_tree_next(&tree, contents)) {
217 ntreenodes++;
218 node_check(&tree, contents);
219 }
220 expect_zu_eq(ntreenodes, nlive_edatas, "");
221
222 /*
223 * Test hpa_shard_destroy, which requires as a precondition that all its
224 * extents have been deallocated.
225 */
226 for (size_t i = 0; i < nlive_edatas; i++) {
227 edata_t *to_free = live_edatas[i];
228 node_remove(&tree, to_free);
229 pai_dalloc(tsdn, &shard->pai, to_free,
230 &deferred_work_generated);
231 }
232 hpa_shard_destroy(tsdn, shard);
233
234 free(live_edatas);
235 destroy_test_data(shard);
236}
237TEST_END
238
239static void
240expect_contiguous(edata_t **edatas, size_t nedatas) {
241 for (size_t i = 0; i < nedatas; i++) {
242 size_t expected = (size_t)edata_base_get(edatas[0])
243 + i * PAGE;
244 expect_zu_eq(expected, (size_t)edata_base_get(edatas[i]),
245 "Mismatch at index %zu", i);
246 }
247}
248
249TEST_BEGIN(test_alloc_dalloc_batch) {
250 test_skip_if(!hpa_supported());
251
252 hpa_shard_t *shard = create_test_data(&hpa_hooks_default,
253 &test_hpa_shard_opts_default);
254 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
255
256 bool deferred_work_generated = false;
257
258 enum {NALLOCS = 8};
259
260 edata_t *allocs[NALLOCS];
261 /*
262 * Allocate a mix of ways; first half from regular alloc, second half
263 * from alloc_batch.
264 */
265 for (size_t i = 0; i < NALLOCS / 2; i++) {
266 allocs[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE,
267 /* zero */ false, /* guarded */ false,
268 /* frequent_reuse */ false, &deferred_work_generated);
269 expect_ptr_not_null(allocs[i], "Unexpected alloc failure");
270 }
271 edata_list_active_t allocs_list;
272 edata_list_active_init(&allocs_list);
273 size_t nsuccess = pai_alloc_batch(tsdn, &shard->pai, PAGE, NALLOCS / 2,
274 &allocs_list, &deferred_work_generated);
275 expect_zu_eq(NALLOCS / 2, nsuccess, "Unexpected oom");
276 for (size_t i = NALLOCS / 2; i < NALLOCS; i++) {
277 allocs[i] = edata_list_active_first(&allocs_list);
278 edata_list_active_remove(&allocs_list, allocs[i]);
279 }
280
281 /*
282 * Should have allocated them contiguously, despite the differing
283 * methods used.
284 */
285 void *orig_base = edata_base_get(allocs[0]);
286 expect_contiguous(allocs, NALLOCS);
287
288 /*
289 * Batch dalloc the first half, individually deallocate the second half.
290 */
291 for (size_t i = 0; i < NALLOCS / 2; i++) {
292 edata_list_active_append(&allocs_list, allocs[i]);
293 }
294 pai_dalloc_batch(tsdn, &shard->pai, &allocs_list,
295 &deferred_work_generated);
296 for (size_t i = NALLOCS / 2; i < NALLOCS; i++) {
297 pai_dalloc(tsdn, &shard->pai, allocs[i],
298 &deferred_work_generated);
299 }
300
301 /* Reallocate (individually), and ensure reuse and contiguity. */
302 for (size_t i = 0; i < NALLOCS; i++) {
303 allocs[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE,
304 /* zero */ false, /* guarded */ false, /* frequent_reuse */
305 false, &deferred_work_generated);
306 expect_ptr_not_null(allocs[i], "Unexpected alloc failure.");
307 }
308 void *new_base = edata_base_get(allocs[0]);
309 expect_ptr_eq(orig_base, new_base,
310 "Failed to reuse the allocated memory.");
311 expect_contiguous(allocs, NALLOCS);
312
313 destroy_test_data(shard);
314}
315TEST_END
316
317static uintptr_t defer_bump_ptr = HUGEPAGE * 123;
318static void *
319defer_test_map(size_t size) {
320 void *result = (void *)defer_bump_ptr;
321 defer_bump_ptr += size;
322 return result;
323}
324
325static void
326defer_test_unmap(void *ptr, size_t size) {
327 (void)ptr;
328 (void)size;
329}
330
331static bool defer_purge_called = false;
332static void
333defer_test_purge(void *ptr, size_t size) {
334 (void)ptr;
335 (void)size;
336 defer_purge_called = true;
337}
338
339static bool defer_hugify_called = false;
340static void
341defer_test_hugify(void *ptr, size_t size) {
342 defer_hugify_called = true;
343}
344
345static bool defer_dehugify_called = false;
346static void
347defer_test_dehugify(void *ptr, size_t size) {
348 defer_dehugify_called = true;
349}
350
351static nstime_t defer_curtime;
352static void
353defer_test_curtime(nstime_t *r_time, bool first_reading) {
354 *r_time = defer_curtime;
355}
356
357static uint64_t
358defer_test_ms_since(nstime_t *past_time) {
359 return (nstime_ns(&defer_curtime) - nstime_ns(past_time)) / 1000 / 1000;
360}
361
362TEST_BEGIN(test_defer_time) {
363 test_skip_if(!hpa_supported());
364
365 hpa_hooks_t hooks;
366 hooks.map = &defer_test_map;
367 hooks.unmap = &defer_test_unmap;
368 hooks.purge = &defer_test_purge;
369 hooks.hugify = &defer_test_hugify;
370 hooks.dehugify = &defer_test_dehugify;
371 hooks.curtime = &defer_test_curtime;
372 hooks.ms_since = &defer_test_ms_since;
373
374 hpa_shard_opts_t opts = test_hpa_shard_opts_default;
375 opts.deferral_allowed = true;
376
377 hpa_shard_t *shard = create_test_data(&hooks, &opts);
378
379 bool deferred_work_generated = false;
380
381 nstime_init(&defer_curtime, 0);
382 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
383 edata_t *edatas[HUGEPAGE_PAGES];
384 for (int i = 0; i < (int)HUGEPAGE_PAGES; i++) {
385 edatas[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false,
386 false, false, &deferred_work_generated);
387 expect_ptr_not_null(edatas[i], "Unexpected null edata");
388 }
389 hpa_shard_do_deferred_work(tsdn, shard);
390 expect_false(defer_hugify_called, "Hugified too early");
391
392 /* Hugification delay is set to 10 seconds in options. */
393 nstime_init2(&defer_curtime, 11, 0);
394 hpa_shard_do_deferred_work(tsdn, shard);
395 expect_true(defer_hugify_called, "Failed to hugify");
396
397 defer_hugify_called = false;
398
399 /* Purge. Recall that dirty_mult is .25. */
400 for (int i = 0; i < (int)HUGEPAGE_PAGES / 2; i++) {
401 pai_dalloc(tsdn, &shard->pai, edatas[i],
402 &deferred_work_generated);
403 }
404
405 hpa_shard_do_deferred_work(tsdn, shard);
406
407 expect_false(defer_hugify_called, "Hugified too early");
408 expect_true(defer_dehugify_called, "Should have dehugified");
409 expect_true(defer_purge_called, "Should have purged");
410 defer_hugify_called = false;
411 defer_dehugify_called = false;
412 defer_purge_called = false;
413
414 /*
415 * Refill the page. We now meet the hugification threshold; we should
416 * be marked for pending hugify.
417 */
418 for (int i = 0; i < (int)HUGEPAGE_PAGES / 2; i++) {
419 edatas[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false,
420 false, false, &deferred_work_generated);
421 expect_ptr_not_null(edatas[i], "Unexpected null edata");
422 }
423 /*
424 * We would be ineligible for hugification, had we not already met the
425 * threshold before dipping below it.
426 */
427 pai_dalloc(tsdn, &shard->pai, edatas[0],
428 &deferred_work_generated);
429 /* Wait for the threshold again. */
430 nstime_init2(&defer_curtime, 22, 0);
431 hpa_shard_do_deferred_work(tsdn, shard);
432 expect_true(defer_hugify_called, "Hugified too early");
433 expect_false(defer_dehugify_called, "Unexpected dehugify");
434 expect_false(defer_purge_called, "Unexpected purge");
435
436 destroy_test_data(shard);
437}
438TEST_END
439
440int
441main(void) {
442 /*
443 * These trigger unused-function warnings on CI runs, even if declared
444 * with static inline.
445 */
446 (void)mem_tree_empty;
447 (void)mem_tree_last;
448 (void)mem_tree_search;
449 (void)mem_tree_nsearch;
450 (void)mem_tree_psearch;
451 (void)mem_tree_iter;
452 (void)mem_tree_reverse_iter;
453 (void)mem_tree_destroy;
454 return test_no_reentrancy(
455 test_alloc_max,
456 test_stress,
457 test_alloc_dalloc_batch,
458 test_defer_time);
459}