| 1394 | } |
| 1395 | |
| 1396 | static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t needed_slices, size_t block_size, bool* reclaimed, mi_segments_tld_t* tld) |
| 1397 | { |
| 1398 | *reclaimed = false; |
| 1399 | mi_segment_t* segment; |
| 1400 | long max_tries = mi_option_get_clamp(mi_option_max_segment_reclaim, 8, 1024); // limit the work to bound allocation times |
| 1401 | while ((max_tries-- > 0) && ((segment = mi_abandoned_pop()) != NULL)) { |
| 1402 | segment->abandoned_visits++; |
| 1403 | // todo: an arena exclusive heap will potentially visit many abandoned unsuitable segments |
| 1404 | // and push them into the visited list and use many tries. Perhaps we can skip non-suitable ones in a better way? |
| 1405 | bool is_suitable = _mi_heap_memid_is_suitable(heap, segment->memid); |
| 1406 | bool has_page = mi_segment_check_free(segment,needed_slices,block_size,tld); // try to free up pages (due to concurrent frees) |
| 1407 | if (segment->used == 0) { |
| 1408 | // free the segment (by forced reclaim) to make it available to other threads. |
| 1409 | // note1: we prefer to free a segment as that might lead to reclaiming another |
| 1410 | // segment that is still partially used. |
| 1411 | // note2: we could in principle optimize this by skipping reclaim and directly |
| 1412 | // freeing but that would violate some invariants temporarily) |
| 1413 | mi_segment_reclaim(segment, heap, 0, NULL, tld); |
| 1414 | } |
| 1415 | else if (has_page && is_suitable) { |
| 1416 | // found a large enough free span, or a page of the right block_size with free space |
| 1417 | // we return the result of reclaim (which is usually `segment`) as it might free |
| 1418 | // the segment due to concurrent frees (in which case `NULL` is returned). |
| 1419 | return mi_segment_reclaim(segment, heap, block_size, reclaimed, tld); |
| 1420 | } |
| 1421 | else if (segment->abandoned_visits > 3 && is_suitable) { |
| 1422 | // always reclaim on 3rd visit to limit the abandoned queue length. |
| 1423 | mi_segment_reclaim(segment, heap, 0, NULL, tld); |
| 1424 | } |
| 1425 | else { |
| 1426 | // otherwise, push on the visited list so it gets not looked at too quickly again |
| 1427 | mi_segment_delayed_decommit(segment, true /* force? */, tld->stats); // forced decommit if needed as we may not visit soon again |
| 1428 | mi_abandoned_visited_push(segment); |
| 1429 | } |
| 1430 | } |
| 1431 | return NULL; |
| 1432 | } |
| 1433 | |
| 1434 | |
| 1435 | void _mi_abandoned_collect(mi_heap_t* heap, bool force, mi_segments_tld_t* tld) |
no test coverage detected