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