Skip to content

Commit f9b6c4d

Browse files
committed
TO-UNDO: update to mimalloc's dev-slice tip commit
To test the changes contributed via microsoft/mimalloc#654 and further fixed up by Daan in the `dev` branch (which corresponds to the v1.x track, and which was merged into the `dev-slice` branch that corresponds to the v2.x track that we follow). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 7cfa93c commit f9b6c4d

File tree

19 files changed

+1546
-772
lines changed

19 files changed

+1546
-772
lines changed

compat/mimalloc/alloc-aligned.c

Lines changed: 90 additions & 45 deletions
Large diffs are not rendered by default.

compat/mimalloc/alloc.c

Lines changed: 283 additions & 187 deletions
Large diffs are not rendered by default.

compat/mimalloc/arena.c

Lines changed: 160 additions & 70 deletions
Large diffs are not rendered by default.

compat/mimalloc/bitmap.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@ bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fiel
108108
return false;
109109
}
110110

111+
// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled
112+
bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields,
113+
const size_t start_field_idx, const size_t count,
114+
mi_bitmap_pred_fun_t pred_fun, void* pred_arg,
115+
mi_bitmap_index_t* bitmap_idx) {
116+
size_t idx = start_field_idx;
117+
for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) {
118+
if (idx >= bitmap_fields) idx = 0; // wrap
119+
if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
120+
if (pred_fun == NULL || pred_fun(*bitmap_idx, pred_arg)) {
121+
return true;
122+
}
123+
// predicate returned false, unclaim and look further
124+
_mi_bitmap_unclaim(bitmap, bitmap_fields, count, *bitmap_idx);
125+
}
126+
}
127+
return false;
128+
}
129+
111130
/*
112131
// Find `count` bits of 0 and set them to 1 atomically; returns `true` on success.
113132
// For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never span fields.
@@ -169,15 +188,15 @@ bool _mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t
169188
// between the fields. This is used in arena allocation
170189
//--------------------------------------------------------------------------
171190

172-
// Try to atomically claim a sequence of `count` bits starting from the field
191+
// Try to atomically claim a sequence of `count` bits starting from the field
173192
// at `idx` in `bitmap` and crossing into subsequent fields. Returns `true` on success.
174193
static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t idx, const size_t count, const size_t retries, mi_bitmap_index_t* bitmap_idx)
175194
{
176195
mi_assert_internal(bitmap_idx != NULL);
177-
196+
178197
// check initial trailing zeros
179198
mi_bitmap_field_t* field = &bitmap[idx];
180-
size_t map = mi_atomic_load_relaxed(field);
199+
size_t map = mi_atomic_load_relaxed(field);
181200
const size_t initial = mi_clz(map); // count of initial zeros starting at idx
182201
mi_assert_internal(initial <= MI_BITMAP_FIELD_BITS);
183202
if (initial == 0) return false;
@@ -212,14 +231,14 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit
212231
newmap = map | initial_mask;
213232
if ((map & initial_mask) != 0) { goto rollback; };
214233
} while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
215-
234+
216235
// intermediate fields
217236
while (++field < final_field) {
218237
newmap = MI_BITMAP_FIELD_FULL;
219238
map = 0;
220239
if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { goto rollback; }
221240
}
222-
241+
223242
// final field
224243
mi_assert_internal(field == final_field);
225244
map = mi_atomic_load_relaxed(field);
@@ -232,7 +251,7 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit
232251
*bitmap_idx = mi_bitmap_index_create(idx, MI_BITMAP_FIELD_BITS - initial);
233252
return true;
234253

235-
rollback:
254+
rollback:
236255
// roll back intermediate fields
237256
while (--field > initial_field) {
238257
newmap = 0;
@@ -246,7 +265,7 @@ static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bit
246265
mi_assert_internal((map & initial_mask) == initial_mask);
247266
newmap = map & ~initial_mask;
248267
} while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
249-
}
268+
}
250269
// retry? (we make a recursive call instead of goto to be able to use const declarations)
251270
if (retries < 4) {
252271
return mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, retries+1, bitmap_idx);
@@ -283,7 +302,7 @@ bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitm
283302
static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_fields, size_t count, size_t* pre_mask, size_t* mid_mask, size_t* post_mask) {
284303
MI_UNUSED_RELEASE(bitmap_fields);
285304
const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
286-
if (mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS)) {
305+
if mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS) {
287306
*pre_mask = mi_bitmap_mask_(count, bitidx);
288307
*mid_mask = 0;
289308
*post_mask = 0;
@@ -311,7 +330,7 @@ bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t
311330
size_t pre_mask;
312331
size_t mid_mask;
313332
size_t post_mask;
314-
size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
333+
size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
315334
bool all_one = true;
316335
mi_bitmap_field_t* field = &bitmap[idx];
317336
size_t prev = mi_atomic_and_acq_rel(field++, ~pre_mask);
@@ -324,7 +343,7 @@ bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t
324343
prev = mi_atomic_and_acq_rel(field, ~post_mask);
325344
if ((prev & post_mask) != post_mask) all_one = false;
326345
}
327-
return all_one;
346+
return all_one;
328347
}
329348

330349
// Set `count` bits at `bitmap_idx` to 1 atomically
@@ -356,7 +375,7 @@ bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t co
356375
}
357376

358377

359-
// Returns `true` if all `count` bits were 1.
378+
// Returns `true` if all `count` bits were 1.
360379
// `any_ones` is `true` if there was at least one bit set to one.
361380
static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_ones) {
362381
size_t idx = mi_bitmap_index_field(bitmap_idx);
@@ -379,7 +398,7 @@ static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_field
379398
prev = mi_atomic_load_relaxed(field);
380399
if ((prev & post_mask) != post_mask) all_ones = false;
381400
if ((prev & post_mask) != 0) any_ones = true;
382-
}
401+
}
383402
if (pany_ones != NULL) *pany_ones = any_ones;
384403
return all_ones;
385404
}

compat/mimalloc/bitmap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_
7272
// For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields.
7373
bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx);
7474

75+
// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled
76+
typedef bool (mi_cdecl *mi_bitmap_pred_fun_t)(mi_bitmap_index_t bitmap_idx, void* pred_arg);
77+
bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_pred_fun_t pred_fun, void* pred_arg, mi_bitmap_index_t* bitmap_idx);
78+
7579
// Set `count` bits at `bitmap_idx` to 0 atomically
7680
// Returns `true` if all `count` bits were 1 previously.
7781
bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);

compat/mimalloc/heap.c

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ static bool mi_heap_page_collect(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t
9292
mi_collect_t collect = *((mi_collect_t*)arg_collect);
9393
_mi_page_free_collect(page, collect >= MI_FORCE);
9494
if (mi_page_all_free(page)) {
95-
// no more used blocks, free the page.
95+
// no more used blocks, free the page.
9696
// note: this will free retired pages as well.
9797
_mi_page_free(page, pq, collect >= MI_FORCE);
9898
}
@@ -133,15 +133,15 @@ static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect)
133133
// if all memory is freed by now, all segments should be freed.
134134
_mi_abandoned_reclaim_all(heap, &heap->tld->segments);
135135
}
136-
136+
137137
// if abandoning, mark all pages to no longer add to delayed_free
138138
if (collect == MI_ABANDON) {
139139
mi_heap_visit_pages(heap, &mi_heap_page_never_delayed_free, NULL, NULL);
140140
}
141141

142-
// free thread delayed blocks.
142+
// free all current thread delayed blocks.
143143
// (if abandoning, after this there are no more thread-delayed references into the pages.)
144-
_mi_heap_delayed_free(heap);
144+
_mi_heap_delayed_free_all(heap);
145145

146146
// collect retired pages
147147
_mi_heap_collect_retired(heap, force);
@@ -200,13 +200,14 @@ mi_heap_t* mi_heap_get_backing(void) {
200200
return bheap;
201201
}
202202

203-
mi_heap_t* mi_heap_new(void) {
203+
mi_decl_nodiscard mi_heap_t* mi_heap_new_in_arena( mi_arena_id_t arena_id ) {
204204
mi_heap_t* bheap = mi_heap_get_backing();
205205
mi_heap_t* heap = mi_heap_malloc_tp(bheap, mi_heap_t); // todo: OS allocate in secure mode?
206206
if (heap==NULL) return NULL;
207207
_mi_memcpy_aligned(heap, &_mi_heap_empty, sizeof(mi_heap_t));
208208
heap->tld = bheap->tld;
209209
heap->thread_id = _mi_thread_id();
210+
heap->arena_id = arena_id;
210211
_mi_random_split(&bheap->random, &heap->random);
211212
heap->cookie = _mi_heap_random_next(heap) | 1;
212213
heap->keys[0] = _mi_heap_random_next(heap);
@@ -218,6 +219,14 @@ mi_heap_t* mi_heap_new(void) {
218219
return heap;
219220
}
220221

222+
mi_decl_nodiscard mi_heap_t* mi_heap_new(void) {
223+
return mi_heap_new_in_arena(_mi_arena_id_none());
224+
}
225+
226+
bool _mi_heap_memid_is_suitable(mi_heap_t* heap, size_t memid) {
227+
return _mi_arena_memid_is_suitable(memid, heap->arena_id);
228+
}
229+
221230
uintptr_t _mi_heap_random_next(mi_heap_t* heap) {
222231
return _mi_random_next(&heap->random);
223232
}
@@ -251,7 +260,7 @@ static void mi_heap_free(mi_heap_t* heap) {
251260
// remove ourselves from the thread local heaps list
252261
// linear search but we expect the number of heaps to be relatively small
253262
mi_heap_t* prev = NULL;
254-
mi_heap_t* curr = heap->tld->heaps;
263+
mi_heap_t* curr = heap->tld->heaps;
255264
while (curr != heap && curr != NULL) {
256265
prev = curr;
257266
curr = curr->next;
@@ -338,7 +347,20 @@ void mi_heap_destroy(mi_heap_t* heap) {
338347
}
339348
}
340349

341-
350+
void _mi_heap_destroy_all(void) {
351+
mi_heap_t* bheap = mi_heap_get_backing();
352+
mi_heap_t* curr = bheap->tld->heaps;
353+
while (curr != NULL) {
354+
mi_heap_t* next = curr->next;
355+
if (curr->no_reclaim) {
356+
mi_heap_destroy(curr);
357+
}
358+
else {
359+
_mi_heap_destroy_pages(curr);
360+
}
361+
curr = next;
362+
}
363+
}
342364

343365
/* -----------------------------------------------------------
344366
Safe Heap delete
@@ -350,9 +372,9 @@ static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) {
350372
if (from==NULL || from->page_count == 0) return;
351373

352374
// reduce the size of the delayed frees
353-
_mi_heap_delayed_free(from);
354-
355-
// transfer all pages by appending the queues; this will set a new heap field
375+
_mi_heap_delayed_free_partial(from);
376+
377+
// transfer all pages by appending the queues; this will set a new heap field
356378
// so threads may do delayed frees in either heap for a while.
357379
// note: appending waits for each page to not be in the `MI_DELAYED_FREEING` state
358380
// so after this only the new heap will get delayed frees
@@ -365,17 +387,17 @@ static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) {
365387
}
366388
mi_assert_internal(from->page_count == 0);
367389

368-
// and do outstanding delayed frees in the `from` heap
390+
// and do outstanding delayed frees in the `from` heap
369391
// note: be careful here as the `heap` field in all those pages no longer point to `from`,
370-
// turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a
392+
// turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a
371393
// the regular `_mi_free_delayed_block` which is safe.
372-
_mi_heap_delayed_free(from);
394+
_mi_heap_delayed_free_all(from);
373395
#if !defined(_MSC_VER) || (_MSC_VER > 1900) // somehow the following line gives an error in VS2015, issue #353
374396
mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_block_t,&from->thread_delayed_free) == NULL);
375397
#endif
376398

377399
// and reset the `from` heap
378-
mi_heap_reset_pages(from);
400+
mi_heap_reset_pages(from);
379401
}
380402

381403
// Safe delete a heap without freeing any still allocated blocks in that heap.
@@ -421,7 +443,7 @@ static mi_heap_t* mi_heap_of_block(const void* p) {
421443
mi_segment_t* segment = _mi_ptr_segment(p);
422444
bool valid = (_mi_ptr_cookie(segment) == segment->cookie);
423445
mi_assert_internal(valid);
424-
if (mi_unlikely(!valid)) return NULL;
446+
if mi_unlikely(!valid) return NULL;
425447
return mi_page_heap(_mi_segment_page_of(segment,p));
426448
}
427449

@@ -543,7 +565,7 @@ static bool mi_heap_visit_areas_page(mi_heap_t* heap, mi_page_queue_t* pq, mi_pa
543565
xarea.area.reserved = page->reserved * bsize;
544566
xarea.area.committed = page->capacity * bsize;
545567
xarea.area.blocks = _mi_page_start(_mi_page_segment(page), page, NULL);
546-
xarea.area.used = page->used * bsize;
568+
xarea.area.used = page->used; // number of blocks in use (#553)
547569
xarea.area.block_size = ubsize;
548570
xarea.area.full_block_size = bsize;
549571
return fun(heap, &xarea, arg);

0 commit comments

Comments
 (0)