xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/cache_bin.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1*7bdf38e5Schristos #include "test/jemalloc_test.h"
2*7bdf38e5Schristos 
3*7bdf38e5Schristos static void
4*7bdf38e5Schristos do_fill_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
5*7bdf38e5Schristos     cache_bin_sz_t ncached_max, cache_bin_sz_t nfill_attempt,
6*7bdf38e5Schristos     cache_bin_sz_t nfill_succeed) {
7*7bdf38e5Schristos 	bool success;
8*7bdf38e5Schristos 	void *ptr;
9*7bdf38e5Schristos 	assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
10*7bdf38e5Schristos 	CACHE_BIN_PTR_ARRAY_DECLARE(arr, nfill_attempt);
11*7bdf38e5Schristos 	cache_bin_init_ptr_array_for_fill(bin, info, &arr, nfill_attempt);
12*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nfill_succeed; i++) {
13*7bdf38e5Schristos 		arr.ptr[i] = &ptrs[i];
14*7bdf38e5Schristos 	}
15*7bdf38e5Schristos 	cache_bin_finish_fill(bin, info, &arr, nfill_succeed);
16*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(bin, info) == nfill_succeed,
17*7bdf38e5Schristos 	    "");
18*7bdf38e5Schristos 	cache_bin_low_water_set(bin);
19*7bdf38e5Schristos 
20*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nfill_succeed; i++) {
21*7bdf38e5Schristos 		ptr = cache_bin_alloc(bin, &success);
22*7bdf38e5Schristos 		expect_true(success, "");
23*7bdf38e5Schristos 		expect_ptr_eq(ptr, (void *)&ptrs[i],
24*7bdf38e5Schristos 		    "Should pop in order filled");
25*7bdf38e5Schristos 		expect_true(cache_bin_low_water_get(bin, info)
26*7bdf38e5Schristos 		    == nfill_succeed - i - 1, "");
27*7bdf38e5Schristos 	}
28*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(bin, info) == 0, "");
29*7bdf38e5Schristos 	expect_true(cache_bin_low_water_get(bin, info) == 0, "");
30*7bdf38e5Schristos }
31*7bdf38e5Schristos 
32*7bdf38e5Schristos static void
33*7bdf38e5Schristos do_flush_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
34*7bdf38e5Schristos     cache_bin_sz_t nfill, cache_bin_sz_t nflush) {
35*7bdf38e5Schristos 	bool success;
36*7bdf38e5Schristos 	assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
37*7bdf38e5Schristos 
38*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nfill; i++) {
39*7bdf38e5Schristos 		success = cache_bin_dalloc_easy(bin, &ptrs[i]);
40*7bdf38e5Schristos 		expect_true(success, "");
41*7bdf38e5Schristos 	}
42*7bdf38e5Schristos 
43*7bdf38e5Schristos 	CACHE_BIN_PTR_ARRAY_DECLARE(arr, nflush);
44*7bdf38e5Schristos 	cache_bin_init_ptr_array_for_flush(bin, info, &arr, nflush);
45*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nflush; i++) {
46*7bdf38e5Schristos 		expect_ptr_eq(arr.ptr[i], &ptrs[nflush - i - 1], "");
47*7bdf38e5Schristos 	}
48*7bdf38e5Schristos 	cache_bin_finish_flush(bin, info, &arr, nflush);
49*7bdf38e5Schristos 
50*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(bin, info) == nfill - nflush,
51*7bdf38e5Schristos 	    "");
52*7bdf38e5Schristos 	while (cache_bin_ncached_get_local(bin, info) > 0) {
53*7bdf38e5Schristos 		cache_bin_alloc(bin, &success);
54*7bdf38e5Schristos 	}
55*7bdf38e5Schristos }
56*7bdf38e5Schristos 
57*7bdf38e5Schristos static void
58*7bdf38e5Schristos do_batch_alloc_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
59*7bdf38e5Schristos     cache_bin_sz_t nfill, size_t batch) {
60*7bdf38e5Schristos 	assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
61*7bdf38e5Schristos 	CACHE_BIN_PTR_ARRAY_DECLARE(arr, nfill);
62*7bdf38e5Schristos 	cache_bin_init_ptr_array_for_fill(bin, info, &arr, nfill);
63*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nfill; i++) {
64*7bdf38e5Schristos 		arr.ptr[i] = &ptrs[i];
65*7bdf38e5Schristos 	}
66*7bdf38e5Schristos 	cache_bin_finish_fill(bin, info, &arr, nfill);
67*7bdf38e5Schristos 	assert_true(cache_bin_ncached_get_local(bin, info) == nfill, "");
68*7bdf38e5Schristos 	cache_bin_low_water_set(bin);
69*7bdf38e5Schristos 
70*7bdf38e5Schristos 	void **out = malloc((batch + 1) * sizeof(void *));
71*7bdf38e5Schristos 	size_t n = cache_bin_alloc_batch(bin, batch, out);
72*7bdf38e5Schristos 	assert_true(n == ((size_t)nfill < batch ? (size_t)nfill : batch), "");
73*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < (cache_bin_sz_t)n; i++) {
74*7bdf38e5Schristos 		expect_ptr_eq(out[i], &ptrs[i], "");
75*7bdf38e5Schristos 	}
76*7bdf38e5Schristos 	expect_true(cache_bin_low_water_get(bin, info) == nfill -
77*7bdf38e5Schristos 	    (cache_bin_sz_t)n, "");
78*7bdf38e5Schristos 	while (cache_bin_ncached_get_local(bin, info) > 0) {
79*7bdf38e5Schristos 		bool success;
80*7bdf38e5Schristos 		cache_bin_alloc(bin, &success);
81*7bdf38e5Schristos 	}
82*7bdf38e5Schristos 	free(out);
83*7bdf38e5Schristos }
84*7bdf38e5Schristos 
85*7bdf38e5Schristos static void
86*7bdf38e5Schristos test_bin_init(cache_bin_t *bin, cache_bin_info_t *info) {
87*7bdf38e5Schristos 	size_t size;
88*7bdf38e5Schristos 	size_t alignment;
89*7bdf38e5Schristos 	cache_bin_info_compute_alloc(info, 1, &size, &alignment);
90*7bdf38e5Schristos 	void *mem = mallocx(size, MALLOCX_ALIGN(alignment));
91*7bdf38e5Schristos 	assert_ptr_not_null(mem, "Unexpected mallocx failure");
92*7bdf38e5Schristos 
93*7bdf38e5Schristos 	size_t cur_offset = 0;
94*7bdf38e5Schristos 	cache_bin_preincrement(info, 1, mem, &cur_offset);
95*7bdf38e5Schristos 	cache_bin_init(bin, info, mem, &cur_offset);
96*7bdf38e5Schristos 	cache_bin_postincrement(info, 1, mem, &cur_offset);
97*7bdf38e5Schristos 	assert_zu_eq(cur_offset, size, "Should use all requested memory");
98*7bdf38e5Schristos }
99*7bdf38e5Schristos 
100*7bdf38e5Schristos TEST_BEGIN(test_cache_bin) {
101*7bdf38e5Schristos 	const int ncached_max = 100;
102*7bdf38e5Schristos 	bool success;
103*7bdf38e5Schristos 	void *ptr;
104*7bdf38e5Schristos 
105*7bdf38e5Schristos 	cache_bin_info_t info;
106*7bdf38e5Schristos 	cache_bin_info_init(&info, ncached_max);
107*7bdf38e5Schristos 	cache_bin_t bin;
108*7bdf38e5Schristos 	test_bin_init(&bin, &info);
109*7bdf38e5Schristos 
110*7bdf38e5Schristos 	/* Initialize to empty; should then have 0 elements. */
111*7bdf38e5Schristos 	expect_d_eq(ncached_max, cache_bin_info_ncached_max(&info), "");
112*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(&bin, &info) == 0, "");
113*7bdf38e5Schristos 	expect_true(cache_bin_low_water_get(&bin, &info) == 0, "");
114*7bdf38e5Schristos 
115*7bdf38e5Schristos 	ptr = cache_bin_alloc_easy(&bin, &success);
116*7bdf38e5Schristos 	expect_false(success, "Shouldn't successfully allocate when empty");
117*7bdf38e5Schristos 	expect_ptr_null(ptr, "Shouldn't get a non-null pointer on failure");
118*7bdf38e5Schristos 
119*7bdf38e5Schristos 	ptr = cache_bin_alloc(&bin, &success);
120*7bdf38e5Schristos 	expect_false(success, "Shouldn't successfully allocate when empty");
121*7bdf38e5Schristos 	expect_ptr_null(ptr, "Shouldn't get a non-null pointer on failure");
122*7bdf38e5Schristos 
123*7bdf38e5Schristos 	/*
124*7bdf38e5Schristos 	 * We allocate one more item than ncached_max, so we can test cache bin
125*7bdf38e5Schristos 	 * exhaustion.
126*7bdf38e5Schristos 	 */
127*7bdf38e5Schristos 	void **ptrs = mallocx(sizeof(void *) * (ncached_max + 1), 0);
128*7bdf38e5Schristos 	assert_ptr_not_null(ptrs, "Unexpected mallocx failure");
129*7bdf38e5Schristos 	for  (cache_bin_sz_t i = 0; i < ncached_max; i++) {
130*7bdf38e5Schristos 		expect_true(cache_bin_ncached_get_local(&bin, &info) == i, "");
131*7bdf38e5Schristos 		success = cache_bin_dalloc_easy(&bin, &ptrs[i]);
132*7bdf38e5Schristos 		expect_true(success,
133*7bdf38e5Schristos 		    "Should be able to dalloc into a non-full cache bin.");
134*7bdf38e5Schristos 		expect_true(cache_bin_low_water_get(&bin, &info) == 0,
135*7bdf38e5Schristos 		    "Pushes and pops shouldn't change low water of zero.");
136*7bdf38e5Schristos 	}
137*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(&bin, &info) == ncached_max,
138*7bdf38e5Schristos 	    "");
139*7bdf38e5Schristos 	success = cache_bin_dalloc_easy(&bin, &ptrs[ncached_max]);
140*7bdf38e5Schristos 	expect_false(success, "Shouldn't be able to dalloc into a full bin.");
141*7bdf38e5Schristos 
142*7bdf38e5Schristos 	cache_bin_low_water_set(&bin);
143*7bdf38e5Schristos 
144*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
145*7bdf38e5Schristos 		expect_true(cache_bin_low_water_get(&bin, &info)
146*7bdf38e5Schristos 		    == ncached_max - i, "");
147*7bdf38e5Schristos 		expect_true(cache_bin_ncached_get_local(&bin, &info)
148*7bdf38e5Schristos 		    == ncached_max - i, "");
149*7bdf38e5Schristos 		/*
150*7bdf38e5Schristos 		 * This should fail -- the easy variant can't change the low
151*7bdf38e5Schristos 		 * water mark.
152*7bdf38e5Schristos 		 */
153*7bdf38e5Schristos 		ptr = cache_bin_alloc_easy(&bin, &success);
154*7bdf38e5Schristos 		expect_ptr_null(ptr, "");
155*7bdf38e5Schristos 		expect_false(success, "");
156*7bdf38e5Schristos 		expect_true(cache_bin_low_water_get(&bin, &info)
157*7bdf38e5Schristos 		    == ncached_max - i, "");
158*7bdf38e5Schristos 		expect_true(cache_bin_ncached_get_local(&bin, &info)
159*7bdf38e5Schristos 		    == ncached_max - i, "");
160*7bdf38e5Schristos 
161*7bdf38e5Schristos 		/* This should succeed, though. */
162*7bdf38e5Schristos 		ptr = cache_bin_alloc(&bin, &success);
163*7bdf38e5Schristos 		expect_true(success, "");
164*7bdf38e5Schristos 		expect_ptr_eq(ptr, &ptrs[ncached_max - i - 1],
165*7bdf38e5Schristos 		    "Alloc should pop in stack order");
166*7bdf38e5Schristos 		expect_true(cache_bin_low_water_get(&bin, &info)
167*7bdf38e5Schristos 		    == ncached_max - i - 1, "");
168*7bdf38e5Schristos 		expect_true(cache_bin_ncached_get_local(&bin, &info)
169*7bdf38e5Schristos 		    == ncached_max - i - 1, "");
170*7bdf38e5Schristos 	}
171*7bdf38e5Schristos 	/* Now we're empty -- all alloc attempts should fail. */
172*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(&bin, &info) == 0, "");
173*7bdf38e5Schristos 	ptr = cache_bin_alloc_easy(&bin, &success);
174*7bdf38e5Schristos 	expect_ptr_null(ptr, "");
175*7bdf38e5Schristos 	expect_false(success, "");
176*7bdf38e5Schristos 	ptr = cache_bin_alloc(&bin, &success);
177*7bdf38e5Schristos 	expect_ptr_null(ptr, "");
178*7bdf38e5Schristos 	expect_false(success, "");
179*7bdf38e5Schristos 
180*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < ncached_max / 2; i++) {
181*7bdf38e5Schristos 		cache_bin_dalloc_easy(&bin, &ptrs[i]);
182*7bdf38e5Schristos 	}
183*7bdf38e5Schristos 	cache_bin_low_water_set(&bin);
184*7bdf38e5Schristos 
185*7bdf38e5Schristos 	for (cache_bin_sz_t i = ncached_max / 2; i < ncached_max; i++) {
186*7bdf38e5Schristos 		cache_bin_dalloc_easy(&bin, &ptrs[i]);
187*7bdf38e5Schristos 	}
188*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(&bin, &info) == ncached_max,
189*7bdf38e5Schristos 	    "");
190*7bdf38e5Schristos 	for (cache_bin_sz_t i = ncached_max - 1; i >= ncached_max / 2; i--) {
191*7bdf38e5Schristos 		/*
192*7bdf38e5Schristos 		 * Size is bigger than low water -- the reduced version should
193*7bdf38e5Schristos 		 * succeed.
194*7bdf38e5Schristos 		 */
195*7bdf38e5Schristos 		ptr = cache_bin_alloc_easy(&bin, &success);
196*7bdf38e5Schristos 		expect_true(success, "");
197*7bdf38e5Schristos 		expect_ptr_eq(ptr, &ptrs[i], "");
198*7bdf38e5Schristos 	}
199*7bdf38e5Schristos 	/* But now, we've hit low-water. */
200*7bdf38e5Schristos 	ptr = cache_bin_alloc_easy(&bin, &success);
201*7bdf38e5Schristos 	expect_false(success, "");
202*7bdf38e5Schristos 	expect_ptr_null(ptr, "");
203*7bdf38e5Schristos 
204*7bdf38e5Schristos 	/* We're going to test filling -- we must be empty to start. */
205*7bdf38e5Schristos 	while (cache_bin_ncached_get_local(&bin, &info)) {
206*7bdf38e5Schristos 		cache_bin_alloc(&bin, &success);
207*7bdf38e5Schristos 		expect_true(success, "");
208*7bdf38e5Schristos 	}
209*7bdf38e5Schristos 
210*7bdf38e5Schristos 	/* Test fill. */
211*7bdf38e5Schristos 	/* Try to fill all, succeed fully. */
212*7bdf38e5Schristos 	do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max, ncached_max);
213*7bdf38e5Schristos 	/* Try to fill all, succeed partially. */
214*7bdf38e5Schristos 	do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max,
215*7bdf38e5Schristos 	    ncached_max / 2);
216*7bdf38e5Schristos 	/* Try to fill all, fail completely. */
217*7bdf38e5Schristos 	do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max, 0);
218*7bdf38e5Schristos 
219*7bdf38e5Schristos 	/* Try to fill some, succeed fully. */
220*7bdf38e5Schristos 	do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2,
221*7bdf38e5Schristos 	    ncached_max / 2);
222*7bdf38e5Schristos 	/* Try to fill some, succeed partially. */
223*7bdf38e5Schristos 	do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2,
224*7bdf38e5Schristos 	    ncached_max / 4);
225*7bdf38e5Schristos 	/* Try to fill some, fail completely. */
226*7bdf38e5Schristos 	do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2, 0);
227*7bdf38e5Schristos 
228*7bdf38e5Schristos 	do_flush_test(&bin, &info, ptrs, ncached_max, ncached_max);
229*7bdf38e5Schristos 	do_flush_test(&bin, &info, ptrs, ncached_max, ncached_max / 2);
230*7bdf38e5Schristos 	do_flush_test(&bin, &info, ptrs, ncached_max, 0);
231*7bdf38e5Schristos 	do_flush_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 2);
232*7bdf38e5Schristos 	do_flush_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 4);
233*7bdf38e5Schristos 	do_flush_test(&bin, &info, ptrs, ncached_max / 2, 0);
234*7bdf38e5Schristos 
235*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max);
236*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max * 2);
237*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max / 2);
238*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 2);
239*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 1);
240*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 0);
241*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2,
242*7bdf38e5Schristos 	    ncached_max / 2);
243*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, ncached_max);
244*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2,
245*7bdf38e5Schristos 	    ncached_max / 4);
246*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 2);
247*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 1);
248*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 0);
249*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 2, ncached_max);
250*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 2, 2);
251*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 2, 1);
252*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 2, 0);
253*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 1, 2);
254*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 1, 1);
255*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 1, 0);
256*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 0, 2);
257*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 0, 1);
258*7bdf38e5Schristos 	do_batch_alloc_test(&bin, &info, ptrs, 0, 0);
259*7bdf38e5Schristos 
260*7bdf38e5Schristos 	free(ptrs);
261*7bdf38e5Schristos }
262*7bdf38e5Schristos TEST_END
263*7bdf38e5Schristos 
264*7bdf38e5Schristos static void
265*7bdf38e5Schristos do_flush_stashed_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
266*7bdf38e5Schristos     cache_bin_sz_t nfill, cache_bin_sz_t nstash) {
267*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(bin, info) == 0,
268*7bdf38e5Schristos 	    "Bin not empty");
269*7bdf38e5Schristos 	expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
270*7bdf38e5Schristos 	    "Bin not empty");
271*7bdf38e5Schristos 	expect_true(nfill + nstash <= info->ncached_max, "Exceeded max");
272*7bdf38e5Schristos 
273*7bdf38e5Schristos 	bool ret;
274*7bdf38e5Schristos 	/* Fill */
275*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nfill; i++) {
276*7bdf38e5Schristos 		ret = cache_bin_dalloc_easy(bin, &ptrs[i]);
277*7bdf38e5Schristos 		expect_true(ret, "Unexpected fill failure");
278*7bdf38e5Schristos 	}
279*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(bin, info) == nfill,
280*7bdf38e5Schristos 	    "Wrong cached count");
281*7bdf38e5Schristos 
282*7bdf38e5Schristos 	/* Stash */
283*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nstash; i++) {
284*7bdf38e5Schristos 		ret = cache_bin_stash(bin, &ptrs[i + nfill]);
285*7bdf38e5Schristos 		expect_true(ret, "Unexpected stash failure");
286*7bdf38e5Schristos 	}
287*7bdf38e5Schristos 	expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
288*7bdf38e5Schristos 	    "Wrong stashed count");
289*7bdf38e5Schristos 
290*7bdf38e5Schristos 	if (nfill + nstash == info->ncached_max) {
291*7bdf38e5Schristos 		ret = cache_bin_dalloc_easy(bin, &ptrs[0]);
292*7bdf38e5Schristos 		expect_false(ret, "Should not dalloc into a full bin");
293*7bdf38e5Schristos 		ret = cache_bin_stash(bin, &ptrs[0]);
294*7bdf38e5Schristos 		expect_false(ret, "Should not stash into a full bin");
295*7bdf38e5Schristos 	}
296*7bdf38e5Schristos 
297*7bdf38e5Schristos 	/* Alloc filled ones */
298*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < nfill; i++) {
299*7bdf38e5Schristos 		void *ptr = cache_bin_alloc(bin, &ret);
300*7bdf38e5Schristos 		expect_true(ret, "Unexpected alloc failure");
301*7bdf38e5Schristos 		/* Verify it's not from the stashed range. */
302*7bdf38e5Schristos 		expect_true((uintptr_t)ptr < (uintptr_t)&ptrs[nfill],
303*7bdf38e5Schristos 		    "Should not alloc stashed ptrs");
304*7bdf38e5Schristos 	}
305*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(bin, info) == 0,
306*7bdf38e5Schristos 	    "Wrong cached count");
307*7bdf38e5Schristos 	expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
308*7bdf38e5Schristos 	    "Wrong stashed count");
309*7bdf38e5Schristos 
310*7bdf38e5Schristos 	cache_bin_alloc(bin, &ret);
311*7bdf38e5Schristos 	expect_false(ret, "Should not alloc stashed");
312*7bdf38e5Schristos 
313*7bdf38e5Schristos 	/* Clear stashed ones */
314*7bdf38e5Schristos 	cache_bin_finish_flush_stashed(bin, info);
315*7bdf38e5Schristos 	expect_true(cache_bin_ncached_get_local(bin, info) == 0,
316*7bdf38e5Schristos 	    "Wrong cached count");
317*7bdf38e5Schristos 	expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
318*7bdf38e5Schristos 	    "Wrong stashed count");
319*7bdf38e5Schristos 
320*7bdf38e5Schristos 	cache_bin_alloc(bin, &ret);
321*7bdf38e5Schristos 	expect_false(ret, "Should not alloc from empty bin");
322*7bdf38e5Schristos }
323*7bdf38e5Schristos 
324*7bdf38e5Schristos TEST_BEGIN(test_cache_bin_stash) {
325*7bdf38e5Schristos 	const int ncached_max = 100;
326*7bdf38e5Schristos 
327*7bdf38e5Schristos 	cache_bin_t bin;
328*7bdf38e5Schristos 	cache_bin_info_t info;
329*7bdf38e5Schristos 	cache_bin_info_init(&info, ncached_max);
330*7bdf38e5Schristos 	test_bin_init(&bin, &info);
331*7bdf38e5Schristos 
332*7bdf38e5Schristos 	/*
333*7bdf38e5Schristos 	 * The content of this array is not accessed; instead the interior
334*7bdf38e5Schristos 	 * addresses are used to insert / stash into the bins as test pointers.
335*7bdf38e5Schristos 	 */
336*7bdf38e5Schristos 	void **ptrs = mallocx(sizeof(void *) * (ncached_max + 1), 0);
337*7bdf38e5Schristos 	assert_ptr_not_null(ptrs, "Unexpected mallocx failure");
338*7bdf38e5Schristos 	bool ret;
339*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
340*7bdf38e5Schristos 		expect_true(cache_bin_ncached_get_local(&bin, &info) ==
341*7bdf38e5Schristos 		    (i / 2 + i % 2), "Wrong ncached value");
342*7bdf38e5Schristos 		expect_true(cache_bin_nstashed_get_local(&bin, &info) == i / 2,
343*7bdf38e5Schristos 		    "Wrong nstashed value");
344*7bdf38e5Schristos 		if (i % 2 == 0) {
345*7bdf38e5Schristos 			cache_bin_dalloc_easy(&bin, &ptrs[i]);
346*7bdf38e5Schristos 		} else {
347*7bdf38e5Schristos 			ret = cache_bin_stash(&bin, &ptrs[i]);
348*7bdf38e5Schristos 			expect_true(ret, "Should be able to stash into a "
349*7bdf38e5Schristos 			    "non-full cache bin");
350*7bdf38e5Schristos 		}
351*7bdf38e5Schristos 	}
352*7bdf38e5Schristos 	ret = cache_bin_dalloc_easy(&bin, &ptrs[0]);
353*7bdf38e5Schristos 	expect_false(ret, "Should not dalloc into a full cache bin");
354*7bdf38e5Schristos 	ret = cache_bin_stash(&bin, &ptrs[0]);
355*7bdf38e5Schristos 	expect_false(ret, "Should not stash into a full cache bin");
356*7bdf38e5Schristos 	for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
357*7bdf38e5Schristos 		void *ptr = cache_bin_alloc(&bin, &ret);
358*7bdf38e5Schristos 		if (i < ncached_max / 2) {
359*7bdf38e5Schristos 			expect_true(ret, "Should be able to alloc");
360*7bdf38e5Schristos 			uintptr_t diff = ((uintptr_t)ptr - (uintptr_t)&ptrs[0])
361*7bdf38e5Schristos 			    / sizeof(void *);
362*7bdf38e5Schristos 			expect_true(diff % 2 == 0, "Should be able to alloc");
363*7bdf38e5Schristos 		} else {
364*7bdf38e5Schristos 			expect_false(ret, "Should not alloc stashed");
365*7bdf38e5Schristos 			expect_true(cache_bin_nstashed_get_local(&bin, &info) ==
366*7bdf38e5Schristos 			    ncached_max / 2, "Wrong nstashed value");
367*7bdf38e5Schristos 		}
368*7bdf38e5Schristos 	}
369*7bdf38e5Schristos 
370*7bdf38e5Schristos 	test_bin_init(&bin, &info);
371*7bdf38e5Schristos 	do_flush_stashed_test(&bin, &info, ptrs, ncached_max, 0);
372*7bdf38e5Schristos 	do_flush_stashed_test(&bin, &info, ptrs, 0, ncached_max);
373*7bdf38e5Schristos 	do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 2);
374*7bdf38e5Schristos 	do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 4, ncached_max / 2);
375*7bdf38e5Schristos 	do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 4);
376*7bdf38e5Schristos 	do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 4, ncached_max / 4);
377*7bdf38e5Schristos }
378*7bdf38e5Schristos TEST_END
379*7bdf38e5Schristos 
380*7bdf38e5Schristos int
381*7bdf38e5Schristos main(void) {
382*7bdf38e5Schristos 	return test(test_cache_bin,
383*7bdf38e5Schristos 		test_cache_bin_stash);
384*7bdf38e5Schristos }
385