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