/*
* Allocate. The main thread will reset the arena, so there's
* no need to deallocate.
*/
for (unsigned i = 0; i < PER_THD_NALLOCS; i++) {
void *p = mallocx(sz, MALLOCX_ARENA(arena_ind) |
MALLOCX_TCACHE_NONE
);
expect_ptr_not_null(p,
"Unexpected mallocx() failure\n");
}
/* Let the main thread know we've finished this iteration. */
atomic_fetch_add_u(&nfinished, 1, ATOMIC_RELEASE);
}
unsigned nthreads = ncpus * 2;
if (LG_SIZEOF_PTR < 3 && nthreads > 16) {
nthreads = 16; /* 32-bit platform could run out of vaddr. */
}
VARIABLE_ARRAY(thd_t, threads, nthreads);
for (unsigned i = 0; i < nthreads; i++) {
thd_create(&threads[i], thd_start, NULL);
}
for (unsigned e = 1; e < NEPOCHS; e++) {
atomic_store_u(&nfinished, 0, ATOMIC_RELEASE);
atomic_store_u(&epoch, e, ATOMIC_RELEASE);
/* Wait for threads to finish allocating. */
spin_t spinner = SPIN_INITIALIZER;
while (atomic_load_u(&nfinished, ATOMIC_ACQUIRE) < nthreads) {
spin_adaptive(&spinner);
}
/*
* Assert that retained is no more than the sum of size classes
* that should have been used to satisfy the worker threads'
* requests, discounting per growth fragmentation.
*/
do_refresh();
/*
* Clean up arena. Destroying and recreating the arena
* is simpler that specifying extent hooks that deallocate
* (rather than retaining) during reset.
*/
do_arena_destroy(arena_ind);
expect_u_eq(do_arena_create(NULL), arena_ind,
"Unexpected arena index");
}
for (unsigned i = 0; i < nthreads; i++) {
thd_join(threads[i], NULL);
}