1 #define JEMALLOC_CHUNK_C_ 2 #include "jemalloc/internal/jemalloc_internal.h" 3 4 /******************************************************************************/ 5 /* Data. */ 6 7 const char *opt_dss = DSS_DEFAULT; 8 size_t opt_lg_chunk = 0; 9 10 /* Used exclusively for gdump triggering. */ 11 static size_t curchunks; 12 static size_t highchunks; 13 14 rtree_t chunks_rtree; 15 16 /* Various chunk-related settings. */ 17 size_t chunksize; 18 size_t chunksize_mask; /* (chunksize - 1). */ 19 size_t chunk_npages; 20 21 static void *chunk_alloc_default(void *new_addr, size_t size, 22 size_t alignment, bool *zero, bool *commit, unsigned arena_ind); 23 static bool chunk_dalloc_default(void *chunk, size_t size, bool committed, 24 unsigned arena_ind); 25 static bool chunk_commit_default(void *chunk, size_t size, size_t offset, 26 size_t length, unsigned arena_ind); 27 static bool chunk_decommit_default(void *chunk, size_t size, size_t offset, 28 size_t length, unsigned arena_ind); 29 static bool chunk_purge_default(void *chunk, size_t size, size_t offset, 30 size_t length, unsigned arena_ind); 31 static bool chunk_split_default(void *chunk, size_t size, size_t size_a, 32 size_t size_b, bool committed, unsigned arena_ind); 33 static bool chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b, 34 size_t size_b, bool committed, unsigned arena_ind); 35 36 const chunk_hooks_t chunk_hooks_default = { 37 chunk_alloc_default, 38 chunk_dalloc_default, 39 chunk_commit_default, 40 chunk_decommit_default, 41 chunk_purge_default, 42 chunk_split_default, 43 chunk_merge_default 44 }; 45 46 /******************************************************************************/ 47 /* 48 * Function prototypes for static functions that are referenced prior to 49 * definition. 50 */ 51 52 static void chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks, 53 extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache, 54 void *chunk, size_t size, bool zeroed, bool committed); 55 56 /******************************************************************************/ 57 58 static chunk_hooks_t 59 chunk_hooks_get_locked(arena_t *arena) 60 { 61 62 return (arena->chunk_hooks); 63 } 64 65 chunk_hooks_t 66 chunk_hooks_get(arena_t *arena) 67 { 68 chunk_hooks_t chunk_hooks; 69 70 malloc_mutex_lock(&arena->chunks_mtx); 71 chunk_hooks = chunk_hooks_get_locked(arena); 72 malloc_mutex_unlock(&arena->chunks_mtx); 73 74 return (chunk_hooks); 75 } 76 77 chunk_hooks_t 78 chunk_hooks_set(arena_t *arena, const chunk_hooks_t *chunk_hooks) 79 { 80 chunk_hooks_t old_chunk_hooks; 81 82 malloc_mutex_lock(&arena->chunks_mtx); 83 old_chunk_hooks = arena->chunk_hooks; 84 /* 85 * Copy each field atomically so that it is impossible for readers to 86 * see partially updated pointers. There are places where readers only 87 * need one hook function pointer (therefore no need to copy the 88 * entirety of arena->chunk_hooks), and stale reads do not affect 89 * correctness, so they perform unlocked reads. 90 */ 91 #define ATOMIC_COPY_HOOK(n) do { \ 92 union { \ 93 chunk_##n##_t **n; \ 94 void **v; \ 95 } u; \ 96 u.n = &arena->chunk_hooks.n; \ 97 atomic_write_p(u.v, chunk_hooks->n); \ 98 } while (0) 99 ATOMIC_COPY_HOOK(alloc); 100 ATOMIC_COPY_HOOK(dalloc); 101 ATOMIC_COPY_HOOK(commit); 102 ATOMIC_COPY_HOOK(decommit); 103 ATOMIC_COPY_HOOK(purge); 104 ATOMIC_COPY_HOOK(split); 105 ATOMIC_COPY_HOOK(merge); 106 #undef ATOMIC_COPY_HOOK 107 malloc_mutex_unlock(&arena->chunks_mtx); 108 109 return (old_chunk_hooks); 110 } 111 112 static void 113 chunk_hooks_assure_initialized_impl(arena_t *arena, chunk_hooks_t *chunk_hooks, 114 bool locked) 115 { 116 static const chunk_hooks_t uninitialized_hooks = 117 CHUNK_HOOKS_INITIALIZER; 118 119 if (memcmp(chunk_hooks, &uninitialized_hooks, sizeof(chunk_hooks_t)) == 120 0) { 121 *chunk_hooks = locked ? chunk_hooks_get_locked(arena) : 122 chunk_hooks_get(arena); 123 } 124 } 125 126 static void 127 chunk_hooks_assure_initialized_locked(arena_t *arena, 128 chunk_hooks_t *chunk_hooks) 129 { 130 131 chunk_hooks_assure_initialized_impl(arena, chunk_hooks, true); 132 } 133 134 static void 135 chunk_hooks_assure_initialized(arena_t *arena, chunk_hooks_t *chunk_hooks) 136 { 137 138 chunk_hooks_assure_initialized_impl(arena, chunk_hooks, false); 139 } 140 141 bool 142 chunk_register(const void *chunk, const extent_node_t *node) 143 { 144 145 assert(extent_node_addr_get(node) == chunk); 146 147 if (rtree_set(&chunks_rtree, (uintptr_t)chunk, node)) 148 return (true); 149 if (config_prof && opt_prof) { 150 size_t size = extent_node_size_get(node); 151 size_t nadd = (size == 0) ? 1 : size / chunksize; 152 size_t cur = atomic_add_z(&curchunks, nadd); 153 size_t high = atomic_read_z(&highchunks); 154 while (cur > high && atomic_cas_z(&highchunks, high, cur)) { 155 /* 156 * Don't refresh cur, because it may have decreased 157 * since this thread lost the highchunks update race. 158 */ 159 high = atomic_read_z(&highchunks); 160 } 161 if (cur > high && prof_gdump_get_unlocked()) 162 prof_gdump(); 163 } 164 165 return (false); 166 } 167 168 void 169 chunk_deregister(const void *chunk, const extent_node_t *node) 170 { 171 bool err; 172 173 err = rtree_set(&chunks_rtree, (uintptr_t)chunk, NULL); 174 assert(!err); 175 if (config_prof && opt_prof) { 176 size_t size = extent_node_size_get(node); 177 size_t nsub = (size == 0) ? 1 : size / chunksize; 178 assert(atomic_read_z(&curchunks) >= nsub); 179 atomic_sub_z(&curchunks, nsub); 180 } 181 } 182 183 /* 184 * Do first-best-fit chunk selection, i.e. select the lowest chunk that best 185 * fits. 186 */ 187 static extent_node_t * 188 chunk_first_best_fit(arena_t *arena, extent_tree_t *chunks_szad, 189 extent_tree_t *chunks_ad, size_t size) 190 { 191 extent_node_t key; 192 193 assert(size == CHUNK_CEILING(size)); 194 195 extent_node_init(&key, arena, NULL, size, false, false); 196 return (extent_tree_szad_nsearch(chunks_szad, &key)); 197 } 198 199 static void * 200 chunk_recycle(arena_t *arena, chunk_hooks_t *chunk_hooks, 201 extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache, 202 void *new_addr, size_t size, size_t alignment, bool *zero, bool *commit, 203 bool dalloc_node) 204 { 205 void *ret; 206 extent_node_t *node; 207 size_t alloc_size, leadsize, trailsize; 208 bool zeroed, committed; 209 210 assert(new_addr == NULL || alignment == chunksize); 211 /* 212 * Cached chunks use the node linkage embedded in their headers, in 213 * which case dalloc_node is true, and new_addr is non-NULL because 214 * we're operating on a specific chunk. 215 */ 216 assert(dalloc_node || new_addr != NULL); 217 218 alloc_size = CHUNK_CEILING(s2u(size + alignment - chunksize)); 219 /* Beware size_t wrap-around. */ 220 if (alloc_size < size) 221 return (NULL); 222 malloc_mutex_lock(&arena->chunks_mtx); 223 chunk_hooks_assure_initialized_locked(arena, chunk_hooks); 224 if (new_addr != NULL) { 225 extent_node_t key; 226 extent_node_init(&key, arena, new_addr, alloc_size, false, 227 false); 228 node = extent_tree_ad_search(chunks_ad, &key); 229 } else { 230 node = chunk_first_best_fit(arena, chunks_szad, chunks_ad, 231 alloc_size); 232 } 233 if (node == NULL || (new_addr != NULL && extent_node_size_get(node) < 234 size)) { 235 malloc_mutex_unlock(&arena->chunks_mtx); 236 return (NULL); 237 } 238 leadsize = ALIGNMENT_CEILING((uintptr_t)extent_node_addr_get(node), 239 alignment) - (uintptr_t)extent_node_addr_get(node); 240 assert(new_addr == NULL || leadsize == 0); 241 assert(extent_node_size_get(node) >= leadsize + size); 242 trailsize = extent_node_size_get(node) - leadsize - size; 243 ret = (void *)((uintptr_t)extent_node_addr_get(node) + leadsize); 244 zeroed = extent_node_zeroed_get(node); 245 if (zeroed) 246 *zero = true; 247 committed = extent_node_committed_get(node); 248 if (committed) 249 *commit = true; 250 /* Split the lead. */ 251 if (leadsize != 0 && 252 chunk_hooks->split(extent_node_addr_get(node), 253 extent_node_size_get(node), leadsize, size, false, arena->ind)) { 254 malloc_mutex_unlock(&arena->chunks_mtx); 255 return (NULL); 256 } 257 /* Remove node from the tree. */ 258 extent_tree_szad_remove(chunks_szad, node); 259 extent_tree_ad_remove(chunks_ad, node); 260 arena_chunk_cache_maybe_remove(arena, node, cache); 261 if (leadsize != 0) { 262 /* Insert the leading space as a smaller chunk. */ 263 extent_node_size_set(node, leadsize); 264 extent_tree_szad_insert(chunks_szad, node); 265 extent_tree_ad_insert(chunks_ad, node); 266 arena_chunk_cache_maybe_insert(arena, node, cache); 267 node = NULL; 268 } 269 if (trailsize != 0) { 270 /* Split the trail. */ 271 if (chunk_hooks->split(ret, size + trailsize, size, 272 trailsize, false, arena->ind)) { 273 if (dalloc_node && node != NULL) 274 arena_node_dalloc(arena, node); 275 malloc_mutex_unlock(&arena->chunks_mtx); 276 chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad, 277 cache, ret, size + trailsize, zeroed, committed); 278 return (NULL); 279 } 280 /* Insert the trailing space as a smaller chunk. */ 281 if (node == NULL) { 282 node = arena_node_alloc(arena); 283 if (node == NULL) { 284 malloc_mutex_unlock(&arena->chunks_mtx); 285 chunk_record(arena, chunk_hooks, chunks_szad, 286 chunks_ad, cache, ret, size + trailsize, 287 zeroed, committed); 288 return (NULL); 289 } 290 } 291 extent_node_init(node, arena, (void *)((uintptr_t)(ret) + size), 292 trailsize, zeroed, committed); 293 extent_tree_szad_insert(chunks_szad, node); 294 extent_tree_ad_insert(chunks_ad, node); 295 arena_chunk_cache_maybe_insert(arena, node, cache); 296 node = NULL; 297 } 298 if (!committed && chunk_hooks->commit(ret, size, 0, size, arena->ind)) { 299 malloc_mutex_unlock(&arena->chunks_mtx); 300 chunk_record(arena, chunk_hooks, chunks_szad, chunks_ad, cache, 301 ret, size, zeroed, committed); 302 return (NULL); 303 } 304 malloc_mutex_unlock(&arena->chunks_mtx); 305 306 assert(dalloc_node || node != NULL); 307 if (dalloc_node && node != NULL) 308 arena_node_dalloc(arena, node); 309 if (*zero) { 310 if (!zeroed) 311 memset(ret, 0, size); 312 else if (config_debug) { 313 size_t i; 314 size_t *p = (size_t *)(uintptr_t)ret; 315 316 JEMALLOC_VALGRIND_MAKE_MEM_DEFINED(ret, size); 317 for (i = 0; i < size / sizeof(size_t); i++) 318 assert(p[i] == 0); 319 } 320 } 321 return (ret); 322 } 323 324 /* 325 * If the caller specifies (!*zero), it is still possible to receive zeroed 326 * memory, in which case *zero is toggled to true. arena_chunk_alloc() takes 327 * advantage of this to avoid demanding zeroed chunks, but taking advantage of 328 * them if they are returned. 329 */ 330 static void * 331 chunk_alloc_core(arena_t *arena, void *new_addr, size_t size, size_t alignment, 332 bool *zero, bool *commit, dss_prec_t dss_prec) 333 { 334 void *ret; 335 336 assert(size != 0); 337 assert((size & chunksize_mask) == 0); 338 assert(alignment != 0); 339 assert((alignment & chunksize_mask) == 0); 340 341 /* "primary" dss. */ 342 if (have_dss && dss_prec == dss_prec_primary && (ret = 343 chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) != 344 NULL) 345 return (ret); 346 /* mmap. */ 347 if ((ret = chunk_alloc_mmap(new_addr, size, alignment, zero, commit)) != 348 NULL) 349 return (ret); 350 /* "secondary" dss. */ 351 if (have_dss && dss_prec == dss_prec_secondary && (ret = 352 chunk_alloc_dss(arena, new_addr, size, alignment, zero, commit)) != 353 NULL) 354 return (ret); 355 356 /* All strategies for allocation failed. */ 357 return (NULL); 358 } 359 360 void * 361 chunk_alloc_base(size_t size) 362 { 363 void *ret; 364 bool zero, commit; 365 366 /* 367 * Directly call chunk_alloc_mmap() rather than chunk_alloc_core() 368 * because it's critical that chunk_alloc_base() return untouched 369 * demand-zeroed virtual memory. 370 */ 371 zero = true; 372 commit = true; 373 ret = chunk_alloc_mmap(NULL, size, chunksize, &zero, &commit); 374 if (ret == NULL) 375 return (NULL); 376 if (config_valgrind) 377 JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); 378 379 return (ret); 380 } 381 382 void * 383 chunk_alloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr, 384 size_t size, size_t alignment, bool *zero, bool dalloc_node) 385 { 386 void *ret; 387 bool commit; 388 389 assert(size != 0); 390 assert((size & chunksize_mask) == 0); 391 assert(alignment != 0); 392 assert((alignment & chunksize_mask) == 0); 393 394 commit = true; 395 ret = chunk_recycle(arena, chunk_hooks, &arena->chunks_szad_cached, 396 &arena->chunks_ad_cached, true, new_addr, size, alignment, zero, 397 &commit, dalloc_node); 398 if (ret == NULL) 399 return (NULL); 400 assert(commit); 401 if (config_valgrind) 402 JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); 403 return (ret); 404 } 405 406 static arena_t * 407 chunk_arena_get(unsigned arena_ind) 408 { 409 arena_t *arena; 410 411 arena = arena_get(arena_ind, false); 412 /* 413 * The arena we're allocating on behalf of must have been initialized 414 * already. 415 */ 416 assert(arena != NULL); 417 return (arena); 418 } 419 420 static void * 421 chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero, 422 bool *commit, unsigned arena_ind) 423 { 424 void *ret; 425 arena_t *arena; 426 427 arena = chunk_arena_get(arena_ind); 428 ret = chunk_alloc_core(arena, new_addr, size, alignment, zero, commit, 429 arena->dss_prec); 430 if (ret == NULL) 431 return (NULL); 432 if (config_valgrind) 433 JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, size); 434 435 return (ret); 436 } 437 438 static void * 439 chunk_alloc_retained(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr, 440 size_t size, size_t alignment, bool *zero, bool *commit) 441 { 442 443 assert(size != 0); 444 assert((size & chunksize_mask) == 0); 445 assert(alignment != 0); 446 assert((alignment & chunksize_mask) == 0); 447 448 return (chunk_recycle(arena, chunk_hooks, &arena->chunks_szad_retained, 449 &arena->chunks_ad_retained, false, new_addr, size, alignment, zero, 450 commit, true)); 451 } 452 453 void * 454 chunk_alloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *new_addr, 455 size_t size, size_t alignment, bool *zero, bool *commit) 456 { 457 void *ret; 458 459 chunk_hooks_assure_initialized(arena, chunk_hooks); 460 461 ret = chunk_alloc_retained(arena, chunk_hooks, new_addr, size, 462 alignment, zero, commit); 463 if (ret == NULL) { 464 ret = chunk_hooks->alloc(new_addr, size, alignment, zero, 465 commit, arena->ind); 466 if (ret == NULL) 467 return (NULL); 468 } 469 470 if (config_valgrind && chunk_hooks->alloc != chunk_alloc_default) 471 JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, chunksize); 472 return (ret); 473 } 474 475 static void 476 chunk_record(arena_t *arena, chunk_hooks_t *chunk_hooks, 477 extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, bool cache, 478 void *chunk, size_t size, bool zeroed, bool committed) 479 { 480 bool unzeroed; 481 extent_node_t *node, *prev; 482 extent_node_t key; 483 484 assert(!cache || !zeroed); 485 unzeroed = cache || !zeroed; 486 JEMALLOC_VALGRIND_MAKE_MEM_NOACCESS(chunk, size); 487 488 malloc_mutex_lock(&arena->chunks_mtx); 489 chunk_hooks_assure_initialized_locked(arena, chunk_hooks); 490 extent_node_init(&key, arena, (void *)((uintptr_t)chunk + size), 0, 491 false, false); 492 node = extent_tree_ad_nsearch(chunks_ad, &key); 493 /* Try to coalesce forward. */ 494 if (node != NULL && extent_node_addr_get(node) == 495 extent_node_addr_get(&key) && extent_node_committed_get(node) == 496 committed && !chunk_hooks->merge(chunk, size, 497 extent_node_addr_get(node), extent_node_size_get(node), false, 498 arena->ind)) { 499 /* 500 * Coalesce chunk with the following address range. This does 501 * not change the position within chunks_ad, so only 502 * remove/insert from/into chunks_szad. 503 */ 504 extent_tree_szad_remove(chunks_szad, node); 505 arena_chunk_cache_maybe_remove(arena, node, cache); 506 extent_node_addr_set(node, chunk); 507 extent_node_size_set(node, size + extent_node_size_get(node)); 508 extent_node_zeroed_set(node, extent_node_zeroed_get(node) && 509 !unzeroed); 510 extent_tree_szad_insert(chunks_szad, node); 511 arena_chunk_cache_maybe_insert(arena, node, cache); 512 } else { 513 /* Coalescing forward failed, so insert a new node. */ 514 node = arena_node_alloc(arena); 515 if (node == NULL) { 516 /* 517 * Node allocation failed, which is an exceedingly 518 * unlikely failure. Leak chunk after making sure its 519 * pages have already been purged, so that this is only 520 * a virtual memory leak. 521 */ 522 if (cache) { 523 chunk_purge_wrapper(arena, chunk_hooks, chunk, 524 size, 0, size); 525 } 526 goto label_return; 527 } 528 extent_node_init(node, arena, chunk, size, !unzeroed, 529 committed); 530 extent_tree_ad_insert(chunks_ad, node); 531 extent_tree_szad_insert(chunks_szad, node); 532 arena_chunk_cache_maybe_insert(arena, node, cache); 533 } 534 535 /* Try to coalesce backward. */ 536 prev = extent_tree_ad_prev(chunks_ad, node); 537 if (prev != NULL && (void *)((uintptr_t)extent_node_addr_get(prev) + 538 extent_node_size_get(prev)) == chunk && 539 extent_node_committed_get(prev) == committed && 540 !chunk_hooks->merge(extent_node_addr_get(prev), 541 extent_node_size_get(prev), chunk, size, false, arena->ind)) { 542 /* 543 * Coalesce chunk with the previous address range. This does 544 * not change the position within chunks_ad, so only 545 * remove/insert node from/into chunks_szad. 546 */ 547 extent_tree_szad_remove(chunks_szad, prev); 548 extent_tree_ad_remove(chunks_ad, prev); 549 arena_chunk_cache_maybe_remove(arena, prev, cache); 550 extent_tree_szad_remove(chunks_szad, node); 551 arena_chunk_cache_maybe_remove(arena, node, cache); 552 extent_node_addr_set(node, extent_node_addr_get(prev)); 553 extent_node_size_set(node, extent_node_size_get(prev) + 554 extent_node_size_get(node)); 555 extent_node_zeroed_set(node, extent_node_zeroed_get(prev) && 556 extent_node_zeroed_get(node)); 557 extent_tree_szad_insert(chunks_szad, node); 558 arena_chunk_cache_maybe_insert(arena, node, cache); 559 560 arena_node_dalloc(arena, prev); 561 } 562 563 label_return: 564 malloc_mutex_unlock(&arena->chunks_mtx); 565 } 566 567 void 568 chunk_dalloc_cache(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk, 569 size_t size, bool committed) 570 { 571 572 assert(chunk != NULL); 573 assert(CHUNK_ADDR2BASE(chunk) == chunk); 574 assert(size != 0); 575 assert((size & chunksize_mask) == 0); 576 577 chunk_record(arena, chunk_hooks, &arena->chunks_szad_cached, 578 &arena->chunks_ad_cached, true, chunk, size, false, committed); 579 arena_maybe_purge(arena); 580 } 581 582 static bool 583 chunk_dalloc_default(void *chunk, size_t size, bool committed, 584 unsigned arena_ind) 585 { 586 587 if (!have_dss || !chunk_in_dss(chunk)) 588 return (chunk_dalloc_mmap(chunk, size)); 589 return (true); 590 } 591 592 void 593 chunk_dalloc_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk, 594 size_t size, bool zeroed, bool committed) 595 { 596 597 assert(chunk != NULL); 598 assert(CHUNK_ADDR2BASE(chunk) == chunk); 599 assert(size != 0); 600 assert((size & chunksize_mask) == 0); 601 602 chunk_hooks_assure_initialized(arena, chunk_hooks); 603 /* Try to deallocate. */ 604 if (!chunk_hooks->dalloc(chunk, size, committed, arena->ind)) 605 return; 606 /* Try to decommit; purge if that fails. */ 607 if (committed) { 608 committed = chunk_hooks->decommit(chunk, size, 0, size, 609 arena->ind); 610 } 611 zeroed = !committed || !chunk_hooks->purge(chunk, size, 0, size, 612 arena->ind); 613 chunk_record(arena, chunk_hooks, &arena->chunks_szad_retained, 614 &arena->chunks_ad_retained, false, chunk, size, zeroed, committed); 615 } 616 617 static bool 618 chunk_commit_default(void *chunk, size_t size, size_t offset, size_t length, 619 unsigned arena_ind) 620 { 621 622 return (pages_commit((void *)((uintptr_t)chunk + (uintptr_t)offset), 623 length)); 624 } 625 626 static bool 627 chunk_decommit_default(void *chunk, size_t size, size_t offset, size_t length, 628 unsigned arena_ind) 629 { 630 631 return (pages_decommit((void *)((uintptr_t)chunk + (uintptr_t)offset), 632 length)); 633 } 634 635 static bool 636 chunk_purge_default(void *chunk, size_t size, size_t offset, size_t length, 637 unsigned arena_ind) 638 { 639 640 assert(chunk != NULL); 641 assert(CHUNK_ADDR2BASE(chunk) == chunk); 642 assert((offset & PAGE_MASK) == 0); 643 assert(length != 0); 644 assert((length & PAGE_MASK) == 0); 645 646 return (pages_purge((void *)((uintptr_t)chunk + (uintptr_t)offset), 647 length)); 648 } 649 650 bool 651 chunk_purge_wrapper(arena_t *arena, chunk_hooks_t *chunk_hooks, void *chunk, 652 size_t size, size_t offset, size_t length) 653 { 654 655 chunk_hooks_assure_initialized(arena, chunk_hooks); 656 return (chunk_hooks->purge(chunk, size, offset, length, arena->ind)); 657 } 658 659 static bool 660 chunk_split_default(void *chunk, size_t size, size_t size_a, size_t size_b, 661 bool committed, unsigned arena_ind) 662 { 663 664 if (!maps_coalesce) 665 return (true); 666 return (false); 667 } 668 669 static bool 670 chunk_merge_default(void *chunk_a, size_t size_a, void *chunk_b, size_t size_b, 671 bool committed, unsigned arena_ind) 672 { 673 674 if (!maps_coalesce) 675 return (true); 676 if (have_dss && chunk_in_dss(chunk_a) != chunk_in_dss(chunk_b)) 677 return (true); 678 679 return (false); 680 } 681 682 static rtree_node_elm_t * 683 chunks_rtree_node_alloc(size_t nelms) 684 { 685 686 return ((rtree_node_elm_t *)base_alloc(nelms * 687 sizeof(rtree_node_elm_t))); 688 } 689 690 bool 691 chunk_boot(void) 692 { 693 #ifdef _WIN32 694 SYSTEM_INFO info; 695 GetSystemInfo(&info); 696 697 /* 698 * Verify actual page size is equal to or an integral multiple of 699 * configured page size. 700 */ 701 if (info.dwPageSize & ((1U << LG_PAGE) - 1)) 702 return (true); 703 704 /* 705 * Configure chunksize (if not set) to match granularity (usually 64K), 706 * so pages_map will always take fast path. 707 */ 708 if (!opt_lg_chunk) { 709 opt_lg_chunk = ffs_u((unsigned)info.dwAllocationGranularity) 710 - 1; 711 } 712 #else 713 if (!opt_lg_chunk) 714 opt_lg_chunk = LG_CHUNK_DEFAULT; 715 #endif 716 717 /* Set variables according to the value of opt_lg_chunk. */ 718 chunksize = (ZU(1) << opt_lg_chunk); 719 assert(chunksize >= PAGE); 720 chunksize_mask = chunksize - 1; 721 chunk_npages = (chunksize >> LG_PAGE); 722 723 if (have_dss && chunk_dss_boot()) 724 return (true); 725 if (rtree_new(&chunks_rtree, (unsigned)((ZU(1) << (LG_SIZEOF_PTR+3)) - 726 opt_lg_chunk), chunks_rtree_node_alloc, NULL)) 727 return (true); 728 729 return (false); 730 } 731 732 void 733 chunk_prefork(void) 734 { 735 736 chunk_dss_prefork(); 737 } 738 739 void 740 chunk_postfork_parent(void) 741 { 742 743 chunk_dss_postfork_parent(); 744 } 745 746 void 747 chunk_postfork_child(void) 748 { 749 750 chunk_dss_postfork_child(); 751 } 752