| 134 | #define MI_MAX_PURGE_PER_PUSH (4) |
| 135 | |
| 136 | static mi_decl_noinline void mi_segment_cache_purge(bool visit_all, bool force, mi_os_tld_t* tld) |
| 137 | { |
| 138 | MI_UNUSED(tld); |
| 139 | if (!mi_option_is_enabled(mi_option_allow_decommit)) return; |
| 140 | mi_msecs_t now = _mi_clock_now(); |
| 141 | size_t purged = 0; |
| 142 | const size_t max_visits = (visit_all ? MI_CACHE_MAX /* visit all */ : MI_CACHE_FIELDS /* probe at most N (=16) slots */); |
| 143 | size_t idx = (visit_all ? 0 : _mi_random_shuffle((uintptr_t)now) % MI_CACHE_MAX /* random start */ ); |
| 144 | for (size_t visited = 0; visited < max_visits; visited++,idx++) { // visit N slots |
| 145 | if (idx >= MI_CACHE_MAX) idx = 0; // wrap |
| 146 | mi_cache_slot_t* slot = &cache[idx]; |
| 147 | mi_msecs_t expire = mi_atomic_loadi64_relaxed(&slot->expire); |
| 148 | if (expire != 0 && (force || now >= expire)) { // racy read |
| 149 | // seems expired, first claim it from available |
| 150 | purged++; |
| 151 | mi_bitmap_index_t bitidx = mi_bitmap_index_create_from_bit(idx); |
| 152 | if (_mi_bitmap_claim(cache_available, MI_CACHE_FIELDS, 1, bitidx, NULL)) { |
| 153 | // was available, we claimed it |
| 154 | expire = mi_atomic_loadi64_acquire(&slot->expire); |
| 155 | if (expire != 0 && (force || now >= expire)) { // safe read |
| 156 | // still expired, decommit it |
| 157 | mi_atomic_storei64_relaxed(&slot->expire,(mi_msecs_t)0); |
| 158 | mi_assert_internal(!mi_commit_mask_is_empty(&slot->commit_mask) && _mi_bitmap_is_claimed(cache_available_large, MI_CACHE_FIELDS, 1, bitidx)); |
| 159 | _mi_abandoned_await_readers(); // wait until safe to decommit |
| 160 | // decommit committed parts |
| 161 | // TODO: instead of decommit, we could also free to the OS? |
| 162 | mi_commit_mask_decommit(&slot->commit_mask, slot->p, MI_SEGMENT_SIZE, tld->stats); |
| 163 | mi_commit_mask_create_empty(&slot->decommit_mask); |
| 164 | } |
| 165 | _mi_bitmap_unclaim(cache_available, MI_CACHE_FIELDS, 1, bitidx); // make it available again for a pop |
| 166 | } |
| 167 | if (!visit_all && purged > MI_MAX_PURGE_PER_PUSH) break; // bound to no more than N purge tries per push |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | void _mi_segment_cache_collect(bool force, mi_os_tld_t* tld) { |
| 173 | if (force) { |
no test coverage detected