Skip to content

Commit 0ed4ebd

Browse files
dschoGit for Windows Build Agent
authored andcommitted
Upgrade mimalloc to v2.2.4 (#5718)
This fixes #5671
2 parents d0772a1 + e862310 commit 0ed4ebd

File tree

19 files changed

+421
-257
lines changed

19 files changed

+421
-257
lines changed

compat/mimalloc/arena.c

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ typedef struct mi_arena_s {
4444
mi_lock_t abandoned_visit_lock; // lock is only used when abandoned segments are being visited
4545
_Atomic(size_t) search_idx; // optimization to start the search for free blocks
4646
_Atomic(mi_msecs_t) purge_expire; // expiration time when blocks should be purged from `blocks_purge`.
47-
47+
4848
mi_bitmap_field_t* blocks_dirty; // are the blocks potentially non-zero?
4949
mi_bitmap_field_t* blocks_committed; // are the blocks committed? (can be NULL for memory that cannot be decommitted)
5050
mi_bitmap_field_t* blocks_purge; // blocks that can be (reset) decommitted. (can be NULL for memory that cannot be (reset) decommitted)
@@ -192,14 +192,9 @@ void* _mi_arena_meta_zalloc(size_t size, mi_memid_t* memid) {
192192
if (p != NULL) return p;
193193

194194
// or fall back to the OS
195-
p = _mi_os_alloc(size, memid);
195+
p = _mi_os_zalloc(size, memid);
196196
if (p == NULL) return NULL;
197197

198-
// zero the OS memory if needed
199-
if (!memid->initially_zero) {
200-
_mi_memzero_aligned(p, size);
201-
memid->initially_zero = true;
202-
}
203198
return p;
204199
}
205200

@@ -270,12 +265,12 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(mi_arena_t* arena, size_t ar
270265
else if (commit) {
271266
// commit requested, but the range may not be committed as a whole: ensure it is committed now
272267
memid->initially_committed = true;
268+
const size_t commit_size = mi_arena_block_size(needed_bcount);
273269
bool any_uncommitted;
274270
size_t already_committed = 0;
275271
_mi_bitmap_claim_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index, &any_uncommitted, &already_committed);
276272
if (any_uncommitted) {
277273
mi_assert_internal(already_committed < needed_bcount);
278-
const size_t commit_size = mi_arena_block_size(needed_bcount);
279274
const size_t stat_commit_size = commit_size - mi_arena_block_size(already_committed);
280275
bool commit_zero = false;
281276
if (!_mi_os_commit_ex(p, commit_size, &commit_zero, stat_commit_size)) {
@@ -285,6 +280,10 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(mi_arena_t* arena, size_t ar
285280
if (commit_zero) { memid->initially_zero = true; }
286281
}
287282
}
283+
else {
284+
// all are already committed: signal that we are reusing memory in case it was purged before
285+
_mi_os_reuse( p, commit_size );
286+
}
288287
}
289288
else {
290289
// no need to commit, but check if already fully committed
@@ -369,7 +368,7 @@ static mi_decl_noinline void* mi_arena_try_alloc(int numa_node, size_t size, siz
369368
static bool mi_arena_reserve(size_t req_size, bool allow_large, mi_arena_id_t *arena_id)
370369
{
371370
if (_mi_preloading()) return false; // use OS only while pre loading
372-
371+
373372
const size_t arena_count = mi_atomic_load_acquire(&mi_arena_count);
374373
if (arena_count > (MI_MAX_ARENAS - 4)) return false;
375374

@@ -411,7 +410,7 @@ void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset
411410

412411
// try to allocate in an arena if the alignment is small enough and the object is not too small (as for heap meta data)
413412
if (!mi_option_is_enabled(mi_option_disallow_arena_alloc)) { // is arena allocation allowed?
414-
if (size >= MI_ARENA_MIN_OBJ_SIZE && alignment <= MI_SEGMENT_ALIGN && align_offset == 0)
413+
if (size >= MI_ARENA_MIN_OBJ_SIZE && alignment <= MI_SEGMENT_ALIGN && align_offset == 0)
415414
{
416415
void* p = mi_arena_try_alloc(numa_node, size, alignment, commit, allow_large, req_arena_id, memid);
417416
if (p != NULL) return p;
@@ -491,7 +490,7 @@ static void mi_arena_purge(mi_arena_t* arena, size_t bitmap_idx, size_t blocks)
491490
// we need to ensure we do not try to reset (as that may be invalid for uncommitted memory).
492491
mi_assert_internal(already_committed < blocks);
493492
mi_assert_internal(mi_option_is_enabled(mi_option_purge_decommits));
494-
needs_recommit = _mi_os_purge_ex(p, size, false /* allow reset? */, mi_arena_block_size(already_committed));
493+
needs_recommit = _mi_os_purge_ex(p, size, false /* allow reset? */, mi_arena_block_size(already_committed));
495494
}
496495

497496
// clear the purged blocks
@@ -560,7 +559,7 @@ static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
560559
{
561560
// check pre-conditions
562561
if (arena->memid.is_pinned) return false;
563-
562+
564563
// expired yet?
565564
mi_msecs_t expire = mi_atomic_loadi64_relaxed(&arena->purge_expire);
566565
if (!force && (expire == 0 || expire > now)) return false;
@@ -615,7 +614,7 @@ static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
615614
return any_purged;
616615
}
617616

618-
static void mi_arenas_try_purge( bool force, bool visit_all )
617+
static void mi_arenas_try_purge( bool force, bool visit_all )
619618
{
620619
if (_mi_preloading() || mi_arena_purge_delay() <= 0) return; // nothing will be scheduled
621620

@@ -632,7 +631,7 @@ static void mi_arenas_try_purge( bool force, bool visit_all )
632631
mi_atomic_guard(&purge_guard)
633632
{
634633
// increase global expire: at most one purge per delay cycle
635-
mi_atomic_storei64_release(&mi_arenas_purge_expire, now + mi_arena_purge_delay());
634+
mi_atomic_storei64_release(&mi_arenas_purge_expire, now + mi_arena_purge_delay());
636635
size_t max_purge_count = (visit_all ? max_arena : 2);
637636
bool all_visited = true;
638637
for (size_t i = 0; i < max_arena; i++) {
@@ -951,7 +950,7 @@ void mi_debug_show_arenas(void) mi_attr_noexcept {
951950
for (size_t i = 0; i < max_arenas; i++) {
952951
mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]);
953952
if (arena == NULL) break;
954-
_mi_message("arena %zu: %zu blocks of size %zuMiB (in %zu fields) %s\n", i, arena->block_count, MI_ARENA_BLOCK_SIZE / MI_MiB, arena->field_count, (arena->memid.is_pinned ? ", pinned" : ""));
953+
_mi_message("arena %zu: %zu blocks of size %zuMiB (in %zu fields) %s\n", i, arena->block_count, (size_t)(MI_ARENA_BLOCK_SIZE / MI_MiB), arena->field_count, (arena->memid.is_pinned ? ", pinned" : ""));
955954
if (show_inuse) {
956955
inuse_total += mi_debug_show_bitmap(" ", "inuse blocks", arena->block_count, arena->blocks_inuse, arena->field_count);
957956
}
@@ -1011,17 +1010,17 @@ int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t
10111010
if (pages == 0) return 0;
10121011

10131012
// pages per numa node
1014-
size_t numa_count = (numa_nodes > 0 ? numa_nodes : _mi_os_numa_node_count());
1015-
if (numa_count <= 0) numa_count = 1;
1013+
int numa_count = (numa_nodes > 0 && numa_nodes <= INT_MAX ? (int)numa_nodes : _mi_os_numa_node_count());
1014+
if (numa_count == 0) numa_count = 1;
10161015
const size_t pages_per = pages / numa_count;
10171016
const size_t pages_mod = pages % numa_count;
10181017
const size_t timeout_per = (timeout_msecs==0 ? 0 : (timeout_msecs / numa_count) + 50);
10191018

10201019
// reserve evenly among numa nodes
1021-
for (size_t numa_node = 0; numa_node < numa_count && pages > 0; numa_node++) {
1020+
for (int numa_node = 0; numa_node < numa_count && pages > 0; numa_node++) {
10221021
size_t node_pages = pages_per; // can be 0
1023-
if (numa_node < pages_mod) node_pages++;
1024-
int err = mi_reserve_huge_os_pages_at(node_pages, (int)numa_node, timeout_per);
1022+
if ((size_t)numa_node < pages_mod) node_pages++;
1023+
int err = mi_reserve_huge_os_pages_at(node_pages, numa_node, timeout_per);
10251024
if (err) return err;
10261025
if (pages < node_pages) {
10271026
pages = 0;

compat/mimalloc/free.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,10 @@ mi_decl_nodiscard size_t mi_usable_size(const void* p) mi_attr_noexcept {
348348

349349
void mi_free_size(void* p, size_t size) mi_attr_noexcept {
350350
MI_UNUSED_RELEASE(size);
351-
mi_assert(p == NULL || size <= _mi_usable_size(p,"mi_free_size"));
351+
#if MI_DEBUG
352+
const size_t available = _mi_usable_size(p,"mi_free_size");
353+
mi_assert(p == NULL || size <= available || available == 0 /* invalid pointer */ );
354+
#endif
352355
mi_free(p);
353356
}
354357

compat/mimalloc/heap.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,7 @@ static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect)
176176
_mi_arenas_collect(collect == MI_FORCE /* force purge? */);
177177

178178
// merge statistics
179-
if (collect <= MI_FORCE) {
180-
mi_stats_merge();
181-
}
179+
if (collect <= MI_FORCE) { _mi_stats_merge_thread(heap->tld); }
182180
}
183181

184182
void _mi_heap_collect_abandon(mi_heap_t* heap) {

compat/mimalloc/init.c

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ mi_decl_cache_align const mi_heap_t _mi_heap_empty = {
123123
false, // can reclaim
124124
0, // tag
125125
#if MI_GUARDED
126-
0, 0, 0, 0, 1, // count is 1 so we never write to it (see `internal.h:mi_heap_malloc_use_guarded`)
126+
0, 0, 0, 1, // count is 1 so we never write to it (see `internal.h:mi_heap_malloc_use_guarded`)
127127
#endif
128128
MI_SMALL_PAGES_EMPTY,
129129
MI_PAGE_QUEUES_EMPTY
@@ -172,7 +172,7 @@ mi_decl_cache_align mi_heap_t _mi_heap_main = {
172172
false, // can reclaim
173173
0, // tag
174174
#if MI_GUARDED
175-
0, 0, 0, 0, 0,
175+
0, 0, 0, 0,
176176
#endif
177177
MI_SMALL_PAGES_EMPTY,
178178
MI_PAGE_QUEUES_EMPTY
@@ -184,15 +184,14 @@ mi_stats_t _mi_stats_main = { MI_STAT_VERSION, MI_STATS_NULL };
184184

185185
#if MI_GUARDED
186186
mi_decl_export void mi_heap_guarded_set_sample_rate(mi_heap_t* heap, size_t sample_rate, size_t seed) {
187-
heap->guarded_sample_seed = seed;
188-
if (heap->guarded_sample_seed == 0) {
189-
heap->guarded_sample_seed = _mi_heap_random_next(heap);
190-
}
191187
heap->guarded_sample_rate = sample_rate;
192-
if (heap->guarded_sample_rate >= 1) {
193-
heap->guarded_sample_seed = heap->guarded_sample_seed % heap->guarded_sample_rate;
188+
heap->guarded_sample_count = sample_rate; // count down samples
189+
if (heap->guarded_sample_rate > 1) {
190+
if (seed == 0) {
191+
seed = _mi_heap_random_next(heap);
192+
}
193+
heap->guarded_sample_count = (seed % heap->guarded_sample_rate) + 1; // start at random count between 1 and `sample_rate`
194194
}
195-
heap->guarded_sample_count = heap->guarded_sample_seed; // count down samples
196195
}
197196

198197
mi_decl_export void mi_heap_guarded_set_size_bound(mi_heap_t* heap, size_t min, size_t max) {
@@ -245,7 +244,6 @@ mi_heap_t* _mi_heap_main_get(void) {
245244
return &_mi_heap_main;
246245
}
247246

248-
249247
/* -----------------------------------------------------------
250248
Sub process
251249
----------------------------------------------------------- */
@@ -319,40 +317,32 @@ static _Atomic(mi_thread_data_t*) td_cache[TD_CACHE_SIZE];
319317

320318
static mi_thread_data_t* mi_thread_data_zalloc(void) {
321319
// try to find thread metadata in the cache
322-
bool is_zero = false;
323320
mi_thread_data_t* td = NULL;
324321
for (int i = 0; i < TD_CACHE_SIZE; i++) {
325322
td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]);
326323
if (td != NULL) {
327324
// found cached allocation, try use it
328325
td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL);
329326
if (td != NULL) {
330-
break;
327+
_mi_memzero(td, offsetof(mi_thread_data_t,memid));
328+
return td;
331329
}
332330
}
333331
}
334332

335333
// if that fails, allocate as meta data
334+
mi_memid_t memid;
335+
td = (mi_thread_data_t*)_mi_os_zalloc(sizeof(mi_thread_data_t), &memid);
336336
if (td == NULL) {
337-
mi_memid_t memid;
338-
td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid);
337+
// if this fails, try once more. (issue #257)
338+
td = (mi_thread_data_t*)_mi_os_zalloc(sizeof(mi_thread_data_t), &memid);
339339
if (td == NULL) {
340-
// if this fails, try once more. (issue #257)
341-
td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid);
342-
if (td == NULL) {
343-
// really out of memory
344-
_mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t));
345-
}
340+
// really out of memory
341+
_mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t));
342+
return NULL;
346343
}
347-
if (td != NULL) {
348-
td->memid = memid;
349-
is_zero = memid.initially_zero;
350-
}
351-
}
352-
353-
if (td != NULL && !is_zero) {
354-
_mi_memzero_aligned(td, offsetof(mi_thread_data_t,memid));
355344
}
345+
td->memid = memid;
356346
return td;
357347
}
358348

@@ -587,7 +577,7 @@ mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept {
587577
}
588578

589579
// Called once by the process loader from `src/prim/prim.c`
590-
void _mi_process_load(void) {
580+
void _mi_auto_process_init(void) {
591581
mi_heap_main_init();
592582
#if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
593583
volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true;
@@ -674,8 +664,8 @@ void mi_process_init(void) mi_attr_noexcept {
674664
}
675665
}
676666

677-
// Called when the process is done (through `at_exit`)
678-
void mi_cdecl _mi_process_done(void) {
667+
// Called when the process is done (cdecl as it is used with `at_exit` on some platforms)
668+
void mi_cdecl mi_process_done(void) mi_attr_noexcept {
679669
// only shutdown if we were initialized
680670
if (!_mi_process_is_initialized) return;
681671
// ensure we are called once
@@ -718,3 +708,7 @@ void mi_cdecl _mi_process_done(void) {
718708
os_preloading = true; // don't call the C runtime anymore
719709
}
720710

711+
void mi_cdecl _mi_auto_process_done(void) mi_attr_noexcept {
712+
if (_mi_option_get_fast(mi_option_destroy_on_exit)>1) return;
713+
mi_process_done();
714+
}

compat/mimalloc/mimalloc.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file
88
#ifndef MIMALLOC_H
99
#define MIMALLOC_H
1010

11-
#define MI_MALLOC_VERSION 223 // major + 2 digits minor
11+
#define MI_MALLOC_VERSION 224 // major + 2 digits minor
1212

1313
// ------------------------------------------------------
1414
// Compiler specific attributes
@@ -155,16 +155,20 @@ mi_decl_export void mi_stats_reset(void) mi_attr_noexcept;
155155
mi_decl_export void mi_stats_merge(void) mi_attr_noexcept;
156156
mi_decl_export void mi_stats_print(void* out) mi_attr_noexcept; // backward compatibility: `out` is ignored and should be NULL
157157
mi_decl_export void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept;
158+
mi_decl_export void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept;
158159
mi_decl_export void mi_options_print(void) mi_attr_noexcept;
159160

161+
mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs,
162+
size_t* current_rss, size_t* peak_rss,
163+
size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept;
164+
165+
166+
// Generally do not use the following as these are usually called automatically
160167
mi_decl_export void mi_process_init(void) mi_attr_noexcept;
168+
mi_decl_export void mi_cdecl mi_process_done(void) mi_attr_noexcept;
161169
mi_decl_export void mi_thread_init(void) mi_attr_noexcept;
162170
mi_decl_export void mi_thread_done(void) mi_attr_noexcept;
163-
mi_decl_export void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept;
164171

165-
mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs,
166-
size_t* current_rss, size_t* peak_rss,
167-
size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept;
168172

169173
// -------------------------------------------------------------------------------------
170174
// Aligned allocation

compat/mimalloc/mimalloc/atomic.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub);
111111
#define mi_atomic_cas_ptr_weak_release(tp,p,exp,des) mi_atomic_cas_weak_release(p,exp,(tp*)des)
112112
#define mi_atomic_cas_ptr_weak_acq_rel(tp,p,exp,des) mi_atomic_cas_weak_acq_rel(p,exp,(tp*)des)
113113
#define mi_atomic_cas_ptr_strong_release(tp,p,exp,des) mi_atomic_cas_strong_release(p,exp,(tp*)des)
114+
#define mi_atomic_cas_ptr_strong_acq_rel(tp,p,exp,des) mi_atomic_cas_strong_acq_rel(p,exp,(tp*)des)
114115
#define mi_atomic_exchange_ptr_relaxed(tp,p,x) mi_atomic_exchange_relaxed(p,(tp*)x)
115116
#define mi_atomic_exchange_ptr_release(tp,p,x) mi_atomic_exchange_release(p,(tp*)x)
116117
#define mi_atomic_exchange_ptr_acq_rel(tp,p,x) mi_atomic_exchange_acq_rel(p,(tp*)x)
@@ -120,6 +121,7 @@ static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub);
120121
#define mi_atomic_cas_ptr_weak_release(tp,p,exp,des) mi_atomic_cas_weak_release(p,exp,des)
121122
#define mi_atomic_cas_ptr_weak_acq_rel(tp,p,exp,des) mi_atomic_cas_weak_acq_rel(p,exp,des)
122123
#define mi_atomic_cas_ptr_strong_release(tp,p,exp,des) mi_atomic_cas_strong_release(p,exp,des)
124+
#define mi_atomic_cas_ptr_strong_acq_rel(tp,p,exp,des) mi_atomic_cas_strong_acq_rel(p,exp,des)
123125
#define mi_atomic_exchange_ptr_relaxed(tp,p,x) mi_atomic_exchange_relaxed(p,x)
124126
#define mi_atomic_exchange_ptr_release(tp,p,x) mi_atomic_exchange_release(p,x)
125127
#define mi_atomic_exchange_ptr_acq_rel(tp,p,x) mi_atomic_exchange_acq_rel(p,x)
@@ -303,6 +305,7 @@ static inline bool mi_atomic_casi64_strong_acq_rel(volatile _Atomic(int64_t*)p,
303305
#define mi_atomic_cas_ptr_weak_release(tp,p,exp,des) mi_atomic_cas_weak_release((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des)
304306
#define mi_atomic_cas_ptr_weak_acq_rel(tp,p,exp,des) mi_atomic_cas_weak_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des)
305307
#define mi_atomic_cas_ptr_strong_release(tp,p,exp,des) mi_atomic_cas_strong_release((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des)
308+
#define mi_atomic_cas_ptr_strong_acq_rel(tp,p,exp,des) mi_atomic_cas_strong_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t*)exp,(uintptr_t)des)
306309
#define mi_atomic_exchange_ptr_relaxed(tp,p,x) (tp*)mi_atomic_exchange_relaxed((_Atomic(uintptr_t)*)(p),(uintptr_t)x)
307310
#define mi_atomic_exchange_ptr_release(tp,p,x) (tp*)mi_atomic_exchange_release((_Atomic(uintptr_t)*)(p),(uintptr_t)x)
308311
#define mi_atomic_exchange_ptr_acq_rel(tp,p,x) (tp*)mi_atomic_exchange_acq_rel((_Atomic(uintptr_t)*)(p),(uintptr_t)x)

0 commit comments

Comments
 (0)