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