#include "test/jemalloc_test.h"

#ifdef JEMALLOC_PROF
const char *malloc_conf = "prof:true,lg_prof_sample:0";
#endif

static unsigned
get_nsizes_impl(const char *cmd)
{
	unsigned ret;
	size_t z;

	z = sizeof(unsigned);
	assert_d_eq(mallctl(cmd, &ret, &z, NULL, 0), 0,
	    "Unexpected mallctl(\"%s\", ...) failure", cmd);

	return (ret);
}

static unsigned
get_nsmall(void)
{

	return (get_nsizes_impl("arenas.nbins"));
}

static unsigned
get_nlarge(void)
{

	return (get_nsizes_impl("arenas.nlruns"));
}

static unsigned
get_nhuge(void)
{

	return (get_nsizes_impl("arenas.nhchunks"));
}

static size_t
get_size_impl(const char *cmd, size_t ind)
{
	size_t ret;
	size_t z;
	size_t mib[4];
	size_t miblen = 4;

	z = sizeof(size_t);
	assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
	    0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
	mib[2] = ind;
	z = sizeof(size_t);
	assert_d_eq(mallctlbymib(mib, miblen, &ret, &z, NULL, 0),
	    0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);

	return (ret);
}

static size_t
get_small_size(size_t ind)
{

	return (get_size_impl("arenas.bin.0.size", ind));
}

static size_t
get_large_size(size_t ind)
{

	return (get_size_impl("arenas.lrun.0.size", ind));
}

static size_t
get_huge_size(size_t ind)
{

	return (get_size_impl("arenas.hchunk.0.size", ind));
}

TEST_BEGIN(test_arena_reset)
{
#define	NHUGE	4
	unsigned arena_ind, nsmall, nlarge, nhuge, nptrs, i;
	size_t sz, miblen;
	void **ptrs;
	int flags;
	size_t mib[3];
	tsdn_t *tsdn;

	test_skip_if((config_valgrind && unlikely(in_valgrind)) || (config_fill
	    && unlikely(opt_quarantine)));

	sz = sizeof(unsigned);
	assert_d_eq(mallctl("arenas.extend", &arena_ind, &sz, NULL, 0), 0,
	    "Unexpected mallctl() failure");

	flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;

	nsmall = get_nsmall();
	nlarge = get_nlarge();
	nhuge = get_nhuge() > NHUGE ? NHUGE : get_nhuge();
	nptrs = nsmall + nlarge + nhuge;
	ptrs = (void **)malloc(nptrs * sizeof(void *));
	assert_ptr_not_null(ptrs, "Unexpected malloc() failure");

	/* Allocate objects with a wide range of sizes. */
	for (i = 0; i < nsmall; i++) {
		sz = get_small_size(i);
		ptrs[i] = mallocx(sz, flags);
		assert_ptr_not_null(ptrs[i],
		    "Unexpected mallocx(%zu, %#x) failure", sz, flags);
	}
	for (i = 0; i < nlarge; i++) {
		sz = get_large_size(i);
		ptrs[nsmall + i] = mallocx(sz, flags);
		assert_ptr_not_null(ptrs[i],
		    "Unexpected mallocx(%zu, %#x) failure", sz, flags);
	}
	for (i = 0; i < nhuge; i++) {
		sz = get_huge_size(i);
		ptrs[nsmall + nlarge + i] = mallocx(sz, flags);
		assert_ptr_not_null(ptrs[i],
		    "Unexpected mallocx(%zu, %#x) failure", sz, flags);
	}

	tsdn = tsdn_fetch();

	/* Verify allocations. */
	for (i = 0; i < nptrs; i++) {
		assert_zu_gt(ivsalloc(tsdn, ptrs[i], false), 0,
		    "Allocation should have queryable size");
	}

	/* Reset. */
	miblen = sizeof(mib)/sizeof(size_t);
	assert_d_eq(mallctlnametomib("arena.0.reset", mib, &miblen), 0,
	    "Unexpected mallctlnametomib() failure");
	mib[1] = (size_t)arena_ind;
	assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
	    "Unexpected mallctlbymib() failure");

	/* Verify allocations no longer exist. */
	for (i = 0; i < nptrs; i++) {
		assert_zu_eq(ivsalloc(tsdn, ptrs[i], false), 0,
		    "Allocation should no longer exist");
	}

	free(ptrs);
}
TEST_END

int
main(void)
{

	return (test(
	    test_arena_reset));
}
