]> git.proxmox.com Git - rustc.git/blob - src/jemalloc/test/unit/decay.c
New upstream version 1.21.0+dfsg1
[rustc.git] / src / jemalloc / test / unit / decay.c
1 #include "test/jemalloc_test.h"
2
3 static nstime_monotonic_t *nstime_monotonic_orig;
4 static nstime_update_t *nstime_update_orig;
5
6 static unsigned nupdates_mock;
7 static nstime_t time_mock;
8 static bool monotonic_mock;
9
10 static bool
11 nstime_monotonic_mock(void)
12 {
13
14 return (monotonic_mock);
15 }
16
17 static bool
18 nstime_update_mock(nstime_t *time)
19 {
20
21 nupdates_mock++;
22 if (monotonic_mock)
23 nstime_copy(time, &time_mock);
24 return (!monotonic_mock);
25 }
26
27 TEST_BEGIN(test_decay_ticks)
28 {
29 ticker_t *decay_ticker;
30 unsigned tick0, tick1;
31 size_t sz, huge0, large0;
32 void *p;
33
34 test_skip_if(opt_purge != purge_mode_decay);
35
36 decay_ticker = decay_ticker_get(tsd_fetch(), 0);
37 assert_ptr_not_null(decay_ticker,
38 "Unexpected failure getting decay ticker");
39
40 sz = sizeof(size_t);
41 assert_d_eq(mallctl("arenas.hchunk.0.size", (void *)&huge0, &sz, NULL,
42 0), 0, "Unexpected mallctl failure");
43 assert_d_eq(mallctl("arenas.lrun.0.size", (void *)&large0, &sz, NULL,
44 0), 0, "Unexpected mallctl failure");
45
46 /*
47 * Test the standard APIs using a huge size class, since we can't
48 * control tcache interactions (except by completely disabling tcache
49 * for the entire test program).
50 */
51
52 /* malloc(). */
53 tick0 = ticker_read(decay_ticker);
54 p = malloc(huge0);
55 assert_ptr_not_null(p, "Unexpected malloc() failure");
56 tick1 = ticker_read(decay_ticker);
57 assert_u32_ne(tick1, tick0, "Expected ticker to tick during malloc()");
58 /* free(). */
59 tick0 = ticker_read(decay_ticker);
60 free(p);
61 tick1 = ticker_read(decay_ticker);
62 assert_u32_ne(tick1, tick0, "Expected ticker to tick during free()");
63
64 /* calloc(). */
65 tick0 = ticker_read(decay_ticker);
66 p = calloc(1, huge0);
67 assert_ptr_not_null(p, "Unexpected calloc() failure");
68 tick1 = ticker_read(decay_ticker);
69 assert_u32_ne(tick1, tick0, "Expected ticker to tick during calloc()");
70 free(p);
71
72 /* posix_memalign(). */
73 tick0 = ticker_read(decay_ticker);
74 assert_d_eq(posix_memalign(&p, sizeof(size_t), huge0), 0,
75 "Unexpected posix_memalign() failure");
76 tick1 = ticker_read(decay_ticker);
77 assert_u32_ne(tick1, tick0,
78 "Expected ticker to tick during posix_memalign()");
79 free(p);
80
81 /* aligned_alloc(). */
82 tick0 = ticker_read(decay_ticker);
83 p = aligned_alloc(sizeof(size_t), huge0);
84 assert_ptr_not_null(p, "Unexpected aligned_alloc() failure");
85 tick1 = ticker_read(decay_ticker);
86 assert_u32_ne(tick1, tick0,
87 "Expected ticker to tick during aligned_alloc()");
88 free(p);
89
90 /* realloc(). */
91 /* Allocate. */
92 tick0 = ticker_read(decay_ticker);
93 p = realloc(NULL, huge0);
94 assert_ptr_not_null(p, "Unexpected realloc() failure");
95 tick1 = ticker_read(decay_ticker);
96 assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
97 /* Reallocate. */
98 tick0 = ticker_read(decay_ticker);
99 p = realloc(p, huge0);
100 assert_ptr_not_null(p, "Unexpected realloc() failure");
101 tick1 = ticker_read(decay_ticker);
102 assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
103 /* Deallocate. */
104 tick0 = ticker_read(decay_ticker);
105 realloc(p, 0);
106 tick1 = ticker_read(decay_ticker);
107 assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
108
109 /*
110 * Test the *allocx() APIs using huge, large, and small size classes,
111 * with tcache explicitly disabled.
112 */
113 {
114 unsigned i;
115 size_t allocx_sizes[3];
116 allocx_sizes[0] = huge0;
117 allocx_sizes[1] = large0;
118 allocx_sizes[2] = 1;
119
120 for (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) {
121 sz = allocx_sizes[i];
122
123 /* mallocx(). */
124 tick0 = ticker_read(decay_ticker);
125 p = mallocx(sz, MALLOCX_TCACHE_NONE);
126 assert_ptr_not_null(p, "Unexpected mallocx() failure");
127 tick1 = ticker_read(decay_ticker);
128 assert_u32_ne(tick1, tick0,
129 "Expected ticker to tick during mallocx() (sz=%zu)",
130 sz);
131 /* rallocx(). */
132 tick0 = ticker_read(decay_ticker);
133 p = rallocx(p, sz, MALLOCX_TCACHE_NONE);
134 assert_ptr_not_null(p, "Unexpected rallocx() failure");
135 tick1 = ticker_read(decay_ticker);
136 assert_u32_ne(tick1, tick0,
137 "Expected ticker to tick during rallocx() (sz=%zu)",
138 sz);
139 /* xallocx(). */
140 tick0 = ticker_read(decay_ticker);
141 xallocx(p, sz, 0, MALLOCX_TCACHE_NONE);
142 tick1 = ticker_read(decay_ticker);
143 assert_u32_ne(tick1, tick0,
144 "Expected ticker to tick during xallocx() (sz=%zu)",
145 sz);
146 /* dallocx(). */
147 tick0 = ticker_read(decay_ticker);
148 dallocx(p, MALLOCX_TCACHE_NONE);
149 tick1 = ticker_read(decay_ticker);
150 assert_u32_ne(tick1, tick0,
151 "Expected ticker to tick during dallocx() (sz=%zu)",
152 sz);
153 /* sdallocx(). */
154 p = mallocx(sz, MALLOCX_TCACHE_NONE);
155 assert_ptr_not_null(p, "Unexpected mallocx() failure");
156 tick0 = ticker_read(decay_ticker);
157 sdallocx(p, sz, MALLOCX_TCACHE_NONE);
158 tick1 = ticker_read(decay_ticker);
159 assert_u32_ne(tick1, tick0,
160 "Expected ticker to tick during sdallocx() "
161 "(sz=%zu)", sz);
162 }
163 }
164
165 /*
166 * Test tcache fill/flush interactions for large and small size classes,
167 * using an explicit tcache.
168 */
169 if (config_tcache) {
170 unsigned tcache_ind, i;
171 size_t tcache_sizes[2];
172 tcache_sizes[0] = large0;
173 tcache_sizes[1] = 1;
174
175 sz = sizeof(unsigned);
176 assert_d_eq(mallctl("tcache.create", (void *)&tcache_ind, &sz,
177 NULL, 0), 0, "Unexpected mallctl failure");
178
179 for (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) {
180 sz = tcache_sizes[i];
181
182 /* tcache fill. */
183 tick0 = ticker_read(decay_ticker);
184 p = mallocx(sz, MALLOCX_TCACHE(tcache_ind));
185 assert_ptr_not_null(p, "Unexpected mallocx() failure");
186 tick1 = ticker_read(decay_ticker);
187 assert_u32_ne(tick1, tick0,
188 "Expected ticker to tick during tcache fill "
189 "(sz=%zu)", sz);
190 /* tcache flush. */
191 dallocx(p, MALLOCX_TCACHE(tcache_ind));
192 tick0 = ticker_read(decay_ticker);
193 assert_d_eq(mallctl("tcache.flush", NULL, NULL,
194 (void *)&tcache_ind, sizeof(unsigned)), 0,
195 "Unexpected mallctl failure");
196 tick1 = ticker_read(decay_ticker);
197 assert_u32_ne(tick1, tick0,
198 "Expected ticker to tick during tcache flush "
199 "(sz=%zu)", sz);
200 }
201 }
202 }
203 TEST_END
204
205 TEST_BEGIN(test_decay_ticker)
206 {
207 #define NPS 1024
208 int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);
209 void *ps[NPS];
210 uint64_t epoch;
211 uint64_t npurge0 = 0;
212 uint64_t npurge1 = 0;
213 size_t sz, large;
214 unsigned i, nupdates0;
215 nstime_t time, decay_time, deadline;
216
217 test_skip_if(opt_purge != purge_mode_decay);
218
219 /*
220 * Allocate a bunch of large objects, pause the clock, deallocate the
221 * objects, restore the clock, then [md]allocx() in a tight loop to
222 * verify the ticker triggers purging.
223 */
224
225 if (config_tcache) {
226 size_t tcache_max;
227
228 sz = sizeof(size_t);
229 assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max,
230 &sz, NULL, 0), 0, "Unexpected mallctl failure");
231 large = nallocx(tcache_max + 1, flags);
232 } else {
233 sz = sizeof(size_t);
234 assert_d_eq(mallctl("arenas.lrun.0.size", (void *)&large, &sz,
235 NULL, 0), 0, "Unexpected mallctl failure");
236 }
237
238 assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
239 "Unexpected mallctl failure");
240 assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
241 sizeof(uint64_t)), 0, "Unexpected mallctl failure");
242 sz = sizeof(uint64_t);
243 assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge0, &sz,
244 NULL, 0), config_stats ? 0 : ENOENT, "Unexpected mallctl result");
245
246 for (i = 0; i < NPS; i++) {
247 ps[i] = mallocx(large, flags);
248 assert_ptr_not_null(ps[i], "Unexpected mallocx() failure");
249 }
250
251 nupdates_mock = 0;
252 nstime_init(&time_mock, 0);
253 nstime_update(&time_mock);
254 monotonic_mock = true;
255
256 nstime_monotonic_orig = nstime_monotonic;
257 nstime_update_orig = nstime_update;
258 nstime_monotonic = nstime_monotonic_mock;
259 nstime_update = nstime_update_mock;
260
261 for (i = 0; i < NPS; i++) {
262 dallocx(ps[i], flags);
263 nupdates0 = nupdates_mock;
264 assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
265 "Unexpected arena.0.decay failure");
266 assert_u_gt(nupdates_mock, nupdates0,
267 "Expected nstime_update() to be called");
268 }
269
270 nstime_monotonic = nstime_monotonic_orig;
271 nstime_update = nstime_update_orig;
272
273 nstime_init(&time, 0);
274 nstime_update(&time);
275 nstime_init2(&decay_time, opt_decay_time, 0);
276 nstime_copy(&deadline, &time);
277 nstime_add(&deadline, &decay_time);
278 do {
279 for (i = 0; i < DECAY_NTICKS_PER_UPDATE / 2; i++) {
280 void *p = mallocx(1, flags);
281 assert_ptr_not_null(p, "Unexpected mallocx() failure");
282 dallocx(p, flags);
283 }
284 assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
285 sizeof(uint64_t)), 0, "Unexpected mallctl failure");
286 sz = sizeof(uint64_t);
287 assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge1,
288 &sz, NULL, 0), config_stats ? 0 : ENOENT,
289 "Unexpected mallctl result");
290
291 nstime_update(&time);
292 } while (nstime_compare(&time, &deadline) <= 0 && npurge1 == npurge0);
293
294 if (config_stats)
295 assert_u64_gt(npurge1, npurge0, "Expected purging to occur");
296 #undef NPS
297 }
298 TEST_END
299
300 TEST_BEGIN(test_decay_nonmonotonic)
301 {
302 #define NPS (SMOOTHSTEP_NSTEPS + 1)
303 int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);
304 void *ps[NPS];
305 uint64_t epoch;
306 uint64_t npurge0 = 0;
307 uint64_t npurge1 = 0;
308 size_t sz, large0;
309 unsigned i, nupdates0;
310
311 test_skip_if(opt_purge != purge_mode_decay);
312
313 sz = sizeof(size_t);
314 assert_d_eq(mallctl("arenas.lrun.0.size", (void *)&large0, &sz, NULL,
315 0), 0, "Unexpected mallctl failure");
316
317 assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
318 "Unexpected mallctl failure");
319 assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
320 sizeof(uint64_t)), 0, "Unexpected mallctl failure");
321 sz = sizeof(uint64_t);
322 assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge0, &sz,
323 NULL, 0), config_stats ? 0 : ENOENT, "Unexpected mallctl result");
324
325 nupdates_mock = 0;
326 nstime_init(&time_mock, 0);
327 nstime_update(&time_mock);
328 monotonic_mock = false;
329
330 nstime_monotonic_orig = nstime_monotonic;
331 nstime_update_orig = nstime_update;
332 nstime_monotonic = nstime_monotonic_mock;
333 nstime_update = nstime_update_mock;
334
335 for (i = 0; i < NPS; i++) {
336 ps[i] = mallocx(large0, flags);
337 assert_ptr_not_null(ps[i], "Unexpected mallocx() failure");
338 }
339
340 for (i = 0; i < NPS; i++) {
341 dallocx(ps[i], flags);
342 nupdates0 = nupdates_mock;
343 assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
344 "Unexpected arena.0.decay failure");
345 assert_u_gt(nupdates_mock, nupdates0,
346 "Expected nstime_update() to be called");
347 }
348
349 assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
350 sizeof(uint64_t)), 0, "Unexpected mallctl failure");
351 sz = sizeof(uint64_t);
352 assert_d_eq(mallctl("stats.arenas.0.npurge", (void *)&npurge1, &sz,
353 NULL, 0), config_stats ? 0 : ENOENT, "Unexpected mallctl result");
354
355 if (config_stats)
356 assert_u64_eq(npurge0, npurge1, "Unexpected purging occurred");
357
358 nstime_monotonic = nstime_monotonic_orig;
359 nstime_update = nstime_update_orig;
360 #undef NPS
361 }
362 TEST_END
363
364 int
365 main(void)
366 {
367
368 return (test(
369 test_decay_ticks,
370 test_decay_ticker,
371 test_decay_nonmonotonic));
372 }