Home | History | Annotate | Line # | Download | only in unit
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