decay.c revision 1.1 1 1.1 christos #include "test/jemalloc_test.h"
2 1.1 christos
3 1.1 christos #include "jemalloc/internal/ticker.h"
4 1.1 christos
5 1.1 christos static nstime_monotonic_t *nstime_monotonic_orig;
6 1.1 christos static nstime_update_t *nstime_update_orig;
7 1.1 christos
8 1.1 christos static unsigned nupdates_mock;
9 1.1 christos static nstime_t time_mock;
10 1.1 christos static bool monotonic_mock;
11 1.1 christos
12 1.1 christos static bool
13 1.1 christos check_background_thread_enabled(void) {
14 1.1 christos bool enabled;
15 1.1 christos size_t sz = sizeof(bool);
16 1.1 christos int ret = mallctl("background_thread", (void *)&enabled, &sz, NULL,0);
17 1.1 christos if (ret == ENOENT) {
18 1.1 christos return false;
19 1.1 christos }
20 1.1 christos assert_d_eq(ret, 0, "Unexpected mallctl error");
21 1.1 christos return enabled;
22 1.1 christos }
23 1.1 christos
24 1.1 christos static bool
25 1.1 christos nstime_monotonic_mock(void) {
26 1.1 christos return monotonic_mock;
27 1.1 christos }
28 1.1 christos
29 1.1 christos static bool
30 1.1 christos nstime_update_mock(nstime_t *time) {
31 1.1 christos nupdates_mock++;
32 1.1 christos if (monotonic_mock) {
33 1.1 christos nstime_copy(time, &time_mock);
34 1.1 christos }
35 1.1 christos return !monotonic_mock;
36 1.1 christos }
37 1.1 christos
38 1.1 christos static unsigned
39 1.1 christos do_arena_create(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) {
40 1.1 christos unsigned arena_ind;
41 1.1 christos size_t sz = sizeof(unsigned);
42 1.1 christos assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
43 1.1 christos 0, "Unexpected mallctl() failure");
44 1.1 christos size_t mib[3];
45 1.1 christos size_t miblen = sizeof(mib)/sizeof(size_t);
46 1.1 christos
47 1.1 christos assert_d_eq(mallctlnametomib("arena.0.dirty_decay_ms", mib, &miblen),
48 1.1 christos 0, "Unexpected mallctlnametomib() failure");
49 1.1 christos mib[1] = (size_t)arena_ind;
50 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,
51 1.1 christos (void *)&dirty_decay_ms, sizeof(dirty_decay_ms)), 0,
52 1.1 christos "Unexpected mallctlbymib() failure");
53 1.1 christos
54 1.1 christos assert_d_eq(mallctlnametomib("arena.0.muzzy_decay_ms", mib, &miblen),
55 1.1 christos 0, "Unexpected mallctlnametomib() failure");
56 1.1 christos mib[1] = (size_t)arena_ind;
57 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,
58 1.1 christos (void *)&muzzy_decay_ms, sizeof(muzzy_decay_ms)), 0,
59 1.1 christos "Unexpected mallctlbymib() failure");
60 1.1 christos
61 1.1 christos return arena_ind;
62 1.1 christos }
63 1.1 christos
64 1.1 christos static void
65 1.1 christos do_arena_destroy(unsigned arena_ind) {
66 1.1 christos size_t mib[3];
67 1.1 christos size_t miblen = sizeof(mib)/sizeof(size_t);
68 1.1 christos assert_d_eq(mallctlnametomib("arena.0.destroy", mib, &miblen), 0,
69 1.1 christos "Unexpected mallctlnametomib() failure");
70 1.1 christos mib[1] = (size_t)arena_ind;
71 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
72 1.1 christos "Unexpected mallctlbymib() failure");
73 1.1 christos }
74 1.1 christos
75 1.1 christos void
76 1.1 christos do_epoch(void) {
77 1.1 christos uint64_t epoch = 1;
78 1.1 christos assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
79 1.1 christos 0, "Unexpected mallctl() failure");
80 1.1 christos }
81 1.1 christos
82 1.1 christos void
83 1.1 christos do_purge(unsigned arena_ind) {
84 1.1 christos size_t mib[3];
85 1.1 christos size_t miblen = sizeof(mib)/sizeof(size_t);
86 1.1 christos assert_d_eq(mallctlnametomib("arena.0.purge", mib, &miblen), 0,
87 1.1 christos "Unexpected mallctlnametomib() failure");
88 1.1 christos mib[1] = (size_t)arena_ind;
89 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
90 1.1 christos "Unexpected mallctlbymib() failure");
91 1.1 christos }
92 1.1 christos
93 1.1 christos void
94 1.1 christos do_decay(unsigned arena_ind) {
95 1.1 christos size_t mib[3];
96 1.1 christos size_t miblen = sizeof(mib)/sizeof(size_t);
97 1.1 christos assert_d_eq(mallctlnametomib("arena.0.decay", mib, &miblen), 0,
98 1.1 christos "Unexpected mallctlnametomib() failure");
99 1.1 christos mib[1] = (size_t)arena_ind;
100 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
101 1.1 christos "Unexpected mallctlbymib() failure");
102 1.1 christos }
103 1.1 christos
104 1.1 christos static uint64_t
105 1.1 christos get_arena_npurge_impl(const char *mibname, unsigned arena_ind) {
106 1.1 christos size_t mib[4];
107 1.1 christos size_t miblen = sizeof(mib)/sizeof(size_t);
108 1.1 christos assert_d_eq(mallctlnametomib(mibname, mib, &miblen), 0,
109 1.1 christos "Unexpected mallctlnametomib() failure");
110 1.1 christos mib[2] = (size_t)arena_ind;
111 1.1 christos uint64_t npurge = 0;
112 1.1 christos size_t sz = sizeof(npurge);
113 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, (void *)&npurge, &sz, NULL, 0),
114 1.1 christos config_stats ? 0 : ENOENT, "Unexpected mallctlbymib() failure");
115 1.1 christos return npurge;
116 1.1 christos }
117 1.1 christos
118 1.1 christos static uint64_t
119 1.1 christos get_arena_dirty_npurge(unsigned arena_ind) {
120 1.1 christos do_epoch();
121 1.1 christos return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind);
122 1.1 christos }
123 1.1 christos
124 1.1 christos static uint64_t
125 1.1 christos get_arena_muzzy_npurge(unsigned arena_ind) {
126 1.1 christos do_epoch();
127 1.1 christos return get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind);
128 1.1 christos }
129 1.1 christos
130 1.1 christos static uint64_t
131 1.1 christos get_arena_npurge(unsigned arena_ind) {
132 1.1 christos do_epoch();
133 1.1 christos return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind) +
134 1.1 christos get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind);
135 1.1 christos }
136 1.1 christos
137 1.1 christos static size_t
138 1.1 christos get_arena_pdirty(unsigned arena_ind) {
139 1.1 christos do_epoch();
140 1.1 christos size_t mib[4];
141 1.1 christos size_t miblen = sizeof(mib)/sizeof(size_t);
142 1.1 christos assert_d_eq(mallctlnametomib("stats.arenas.0.pdirty", mib, &miblen), 0,
143 1.1 christos "Unexpected mallctlnametomib() failure");
144 1.1 christos mib[2] = (size_t)arena_ind;
145 1.1 christos size_t pdirty;
146 1.1 christos size_t sz = sizeof(pdirty);
147 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, (void *)&pdirty, &sz, NULL, 0), 0,
148 1.1 christos "Unexpected mallctlbymib() failure");
149 1.1 christos return pdirty;
150 1.1 christos }
151 1.1 christos
152 1.1 christos static size_t
153 1.1 christos get_arena_pmuzzy(unsigned arena_ind) {
154 1.1 christos do_epoch();
155 1.1 christos size_t mib[4];
156 1.1 christos size_t miblen = sizeof(mib)/sizeof(size_t);
157 1.1 christos assert_d_eq(mallctlnametomib("stats.arenas.0.pmuzzy", mib, &miblen), 0,
158 1.1 christos "Unexpected mallctlnametomib() failure");
159 1.1 christos mib[2] = (size_t)arena_ind;
160 1.1 christos size_t pmuzzy;
161 1.1 christos size_t sz = sizeof(pmuzzy);
162 1.1 christos assert_d_eq(mallctlbymib(mib, miblen, (void *)&pmuzzy, &sz, NULL, 0), 0,
163 1.1 christos "Unexpected mallctlbymib() failure");
164 1.1 christos return pmuzzy;
165 1.1 christos }
166 1.1 christos
167 1.1 christos static void *
168 1.1 christos do_mallocx(size_t size, int flags) {
169 1.1 christos void *p = mallocx(size, flags);
170 1.1 christos assert_ptr_not_null(p, "Unexpected mallocx() failure");
171 1.1 christos return p;
172 1.1 christos }
173 1.1 christos
174 1.1 christos static void
175 1.1 christos generate_dirty(unsigned arena_ind, size_t size) {
176 1.1 christos int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
177 1.1 christos void *p = do_mallocx(size, flags);
178 1.1 christos dallocx(p, flags);
179 1.1 christos }
180 1.1 christos
181 1.1 christos TEST_BEGIN(test_decay_ticks) {
182 1.1 christos test_skip_if(check_background_thread_enabled());
183 1.1 christos
184 1.1 christos ticker_t *decay_ticker;
185 1.1 christos unsigned tick0, tick1, arena_ind;
186 1.1 christos size_t sz, large0;
187 1.1 christos void *p;
188 1.1 christos
189 1.1 christos sz = sizeof(size_t);
190 1.1 christos assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
191 1.1 christos 0), 0, "Unexpected mallctl failure");
192 1.1 christos
193 1.1 christos /* Set up a manually managed arena for test. */
194 1.1 christos arena_ind = do_arena_create(0, 0);
195 1.1 christos
196 1.1 christos /* Migrate to the new arena, and get the ticker. */
197 1.1 christos unsigned old_arena_ind;
198 1.1 christos size_t sz_arena_ind = sizeof(old_arena_ind);
199 1.1 christos assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind,
200 1.1 christos &sz_arena_ind, (void *)&arena_ind, sizeof(arena_ind)), 0,
201 1.1 christos "Unexpected mallctl() failure");
202 1.1 christos decay_ticker = decay_ticker_get(tsd_fetch(), arena_ind);
203 1.1 christos assert_ptr_not_null(decay_ticker,
204 1.1 christos "Unexpected failure getting decay ticker");
205 1.1 christos
206 1.1 christos /*
207 1.1 christos * Test the standard APIs using a large size class, since we can't
208 1.1 christos * control tcache interactions for small size classes (except by
209 1.1 christos * completely disabling tcache for the entire test program).
210 1.1 christos */
211 1.1 christos
212 1.1 christos /* malloc(). */
213 1.1 christos tick0 = ticker_read(decay_ticker);
214 1.1 christos p = malloc(large0);
215 1.1 christos assert_ptr_not_null(p, "Unexpected malloc() failure");
216 1.1 christos tick1 = ticker_read(decay_ticker);
217 1.1 christos assert_u32_ne(tick1, tick0, "Expected ticker to tick during malloc()");
218 1.1 christos /* free(). */
219 1.1 christos tick0 = ticker_read(decay_ticker);
220 1.1 christos free(p);
221 1.1 christos tick1 = ticker_read(decay_ticker);
222 1.1 christos assert_u32_ne(tick1, tick0, "Expected ticker to tick during free()");
223 1.1 christos
224 1.1 christos /* calloc(). */
225 1.1 christos tick0 = ticker_read(decay_ticker);
226 1.1 christos p = calloc(1, large0);
227 1.1 christos assert_ptr_not_null(p, "Unexpected calloc() failure");
228 1.1 christos tick1 = ticker_read(decay_ticker);
229 1.1 christos assert_u32_ne(tick1, tick0, "Expected ticker to tick during calloc()");
230 1.1 christos free(p);
231 1.1 christos
232 1.1 christos /* posix_memalign(). */
233 1.1 christos tick0 = ticker_read(decay_ticker);
234 1.1 christos assert_d_eq(posix_memalign(&p, sizeof(size_t), large0), 0,
235 1.1 christos "Unexpected posix_memalign() failure");
236 1.1 christos tick1 = ticker_read(decay_ticker);
237 1.1 christos assert_u32_ne(tick1, tick0,
238 1.1 christos "Expected ticker to tick during posix_memalign()");
239 1.1 christos free(p);
240 1.1 christos
241 1.1 christos /* aligned_alloc(). */
242 1.1 christos tick0 = ticker_read(decay_ticker);
243 1.1 christos p = aligned_alloc(sizeof(size_t), large0);
244 1.1 christos assert_ptr_not_null(p, "Unexpected aligned_alloc() failure");
245 1.1 christos tick1 = ticker_read(decay_ticker);
246 1.1 christos assert_u32_ne(tick1, tick0,
247 1.1 christos "Expected ticker to tick during aligned_alloc()");
248 1.1 christos free(p);
249 1.1 christos
250 1.1 christos /* realloc(). */
251 1.1 christos /* Allocate. */
252 1.1 christos tick0 = ticker_read(decay_ticker);
253 1.1 christos p = realloc(NULL, large0);
254 1.1 christos assert_ptr_not_null(p, "Unexpected realloc() failure");
255 1.1 christos tick1 = ticker_read(decay_ticker);
256 1.1 christos assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
257 1.1 christos /* Reallocate. */
258 1.1 christos tick0 = ticker_read(decay_ticker);
259 1.1 christos p = realloc(p, large0);
260 1.1 christos assert_ptr_not_null(p, "Unexpected realloc() failure");
261 1.1 christos tick1 = ticker_read(decay_ticker);
262 1.1 christos assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
263 1.1 christos /* Deallocate. */
264 1.1 christos tick0 = ticker_read(decay_ticker);
265 1.1 christos realloc(p, 0);
266 1.1 christos tick1 = ticker_read(decay_ticker);
267 1.1 christos assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
268 1.1 christos
269 1.1 christos /*
270 1.1 christos * Test the *allocx() APIs using large and small size classes, with
271 1.1 christos * tcache explicitly disabled.
272 1.1 christos */
273 1.1 christos {
274 1.1 christos unsigned i;
275 1.1 christos size_t allocx_sizes[2];
276 1.1 christos allocx_sizes[0] = large0;
277 1.1 christos allocx_sizes[1] = 1;
278 1.1 christos
279 1.1 christos for (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) {
280 1.1 christos sz = allocx_sizes[i];
281 1.1 christos
282 1.1 christos /* mallocx(). */
283 1.1 christos tick0 = ticker_read(decay_ticker);
284 1.1 christos p = mallocx(sz, MALLOCX_TCACHE_NONE);
285 1.1 christos assert_ptr_not_null(p, "Unexpected mallocx() failure");
286 1.1 christos tick1 = ticker_read(decay_ticker);
287 1.1 christos assert_u32_ne(tick1, tick0,
288 1.1 christos "Expected ticker to tick during mallocx() (sz=%zu)",
289 1.1 christos sz);
290 1.1 christos /* rallocx(). */
291 1.1 christos tick0 = ticker_read(decay_ticker);
292 1.1 christos p = rallocx(p, sz, MALLOCX_TCACHE_NONE);
293 1.1 christos assert_ptr_not_null(p, "Unexpected rallocx() failure");
294 1.1 christos tick1 = ticker_read(decay_ticker);
295 1.1 christos assert_u32_ne(tick1, tick0,
296 1.1 christos "Expected ticker to tick during rallocx() (sz=%zu)",
297 1.1 christos sz);
298 1.1 christos /* xallocx(). */
299 1.1 christos tick0 = ticker_read(decay_ticker);
300 1.1 christos xallocx(p, sz, 0, MALLOCX_TCACHE_NONE);
301 1.1 christos tick1 = ticker_read(decay_ticker);
302 1.1 christos assert_u32_ne(tick1, tick0,
303 1.1 christos "Expected ticker to tick during xallocx() (sz=%zu)",
304 1.1 christos sz);
305 1.1 christos /* dallocx(). */
306 1.1 christos tick0 = ticker_read(decay_ticker);
307 1.1 christos dallocx(p, MALLOCX_TCACHE_NONE);
308 1.1 christos tick1 = ticker_read(decay_ticker);
309 1.1 christos assert_u32_ne(tick1, tick0,
310 1.1 christos "Expected ticker to tick during dallocx() (sz=%zu)",
311 1.1 christos sz);
312 1.1 christos /* sdallocx(). */
313 1.1 christos p = mallocx(sz, MALLOCX_TCACHE_NONE);
314 1.1 christos assert_ptr_not_null(p, "Unexpected mallocx() failure");
315 1.1 christos tick0 = ticker_read(decay_ticker);
316 1.1 christos sdallocx(p, sz, MALLOCX_TCACHE_NONE);
317 1.1 christos tick1 = ticker_read(decay_ticker);
318 1.1 christos assert_u32_ne(tick1, tick0,
319 1.1 christos "Expected ticker to tick during sdallocx() "
320 1.1 christos "(sz=%zu)", sz);
321 1.1 christos }
322 1.1 christos }
323 1.1 christos
324 1.1 christos /*
325 1.1 christos * Test tcache fill/flush interactions for large and small size classes,
326 1.1 christos * using an explicit tcache.
327 1.1 christos */
328 1.1 christos unsigned tcache_ind, i;
329 1.1 christos size_t tcache_sizes[2];
330 1.1 christos tcache_sizes[0] = large0;
331 1.1 christos tcache_sizes[1] = 1;
332 1.1 christos
333 1.1 christos size_t tcache_max, sz_tcache_max;
334 1.1 christos sz_tcache_max = sizeof(tcache_max);
335 1.1 christos assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max,
336 1.1 christos &sz_tcache_max, NULL, 0), 0, "Unexpected mallctl() failure");
337 1.1 christos
338 1.1 christos sz = sizeof(unsigned);
339 1.1 christos assert_d_eq(mallctl("tcache.create", (void *)&tcache_ind, &sz,
340 1.1 christos NULL, 0), 0, "Unexpected mallctl failure");
341 1.1 christos
342 1.1 christos for (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) {
343 1.1 christos sz = tcache_sizes[i];
344 1.1 christos
345 1.1 christos /* tcache fill. */
346 1.1 christos tick0 = ticker_read(decay_ticker);
347 1.1 christos p = mallocx(sz, MALLOCX_TCACHE(tcache_ind));
348 1.1 christos assert_ptr_not_null(p, "Unexpected mallocx() failure");
349 1.1 christos tick1 = ticker_read(decay_ticker);
350 1.1 christos assert_u32_ne(tick1, tick0,
351 1.1 christos "Expected ticker to tick during tcache fill "
352 1.1 christos "(sz=%zu)", sz);
353 1.1 christos /* tcache flush. */
354 1.1 christos dallocx(p, MALLOCX_TCACHE(tcache_ind));
355 1.1 christos tick0 = ticker_read(decay_ticker);
356 1.1 christos assert_d_eq(mallctl("tcache.flush", NULL, NULL,
357 1.1 christos (void *)&tcache_ind, sizeof(unsigned)), 0,
358 1.1 christos "Unexpected mallctl failure");
359 1.1 christos tick1 = ticker_read(decay_ticker);
360 1.1 christos
361 1.1 christos /* Will only tick if it's in tcache. */
362 1.1 christos if (sz <= tcache_max) {
363 1.1 christos assert_u32_ne(tick1, tick0,
364 1.1 christos "Expected ticker to tick during tcache "
365 1.1 christos "flush (sz=%zu)", sz);
366 1.1 christos } else {
367 1.1 christos assert_u32_eq(tick1, tick0,
368 1.1 christos "Unexpected ticker tick during tcache "
369 1.1 christos "flush (sz=%zu)", sz);
370 1.1 christos }
371 1.1 christos }
372 1.1 christos }
373 1.1 christos TEST_END
374 1.1 christos
375 1.1 christos static void
376 1.1 christos decay_ticker_helper(unsigned arena_ind, int flags, bool dirty, ssize_t dt,
377 1.1 christos uint64_t dirty_npurge0, uint64_t muzzy_npurge0, bool terminate_asap) {
378 1.1 christos #define NINTERVALS 101
379 1.1 christos nstime_t time, update_interval, decay_ms, deadline;
380 1.1 christos
381 1.1 christos nstime_init(&time, 0);
382 1.1 christos nstime_update(&time);
383 1.1 christos
384 1.1 christos nstime_init2(&decay_ms, dt, 0);
385 1.1 christos nstime_copy(&deadline, &time);
386 1.1 christos nstime_add(&deadline, &decay_ms);
387 1.1 christos
388 1.1 christos nstime_init2(&update_interval, dt, 0);
389 1.1 christos nstime_idivide(&update_interval, NINTERVALS);
390 1.1 christos
391 1.1 christos /*
392 1.1 christos * Keep q's slab from being deallocated during the looping below. If a
393 1.1 christos * cached slab were to repeatedly come and go during looping, it could
394 1.1 christos * prevent the decay backlog ever becoming empty.
395 1.1 christos */
396 1.1 christos void *p = do_mallocx(1, flags);
397 1.1 christos uint64_t dirty_npurge1, muzzy_npurge1;
398 1.1 christos do {
399 1.1 christos for (unsigned i = 0; i < DECAY_NTICKS_PER_UPDATE / 2;
400 1.1 christos i++) {
401 1.1 christos void *q = do_mallocx(1, flags);
402 1.1 christos dallocx(q, flags);
403 1.1 christos }
404 1.1 christos dirty_npurge1 = get_arena_dirty_npurge(arena_ind);
405 1.1 christos muzzy_npurge1 = get_arena_muzzy_npurge(arena_ind);
406 1.1 christos
407 1.1 christos nstime_add(&time_mock, &update_interval);
408 1.1 christos nstime_update(&time);
409 1.1 christos } while (nstime_compare(&time, &deadline) <= 0 && ((dirty_npurge1 ==
410 1.1 christos dirty_npurge0 && muzzy_npurge1 == muzzy_npurge0) ||
411 1.1 christos !terminate_asap));
412 1.1 christos dallocx(p, flags);
413 1.1 christos
414 1.1 christos if (config_stats) {
415 1.1 christos assert_u64_gt(dirty_npurge1 + muzzy_npurge1, dirty_npurge0 +
416 1.1 christos muzzy_npurge0, "Expected purging to occur");
417 1.1 christos }
418 1.1 christos #undef NINTERVALS
419 1.1 christos }
420 1.1 christos
421 1.1 christos TEST_BEGIN(test_decay_ticker) {
422 1.1 christos test_skip_if(check_background_thread_enabled());
423 1.1 christos #define NPS 2048
424 1.1 christos ssize_t ddt = opt_dirty_decay_ms;
425 1.1 christos ssize_t mdt = opt_muzzy_decay_ms;
426 1.1 christos unsigned arena_ind = do_arena_create(ddt, mdt);
427 1.1 christos int flags = (MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE);
428 1.1 christos void *ps[NPS];
429 1.1 christos size_t large;
430 1.1 christos
431 1.1 christos /*
432 1.1 christos * Allocate a bunch of large objects, pause the clock, deallocate every
433 1.1 christos * other object (to fragment virtual memory), restore the clock, then
434 1.1 christos * [md]allocx() in a tight loop while advancing time rapidly to verify
435 1.1 christos * the ticker triggers purging.
436 1.1 christos */
437 1.1 christos
438 1.1 christos size_t tcache_max;
439 1.1 christos size_t sz = sizeof(size_t);
440 1.1 christos assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max, &sz, NULL,
441 1.1 christos 0), 0, "Unexpected mallctl failure");
442 1.1 christos large = nallocx(tcache_max + 1, flags);
443 1.1 christos
444 1.1 christos do_purge(arena_ind);
445 1.1 christos uint64_t dirty_npurge0 = get_arena_dirty_npurge(arena_ind);
446 1.1 christos uint64_t muzzy_npurge0 = get_arena_muzzy_npurge(arena_ind);
447 1.1 christos
448 1.1 christos for (unsigned i = 0; i < NPS; i++) {
449 1.1 christos ps[i] = do_mallocx(large, flags);
450 1.1 christos }
451 1.1 christos
452 1.1 christos nupdates_mock = 0;
453 1.1 christos nstime_init(&time_mock, 0);
454 1.1 christos nstime_update(&time_mock);
455 1.1 christos monotonic_mock = true;
456 1.1 christos
457 1.1 christos nstime_monotonic_orig = nstime_monotonic;
458 1.1 christos nstime_update_orig = nstime_update;
459 1.1 christos nstime_monotonic = nstime_monotonic_mock;
460 1.1 christos nstime_update = nstime_update_mock;
461 1.1 christos
462 1.1 christos for (unsigned i = 0; i < NPS; i += 2) {
463 1.1 christos dallocx(ps[i], flags);
464 1.1 christos unsigned nupdates0 = nupdates_mock;
465 1.1 christos do_decay(arena_ind);
466 1.1 christos assert_u_gt(nupdates_mock, nupdates0,
467 1.1 christos "Expected nstime_update() to be called");
468 1.1 christos }
469 1.1 christos
470 1.1 christos decay_ticker_helper(arena_ind, flags, true, ddt, dirty_npurge0,
471 1.1 christos muzzy_npurge0, true);
472 1.1 christos decay_ticker_helper(arena_ind, flags, false, ddt+mdt, dirty_npurge0,
473 1.1 christos muzzy_npurge0, false);
474 1.1 christos
475 1.1 christos do_arena_destroy(arena_ind);
476 1.1 christos
477 1.1 christos nstime_monotonic = nstime_monotonic_orig;
478 1.1 christos nstime_update = nstime_update_orig;
479 1.1 christos #undef NPS
480 1.1 christos }
481 1.1 christos TEST_END
482 1.1 christos
483 1.1 christos TEST_BEGIN(test_decay_nonmonotonic) {
484 1.1 christos test_skip_if(check_background_thread_enabled());
485 1.1 christos #define NPS (SMOOTHSTEP_NSTEPS + 1)
486 1.1 christos int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);
487 1.1 christos void *ps[NPS];
488 1.1 christos uint64_t npurge0 = 0;
489 1.1 christos uint64_t npurge1 = 0;
490 1.1 christos size_t sz, large0;
491 1.1 christos unsigned i, nupdates0;
492 1.1 christos
493 1.1 christos sz = sizeof(size_t);
494 1.1 christos assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
495 1.1 christos 0), 0, "Unexpected mallctl failure");
496 1.1 christos
497 1.1 christos assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
498 1.1 christos "Unexpected mallctl failure");
499 1.1 christos do_epoch();
500 1.1 christos sz = sizeof(uint64_t);
501 1.1 christos npurge0 = get_arena_npurge(0);
502 1.1 christos
503 1.1 christos nupdates_mock = 0;
504 1.1 christos nstime_init(&time_mock, 0);
505 1.1 christos nstime_update(&time_mock);
506 1.1 christos monotonic_mock = false;
507 1.1 christos
508 1.1 christos nstime_monotonic_orig = nstime_monotonic;
509 1.1 christos nstime_update_orig = nstime_update;
510 1.1 christos nstime_monotonic = nstime_monotonic_mock;
511 1.1 christos nstime_update = nstime_update_mock;
512 1.1 christos
513 1.1 christos for (i = 0; i < NPS; i++) {
514 1.1 christos ps[i] = mallocx(large0, flags);
515 1.1 christos assert_ptr_not_null(ps[i], "Unexpected mallocx() failure");
516 1.1 christos }
517 1.1 christos
518 1.1 christos for (i = 0; i < NPS; i++) {
519 1.1 christos dallocx(ps[i], flags);
520 1.1 christos nupdates0 = nupdates_mock;
521 1.1 christos assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
522 1.1 christos "Unexpected arena.0.decay failure");
523 1.1 christos assert_u_gt(nupdates_mock, nupdates0,
524 1.1 christos "Expected nstime_update() to be called");
525 1.1 christos }
526 1.1 christos
527 1.1 christos do_epoch();
528 1.1 christos sz = sizeof(uint64_t);
529 1.1 christos npurge1 = get_arena_npurge(0);
530 1.1 christos
531 1.1 christos if (config_stats) {
532 1.1 christos assert_u64_eq(npurge0, npurge1, "Unexpected purging occurred");
533 1.1 christos }
534 1.1 christos
535 1.1 christos nstime_monotonic = nstime_monotonic_orig;
536 1.1 christos nstime_update = nstime_update_orig;
537 1.1 christos #undef NPS
538 1.1 christos }
539 1.1 christos TEST_END
540 1.1 christos
541 1.1 christos TEST_BEGIN(test_decay_now) {
542 1.1 christos test_skip_if(check_background_thread_enabled());
543 1.1 christos
544 1.1 christos unsigned arena_ind = do_arena_create(0, 0);
545 1.1 christos assert_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages");
546 1.1 christos assert_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages");
547 1.1 christos size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};
548 1.1 christos /* Verify that dirty/muzzy pages never linger after deallocation. */
549 1.1 christos for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
550 1.1 christos size_t size = sizes[i];
551 1.1 christos generate_dirty(arena_ind, size);
552 1.1 christos assert_zu_eq(get_arena_pdirty(arena_ind), 0,
553 1.1 christos "Unexpected dirty pages");
554 1.1 christos assert_zu_eq(get_arena_pmuzzy(arena_ind), 0,
555 1.1 christos "Unexpected muzzy pages");
556 1.1 christos }
557 1.1 christos do_arena_destroy(arena_ind);
558 1.1 christos }
559 1.1 christos TEST_END
560 1.1 christos
561 1.1 christos TEST_BEGIN(test_decay_never) {
562 1.1 christos test_skip_if(check_background_thread_enabled());
563 1.1 christos
564 1.1 christos unsigned arena_ind = do_arena_create(-1, -1);
565 1.1 christos int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
566 1.1 christos assert_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages");
567 1.1 christos assert_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages");
568 1.1 christos size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};
569 1.1 christos void *ptrs[sizeof(sizes)/sizeof(size_t)];
570 1.1 christos for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
571 1.1 christos ptrs[i] = do_mallocx(sizes[i], flags);
572 1.1 christos }
573 1.1 christos /* Verify that each deallocation generates additional dirty pages. */
574 1.1 christos size_t pdirty_prev = get_arena_pdirty(arena_ind);
575 1.1 christos size_t pmuzzy_prev = get_arena_pmuzzy(arena_ind);
576 1.1 christos assert_zu_eq(pdirty_prev, 0, "Unexpected dirty pages");
577 1.1 christos assert_zu_eq(pmuzzy_prev, 0, "Unexpected muzzy pages");
578 1.1 christos for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
579 1.1 christos dallocx(ptrs[i], flags);
580 1.1 christos size_t pdirty = get_arena_pdirty(arena_ind);
581 1.1 christos size_t pmuzzy = get_arena_pmuzzy(arena_ind);
582 1.1 christos assert_zu_gt(pdirty, pdirty_prev,
583 1.1 christos "Expected dirty pages to increase.");
584 1.1 christos assert_zu_eq(pmuzzy, 0, "Unexpected muzzy pages");
585 1.1 christos pdirty_prev = pdirty;
586 1.1 christos }
587 1.1 christos do_arena_destroy(arena_ind);
588 1.1 christos }
589 1.1 christos TEST_END
590 1.1 christos
591 1.1 christos int
592 1.1 christos main(void) {
593 1.1 christos return test(
594 1.1 christos test_decay_ticks,
595 1.1 christos test_decay_ticker,
596 1.1 christos test_decay_nonmonotonic,
597 1.1 christos test_decay_now,
598 1.1 christos test_decay_never);
599 1.1 christos }
600