Skip to content

Commit b3520db

Browse files
authored
Update mimalloc to v2.0.9 (#4211)
I recently [upstreamed the mimalloc fixes we developed in Git for Windows](microsoft/mimalloc#654), most notably the [fix for running in Windows Nano Server](#4074). In the meantime, that PR was accepted and a new version was created from it (that also includes a couple of other fixes). Let's integrate the latest version.
2 parents e7d4c50 + 096b848 commit b3520db

20 files changed

+1545
-665
lines changed

compat/mimalloc/alloc-aligned.c

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

compat/mimalloc/alloc.c

Lines changed: 291 additions & 194 deletions
Large diffs are not rendered by default.

compat/mimalloc/arena.c

Lines changed: 154 additions & 64 deletions
Large diffs are not rendered by default.

compat/mimalloc/bitmap.c

Lines changed: 20 additions & 1 deletion
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.
@@ -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;

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: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect)
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
}
@@ -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,7 +372,7 @@ 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);
375+
_mi_heap_delayed_free_partial(from);
354376

355377
// 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.
@@ -369,7 +391,7 @@ static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) {
369391
// note: be careful here as the `heap` field in all those pages no longer point to `from`,
370392
// 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
@@ -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);

compat/mimalloc/init.c

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ const mi_page_t _mi_page_empty = {
1919
false, // is_zero
2020
0, // retire_expire
2121
NULL, // free
22-
#if MI_ENCODE_FREELIST
23-
{ 0, 0 },
24-
#endif
2522
0, // used
2623
0, // xblock_size
2724
NULL, // local_free
25+
#if MI_ENCODE_FREELIST
26+
{ 0, 0 },
27+
#endif
2828
MI_ATOMIC_VAR_INIT(0), // xthread_free
2929
MI_ATOMIC_VAR_INIT(0), // xheap
3030
NULL, NULL
@@ -109,8 +109,9 @@ mi_decl_cache_align const mi_heap_t _mi_heap_empty = {
109109
MI_ATOMIC_VAR_INIT(NULL),
110110
0, // tid
111111
0, // cookie
112+
0, // arena id
112113
{ 0, 0 }, // keys
113-
{ {0}, {0}, 0 },
114+
{ {0}, {0}, 0, true }, // random
114115
0, // page count
115116
MI_BIN_FULL, 0, // page retired min/max
116117
NULL, // next
@@ -149,8 +150,9 @@ mi_heap_t _mi_heap_main = {
149150
MI_ATOMIC_VAR_INIT(NULL),
150151
0, // thread id
151152
0, // initial cookie
153+
0, // arena id
152154
{ 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!)
153-
{ {0x846ca68b}, {0}, 0 }, // random
155+
{ {0x846ca68b}, {0}, 0, true }, // random
154156
0, // page count
155157
MI_BIN_FULL, 0, // page retired min/max
156158
NULL, // next heap
@@ -165,8 +167,13 @@ mi_stats_t _mi_stats_main = { MI_STATS_NULL };
165167
static void mi_heap_main_init(void) {
166168
if (_mi_heap_main.cookie == 0) {
167169
_mi_heap_main.thread_id = _mi_thread_id();
168-
_mi_heap_main.cookie = _mi_os_random_weak((uintptr_t)&mi_heap_main_init);
169-
_mi_random_init(&_mi_heap_main.random);
170+
_mi_heap_main.cookie = 1;
171+
#if defined(_WIN32) && !defined(MI_SHARED_LIB)
172+
_mi_random_init_weak(&_mi_heap_main.random); // prevent allocation failure during bcrypt dll initialization with static linking
173+
#else
174+
_mi_random_init(&_mi_heap_main.random);
175+
#endif
176+
_mi_heap_main.cookie = _mi_heap_random_next(&_mi_heap_main);
170177
_mi_heap_main.keys[0] = _mi_heap_random_next(&_mi_heap_main);
171178
_mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main);
172179
}
@@ -372,7 +379,11 @@ static void _mi_thread_done(mi_heap_t* default_heap);
372379
#endif
373380
static DWORD mi_fls_key = (DWORD)(-1);
374381
static void NTAPI mi_fls_done(PVOID value) {
375-
if (value!=NULL) _mi_thread_done((mi_heap_t*)value);
382+
mi_heap_t* heap = (mi_heap_t*)value;
383+
if (heap != NULL) {
384+
_mi_thread_done(heap);
385+
FlsSetValue(mi_fls_key, NULL); // prevent recursion as _mi_thread_done may set it back to the main heap, issue #672
386+
}
376387
}
377388
#elif defined(MI_USE_PTHREADS)
378389
// use pthread local storage keys to detect thread ending
@@ -475,7 +486,7 @@ void _mi_heap_set_default_direct(mi_heap_t* heap) {
475486
// --------------------------------------------------------
476487
// Run functions on process init/done, and thread init/done
477488
// --------------------------------------------------------
478-
static void mi_process_done(void);
489+
static void mi_cdecl mi_process_done(void);
479490

480491
static bool os_preloading = true; // true until this module is initialized
481492
static bool mi_redirected = false; // true if malloc redirects to mi_malloc
@@ -490,7 +501,7 @@ mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept {
490501
}
491502

492503
// Communicate with the redirection module on Windows
493-
#if defined(_WIN32) && defined(MI_SHARED_LIB)
504+
#if defined(_WIN32) && defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT)
494505
#ifdef __cplusplus
495506
extern "C" {
496507
#endif
@@ -506,8 +517,8 @@ mi_decl_export void _mi_redirect_entry(DWORD reason) {
506517
mi_thread_done();
507518
}
508519
}
509-
__declspec(dllimport) bool mi_allocator_init(const char** message);
510-
__declspec(dllimport) void mi_allocator_done(void);
520+
__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message);
521+
__declspec(dllimport) void mi_cdecl mi_allocator_done(void);
511522
#ifdef __cplusplus
512523
}
513524
#endif
@@ -529,12 +540,13 @@ static void mi_process_load(void) {
529540
MI_UNUSED(dummy);
530541
#endif
531542
os_preloading = false;
543+
mi_assert_internal(_mi_is_main_thread());
532544
#if !(defined(_WIN32) && defined(MI_SHARED_LIB)) // use Dll process detach (see below) instead of atexit (issue #521)
533545
atexit(&mi_process_done);
534546
#endif
535547
_mi_options_init();
548+
mi_process_setup_auto_thread_done();
536549
mi_process_init();
537-
//mi_stats_reset();-
538550
if (mi_redirected) _mi_verbose_message("malloc is redirected.\n");
539551

540552
// show message from the redirector (if present)
@@ -543,6 +555,9 @@ static void mi_process_load(void) {
543555
if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) {
544556
_mi_fputs(NULL,NULL,NULL,msg);
545557
}
558+
559+
// reseed random
560+
_mi_random_reinit_if_weak(&_mi_heap_main.random);
546561
}
547562

548563
#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))
@@ -569,14 +584,14 @@ void mi_process_init(void) mi_attr_noexcept {
569584
_mi_process_is_initialized = true;
570585
mi_process_setup_auto_thread_done();
571586

572-
573587
mi_detect_cpu_features();
574588
_mi_os_init();
575589
mi_heap_main_init();
576590
#if (MI_DEBUG)
577591
_mi_verbose_message("debug level : %d\n", MI_DEBUG);
578592
#endif
579593
_mi_verbose_message("secure level: %d\n", MI_SECURE);
594+
_mi_verbose_message("mem tracking: %s\n", MI_TRACK_TOOL);
580595
mi_thread_init();
581596

582597
#if defined(_WIN32) && !defined(MI_SHARED_LIB)
@@ -606,7 +621,7 @@ void mi_process_init(void) mi_attr_noexcept {
606621
}
607622

608623
// Called when the process is done (through `at_exit`)
609-
static void mi_process_done(void) {
624+
static void mi_cdecl mi_process_done(void) {
610625
// only shutdown if we were initialized
611626
if (!_mi_process_is_initialized) return;
612627
// ensure we are called once
@@ -627,6 +642,14 @@ static void mi_process_done(void) {
627642
#endif
628643
#endif
629644

645+
// Forcefully release all retained memory; this can be dangerous in general if overriding regular malloc/free
646+
// since after process_done there might still be other code running that calls `free` (like at_exit routines,
647+
// or C-runtime termination code.
648+
if (mi_option_is_enabled(mi_option_destroy_on_exit)) {
649+
_mi_heap_destroy_all(); // forcefully release all memory held by all heaps (of this thread only!)
650+
_mi_segment_cache_free_all(&_mi_heap_main_get()->tld->os); // release all cached segments
651+
}
652+
630653
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
631654
mi_stats_print(NULL);
632655
}

0 commit comments

Comments
 (0)