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