Skip to content

Commit 23edb13

Browse files
committed
Merge remote-tracking branch 'eduke32/master'
2 parents 0c4bba7 + 088ce5c commit 23edb13

32 files changed

+2183
-1377
lines changed

GNUmakefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ mimalloc_obj := $(obj)/$(mimalloc)
130130

131131
mimalloc_excl := \
132132
alloc-override.c \
133+
free.c \
133134
page-queue.c \
134135
static.c \
135136

@@ -142,6 +143,7 @@ ifeq (,$(filter 1 2 3 4 5 6 7,$(GCC_MAJOR)))
142143
mimalloc_cflags += -Wno-class-memaccess
143144
endif
144145

146+
145147
#### imgui
146148

147149
imgui := imgui

source/mimalloc/include/mimalloc.h

Lines changed: 31 additions & 26 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 182 // major + 2 digits minor
11+
#define MI_MALLOC_VERSION 186 // major + 2 digits minor
1212

1313
// ------------------------------------------------------
1414
// Compiler specific attributes
@@ -274,6 +274,8 @@ mi_decl_export int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size
274274
mi_decl_export int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept;
275275
mi_decl_export bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept;
276276

277+
mi_decl_export void mi_debug_show_arenas(bool show_inuse, bool show_abandoned, bool show_purge) mi_attr_noexcept;
278+
277279
// Experimental: heaps associated with specific memory arena's
278280
typedef int mi_arena_id_t;
279281
mi_decl_export void* mi_arena_area(mi_arena_id_t arena_id, size_t* size);
@@ -315,40 +317,43 @@ mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size
315317

316318
typedef enum mi_option_e {
317319
// stable options
318-
mi_option_show_errors, // print error messages
319-
mi_option_show_stats, // print statistics on termination
320-
mi_option_verbose, // print verbose messages
321-
// the following options are experimental (see src/options.h)
322-
mi_option_eager_commit, // eager commit segments? (after `eager_commit_delay` segments) (=1)
323-
mi_option_arena_eager_commit, // eager commit arenas? Use 2 to enable just on overcommit systems (=2)
324-
mi_option_purge_decommits, // should a memory purge decommit (or only reset) (=1)
325-
mi_option_allow_large_os_pages, // allow large (2MiB) OS pages, implies eager commit
326-
mi_option_reserve_huge_os_pages, // reserve N huge OS pages (1GiB/page) at startup
327-
mi_option_reserve_huge_os_pages_at, // reserve huge OS pages at a specific NUMA node
328-
mi_option_reserve_os_memory, // reserve specified amount of OS memory in an arena at startup
320+
mi_option_show_errors, // print error messages
321+
mi_option_show_stats, // print statistics on termination
322+
mi_option_verbose, // print verbose messages
323+
// advanced options
324+
mi_option_eager_commit, // eager commit segments? (after `eager_commit_delay` segments) (=1)
325+
mi_option_arena_eager_commit, // eager commit arenas? Use 2 to enable just on overcommit systems (=2)
326+
mi_option_purge_decommits, // should a memory purge decommit? (=1). Set to 0 to use memory reset on a purge (instead of decommit)
327+
mi_option_allow_large_os_pages, // allow large (2 or 4 MiB) OS pages, implies eager commit. If false, also disables THP for the process.
328+
mi_option_reserve_huge_os_pages, // reserve N huge OS pages (1GiB pages) at startup
329+
mi_option_reserve_huge_os_pages_at, // reserve huge OS pages at a specific NUMA node
330+
mi_option_reserve_os_memory, // reserve specified amount of OS memory in an arena at startup
329331
mi_option_deprecated_segment_cache,
330332
mi_option_deprecated_page_reset,
331-
mi_option_abandoned_page_purge, // immediately purge delayed purges on thread termination
333+
mi_option_abandoned_page_purge, // immediately purge delayed purges on thread termination
332334
mi_option_deprecated_segment_reset,
333-
mi_option_eager_commit_delay,
334-
mi_option_purge_delay, // memory purging is delayed by N milli seconds; use 0 for immediate purging or -1 for no purging at all.
335-
mi_option_use_numa_nodes, // 0 = use all available numa nodes, otherwise use at most N nodes.
336-
mi_option_limit_os_alloc, // 1 = do not use OS memory for allocation (but only programmatically reserved arenas)
337-
mi_option_os_tag, // tag used for OS logging (macOS only for now)
338-
mi_option_max_errors, // issue at most N error messages
339-
mi_option_max_warnings, // issue at most N warning messages
340-
mi_option_max_segment_reclaim,
341-
mi_option_destroy_on_exit, // if set, release all memory on exit; sometimes used for dynamic unloading but can be unsafe.
342-
mi_option_arena_reserve, // initial memory size in KiB for arena reservation (1GiB on 64-bit)
343-
mi_option_arena_purge_mult,
335+
mi_option_eager_commit_delay, // the first N segments per thread are not eagerly committed (but per page in the segment on demand)
336+
mi_option_purge_delay, // memory purging is delayed by N milli seconds; use 0 for immediate purging or -1 for no purging at all. (=10)
337+
mi_option_use_numa_nodes, // 0 = use all available numa nodes, otherwise use at most N nodes.
338+
mi_option_disallow_os_alloc, // 1 = do not use OS memory for allocation (but only programmatically reserved arenas)
339+
mi_option_os_tag, // tag used for OS logging (macOS only for now) (=100)
340+
mi_option_max_errors, // issue at most N error messages
341+
mi_option_max_warnings, // issue at most N warning messages
342+
mi_option_max_segment_reclaim, // max. percentage of the abandoned segments can be reclaimed per try (=10%)
343+
mi_option_destroy_on_exit, // if set, release all memory on exit; sometimes used for dynamic unloading but can be unsafe
344+
mi_option_arena_reserve, // initial memory size in KiB for arena reservation (= 1 GiB on 64-bit)
345+
mi_option_arena_purge_mult, // multiplier for `purge_delay` for the purging delay for arenas (=10)
344346
mi_option_purge_extend_delay,
347+
mi_option_abandoned_reclaim_on_free, // allow to reclaim an abandoned segment on a free (=1)
348+
mi_option_disallow_arena_alloc, // 1 = do not use arena's for allocation (except if using specific arena id's)
345349
_mi_option_last,
346350
// legacy option names
347351
mi_option_large_os_pages = mi_option_allow_large_os_pages,
348352
mi_option_eager_region_commit = mi_option_arena_eager_commit,
349353
mi_option_reset_decommits = mi_option_purge_decommits,
350354
mi_option_reset_delay = mi_option_purge_delay,
351-
mi_option_abandoned_page_reset = mi_option_abandoned_page_purge
355+
mi_option_abandoned_page_reset = mi_option_abandoned_page_purge,
356+
mi_option_limit_os_alloc = mi_option_disallow_os_alloc
352357
} mi_option_t;
353358

354359

@@ -491,7 +496,7 @@ template<class T, bool _mi_destroy> struct _mi_heap_stl_allocator_common : publi
491496
using typename _mi_stl_allocator_common<T>::value_type;
492497
using typename _mi_stl_allocator_common<T>::pointer;
493498

494-
_mi_heap_stl_allocator_common(mi_heap_t* hp) : heap(hp) { } /* will not delete nor destroy the passed in heap */
499+
_mi_heap_stl_allocator_common(mi_heap_t* hp) : heap(hp, [](mi_heap_t*) {}) {} /* will not delete nor destroy the passed in heap */
495500

496501
#if (__cplusplus >= 201703L) // C++17
497502
mi_decl_nodiscard T* allocate(size_type count) { return static_cast<T*>(mi_heap_alloc_new_n(this->heap.get(), count, sizeof(T))); }

source/mimalloc/include/mimalloc/atomic.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ terms of the MIT license. A copy of the license can be found in the file
2323
#define _Atomic(tp) std::atomic<tp>
2424
#define mi_atomic(name) std::atomic_##name
2525
#define mi_memory_order(name) std::memory_order_##name
26-
#if !defined(ATOMIC_VAR_INIT) || (__cplusplus >= 202002L) // c++20, see issue #571
27-
#define MI_ATOMIC_VAR_INIT(x) x
26+
#if (__cplusplus >= 202002L) // c++20, see issue #571
27+
#define MI_ATOMIC_VAR_INIT(x) x
28+
#elif !defined(ATOMIC_VAR_INIT)
29+
#define MI_ATOMIC_VAR_INIT(x) x
2830
#else
2931
#define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x)
3032
#endif
@@ -39,7 +41,9 @@ terms of the MIT license. A copy of the license can be found in the file
3941
#include <stdatomic.h>
4042
#define mi_atomic(name) atomic_##name
4143
#define mi_memory_order(name) memory_order_##name
42-
#if !defined(ATOMIC_VAR_INIT) || (__STDC_VERSION__ >= 201710L) // c17, see issue #735
44+
#if (__STDC_VERSION__ >= 201710L) // c17, see issue #735
45+
#define MI_ATOMIC_VAR_INIT(x) x
46+
#elif !defined(ATOMIC_VAR_INIT)
4347
#define MI_ATOMIC_VAR_INIT(x) x
4448
#else
4549
#define MI_ATOMIC_VAR_INIT(x) ATOMIC_VAR_INIT(x)
@@ -129,7 +133,9 @@ static inline void mi_atomic_maxi64_relaxed(volatile int64_t* p, int64_t x) {
129133
#elif defined(_MSC_VER)
130134

131135
// MSVC C compilation wrapper that uses Interlocked operations to model C11 atomics.
136+
#ifndef WIN32_LEAN_AND_MEAN
132137
#define WIN32_LEAN_AND_MEAN
138+
#endif
133139
#include <windows.h>
134140
#include <intrin.h>
135141
#ifdef _WIN64
@@ -323,7 +329,9 @@ static inline void mi_atomic_yield(void) {
323329
std::this_thread::yield();
324330
}
325331
#elif defined(_WIN32)
332+
#ifndef WIN32_LEAN_AND_MEAN
326333
#define WIN32_LEAN_AND_MEAN
334+
#endif
327335
#include <windows.h>
328336
static inline void mi_atomic_yield(void) {
329337
YieldProcessor();

source/mimalloc/include/mimalloc/internal.h

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@ terms of the MIT license. A copy of the license can be found in the file
3030
#define mi_decl_noinline __declspec(noinline)
3131
#define mi_decl_thread __declspec(thread)
3232
#define mi_decl_cache_align __declspec(align(MI_CACHE_LINE))
33+
#define mi_decl_weak
3334
#elif (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) // includes clang and icc
3435
#define mi_decl_noinline __attribute__((noinline))
3536
#define mi_decl_thread __thread
3637
#define mi_decl_cache_align __attribute__((aligned(MI_CACHE_LINE)))
38+
#define mi_decl_weak __attribute__((weak))
3739
#else
3840
#define mi_decl_noinline
3941
#define mi_decl_thread __thread // hope for the best :-)
4042
#define mi_decl_cache_align
43+
#define mi_decl_weak
4144
#endif
4245

4346
#if defined(__EMSCRIPTEN__) && !defined(__wasi__)
@@ -88,7 +91,7 @@ void _mi_thread_data_collect(void);
8891

8992
// os.c
9093
void _mi_os_init(void); // called from process init
91-
void* _mi_os_alloc(size_t size, mi_memid_t* memid, mi_stats_t* stats);
94+
void* _mi_os_alloc(size_t size, mi_memid_t* memid, mi_stats_t* stats);
9295
void _mi_os_free(void* p, size_t size, mi_memid_t memid, mi_stats_t* stats);
9396
void _mi_os_free_ex(void* p, size_t size, bool still_committed, mi_memid_t memid, mi_stats_t* stats);
9497

@@ -121,9 +124,21 @@ void* _mi_arena_alloc(size_t size, bool commit, bool allow_large, mi_arena_
121124
void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld);
122125
bool _mi_arena_memid_is_suitable(mi_memid_t memid, mi_arena_id_t request_arena_id);
123126
bool _mi_arena_contains(const void* p);
124-
void _mi_arena_collect(bool force_purge, mi_stats_t* stats);
127+
void _mi_arenas_collect(bool force_purge, mi_stats_t* stats);
125128
void _mi_arena_unsafe_destroy_all(mi_stats_t* stats);
126129

130+
bool _mi_arena_segment_clear_abandoned(mi_segment_t* segment);
131+
void _mi_arena_segment_mark_abandoned(mi_segment_t* segment);
132+
size_t _mi_arena_segment_abandoned_count(void);
133+
134+
typedef struct mi_arena_field_cursor_s { // abstract
135+
mi_arena_id_t start;
136+
int count;
137+
size_t bitmap_idx;
138+
} mi_arena_field_cursor_t;
139+
void _mi_arena_field_cursor_init(mi_heap_t* heap, mi_arena_field_cursor_t* current);
140+
mi_segment_t* _mi_arena_segment_clear_abandoned_next(mi_arena_field_cursor_t* previous);
141+
127142
// "segment-map.c"
128143
void _mi_segment_map_allocated_at(const mi_segment_t* segment);
129144
void _mi_segment_map_freed_at(const mi_segment_t* segment);
@@ -132,17 +147,18 @@ void _mi_segment_map_freed_at(const mi_segment_t* segment);
132147
mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, size_t page_alignment, mi_segments_tld_t* tld, mi_os_tld_t* os_tld);
133148
void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld);
134149
void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld);
135-
uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t block_size, size_t* page_size, size_t* pre_size); // page start for any page
150+
uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size);
136151

137152
#if MI_HUGE_PAGE_ABANDON
138153
void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block);
139154
#else
140155
void _mi_segment_huge_page_reset(mi_segment_t* segment, mi_page_t* page, mi_block_t* block);
141156
#endif
142157

143-
void _mi_segment_thread_collect(mi_segments_tld_t* tld);
158+
void _mi_segments_collect(bool force, mi_segments_tld_t* tld);
144159
void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld);
145160
void _mi_abandoned_await_readers(void);
161+
bool _mi_segment_attempt_reclaim(mi_heap_t* heap, mi_segment_t* segment);
146162

147163
// "page.c"
148164
void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept mi_attr_malloc;
@@ -180,23 +196,28 @@ mi_msecs_t _mi_clock_end(mi_msecs_t start);
180196
mi_msecs_t _mi_clock_start(void);
181197

182198
// "alloc.c"
183-
void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept; // called from `_mi_malloc_generic`
199+
void* _mi_page_malloc_zero(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept; // called from `_mi_malloc_generic`
200+
void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned`
201+
void* _mi_page_malloc_zeroed(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned`
184202
void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept;
185203
void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept; // called from `_mi_heap_malloc_aligned`
186204
void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept;
187-
mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* page, const void* p);
205+
mi_block_t* _mi_page_ptr_unalign(const mi_page_t* page, const void* p);
188206
bool _mi_free_delayed_block(mi_block_t* block);
189-
void _mi_free_generic(const mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept; // for runtime integration
207+
void _mi_free_generic(mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept; // for runtime integration
190208
void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size);
191209

192-
// option.c, c primitives
210+
// "libc.c"
211+
#include <stdarg.h>
212+
void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args);
213+
void _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...);
193214
char _mi_toupper(char c);
194215
int _mi_strnicmp(const char* s, const char* t, size_t n);
195216
void _mi_strlcpy(char* dest, const char* src, size_t dest_size);
196217
void _mi_strlcat(char* dest, const char* src, size_t dest_size);
197218
size_t _mi_strlen(const char* s);
198219
size_t _mi_strnlen(const char* s, size_t max_len);
199-
220+
bool _mi_getenv(const char* name, char* result, size_t result_size);
200221

201222
#if MI_DEBUG>1
202223
bool _mi_page_is_valid(mi_page_t* page);
@@ -293,6 +314,12 @@ static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) {
293314
}
294315
}
295316

317+
// Align a pointer upwards
318+
static inline void* mi_align_up_ptr(void* p, size_t alignment) {
319+
return (void*)_mi_align_up((uintptr_t)p, alignment);
320+
}
321+
322+
296323
// Divide upwards: `s <= _mi_divide_up(s,d)*d < s+d`.
297324
static inline uintptr_t _mi_divide_up(uintptr_t size, size_t divider) {
298325
mi_assert_internal(divider != 0);
@@ -391,13 +418,19 @@ static inline mi_page_t* _mi_heap_get_free_small_page(mi_heap_t* heap, size_t si
391418
// Large aligned blocks may be aligned at N*MI_SEGMENT_SIZE (inside a huge segment > MI_SEGMENT_SIZE),
392419
// and we need align "down" to the segment info which is `MI_SEGMENT_SIZE` bytes before it;
393420
// therefore we align one byte before `p`.
421+
// We check for NULL afterwards on 64-bit systems to improve codegen for `mi_free`.
394422
static inline mi_segment_t* _mi_ptr_segment(const void* p) {
395-
mi_assert_internal(p != NULL);
396-
return (mi_segment_t*)(((uintptr_t)p - 1) & ~MI_SEGMENT_MASK);
423+
mi_segment_t* const segment = (mi_segment_t*)(((uintptr_t)p - 1) & ~MI_SEGMENT_MASK);
424+
#if MI_INTPTR_SIZE <= 4
425+
return (p==NULL ? NULL : segment);
426+
#else
427+
return ((intptr_t)segment <= 0 ? NULL : segment);
428+
#endif
397429
}
398430

399431
// Segment belonging to a page
400432
static inline mi_segment_t* _mi_page_segment(const mi_page_t* page) {
433+
mi_assert_internal(page!=NULL);
401434
mi_segment_t* segment = _mi_ptr_segment(page);
402435
mi_assert_internal(segment == NULL || page == &segment->pages[page->segment_idx]);
403436
return segment;
@@ -421,33 +454,28 @@ static inline mi_page_t* _mi_segment_page_of(const mi_segment_t* segment, const
421454
}
422455

423456
// Quick page start for initialized pages
424-
static inline uint8_t* _mi_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size) {
425-
const size_t bsize = page->xblock_size;
426-
mi_assert_internal(bsize > 0 && (bsize%sizeof(void*)) == 0);
427-
return _mi_segment_page_start(segment, page, bsize, page_size, NULL);
457+
static inline uint8_t* mi_page_start(const mi_page_t* page) {
458+
mi_assert_internal(page->page_start != NULL);
459+
mi_assert_expensive(_mi_segment_page_start(_mi_page_segment(page),page,NULL) == page->page_start);
460+
return page->page_start;
428461
}
429462

430463
// Get the page containing the pointer
431464
static inline mi_page_t* _mi_ptr_page(void* p) {
465+
mi_assert_internal(p!=NULL);
432466
return _mi_segment_page_of(_mi_ptr_segment(p), p);
433467
}
434468

435469
// Get the block size of a page (special case for huge objects)
436470
static inline size_t mi_page_block_size(const mi_page_t* page) {
437-
const size_t bsize = page->xblock_size;
438-
mi_assert_internal(bsize > 0);
439-
if mi_likely(bsize < MI_HUGE_BLOCK_SIZE) {
440-
return bsize;
441-
}
442-
else {
443-
size_t psize;
444-
_mi_segment_page_start(_mi_page_segment(page), page, bsize, &psize, NULL);
445-
return psize;
446-
}
471+
mi_assert_internal(page->block_size > 0);
472+
return page->block_size;
447473
}
448474

449475
static inline bool mi_page_is_huge(const mi_page_t* page) {
450-
return (_mi_page_segment(page)->page_kind == MI_PAGE_HUGE);
476+
mi_assert_internal((page->is_huge && _mi_page_segment(page)->page_kind == MI_PAGE_HUGE) ||
477+
(!page->is_huge && _mi_page_segment(page)->page_kind != MI_PAGE_HUGE));
478+
return page->is_huge;
451479
}
452480

453481
// Get the usable block size of a page without fixed padding.

0 commit comments

Comments
 (0)