From 64b0a02690224b66eaa24a89e48c63b715ce27cb Mon Sep 17 00:00:00 2001 From: Daan Date: Sun, 3 May 2026 17:06:10 -0700 Subject: [PATCH 01/35] remove wchar.h include from mimalloc.h (see also issue leanprover/lean4#7786) --- include/mimalloc.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/mimalloc.h b/include/mimalloc.h index 574336397..6522c9ebc 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -95,7 +95,7 @@ terms of the MIT license. A copy of the license can be found in the file // Includes // ------------------------------------------------------ -#include // size_t +#include // size_t, wchar_t #include // bool #ifdef __cplusplus @@ -483,7 +483,6 @@ mi_decl_export void mi_free_aligned(void* p, size_t alignment) mi_decl_export int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept; // wide characters -#include // wchar_t mi_decl_export int mi_wdupenv_s(wchar_t** buf, size_t* size, const wchar_t* name) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export mi_decl_restrict wchar_t* mi_wcsdup(const wchar_t* s) mi_attr_noexcept mi_attr_malloc; mi_decl_nodiscard mi_decl_export mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept mi_attr_malloc; From a0212e24bdcb6de370365e4134e8640d22759520 Mon Sep 17 00:00:00 2001 From: thunderllei Date: Mon, 15 Jun 2026 15:30:46 +0800 Subject: [PATCH 02/35] fix(osx): implement introspect.reinit_lock to avoid fork() crash The macOS malloc zone advertises version >= 9, for which libmalloc invokes introspect->reinit_lock from the atfork_child handler (_malloc_fork_child) without a NULL check. The introspection struct left reinit_lock unset (NULL), so any fork() in a process that statically links the mimalloc zone made the child jump to address 0 and crash (EXC_BAD_ACCESS at 0x0) on macOS 15. Provide a no-op reinit_lock: mimalloc has no zone-level locks to reinitialize after fork. --- src/prim/osx/alloc-override-zone.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/prim/osx/alloc-override-zone.c b/src/prim/osx/alloc-override-zone.c index 3b1d10706..543d85e64 100644 --- a/src/prim/osx/alloc-override-zone.c +++ b/src/prim/osx/alloc-override-zone.c @@ -174,6 +174,14 @@ static boolean_t intro_zone_locked(malloc_zone_t* zone) { return false; } +// Required whenever the zone advertises version >= 9: macOS calls this from the +// atfork_child handler (_malloc_fork_child) without a NULL check. mimalloc keeps +// no zone-level locks that need reinitializing after fork, so a no-op is safe. +// Leaving it NULL makes the forked child jump to address 0 and crash in fork(). +static void intro_reinit_lock(malloc_zone_t* zone) { + MI_UNUSED(zone); +} + /* ------------------------------------------------------ At process start, override the default allocator @@ -198,6 +206,7 @@ static malloc_introspection_t mi_introspect = { #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) && !defined(__ppc__) .statistics = &intro_statistics, .zone_locked = &intro_zone_locked, + .reinit_lock = &intro_reinit_lock, #endif }; From d63ab625da83048ddec0c33ce6bbe3d9eb3dd4ce Mon Sep 17 00:00:00 2001 From: Daan Leijen Date: Mon, 22 Jun 2026 09:42:22 -0700 Subject: [PATCH 03/35] update _mi_prim_getenv interface to distinguish not-found and errors better, see issue #1314 --- include/mimalloc/internal.h | 7 +++++-- include/mimalloc/prim.h | 4 +++- src/libc.c | 16 +++++++++------- src/options.c | 17 +++++++++-------- src/prim/emscripten/prim.c | 4 ++-- src/prim/unix/prim.c | 20 ++++++++++---------- src/prim/wasi/prim.c | 8 ++++---- src/prim/windows/prim.c | 8 ++++---- 8 files changed, 46 insertions(+), 38 deletions(-) diff --git a/include/mimalloc/internal.h b/include/mimalloc/internal.h index 133a235bd..7b3aa3a52 100644 --- a/include/mimalloc/internal.h +++ b/include/mimalloc/internal.h @@ -110,7 +110,7 @@ void _mi_strlcat(char* dest, const char* src, size_t dest_size); size_t _mi_strlen(const char* s); size_t _mi_strnlen(const char* s, size_t max_len); bool _mi_streq(const char* s, const char* t); -bool _mi_getenv(const char* name, char* result, size_t result_size); +int _mi_getenv(const char* name, char* result, size_t result_size); // "options.c" void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message); @@ -318,6 +318,9 @@ bool _mi_page_is_valid(mi_page_t* page); #ifndef EOVERFLOW // count*size overflow #define EOVERFLOW (75) #endif +#ifndef ENOENT // environment variable not found +#define ENOENT (2) +#endif // ------------------------------------------------------ @@ -680,7 +683,7 @@ static inline bool mi_block_ptr_is_guarded(const mi_block_t* block, const void* #else MI_UNUSED(block); MI_UNUSED(p); return false; -#endif +#endif } #if MI_GUARDED diff --git a/include/mimalloc/prim.h b/include/mimalloc/prim.h index b963ad5dc..51b5c9a6f 100644 --- a/include/mimalloc/prim.h +++ b/include/mimalloc/prim.h @@ -106,7 +106,9 @@ void _mi_prim_out_stderr( const char* msg ); // Get an environment variable. (only for options) // name != NULL, result != NULL, result_size >= 64 -bool _mi_prim_getenv(const char* name, char* result, size_t result_size); +// Return 1 for success, 0 if not found, +// and -1 on error (for example, if `getenv` cannot be called yet during preloading). +int _mi_prim_getenv(const char* name, char* result, size_t result_size); // Fill a buffer with strong randomness; return `false` on error or if diff --git a/src/libc.c b/src/libc.c index 3eefa52d0..197a27228 100644 --- a/src/libc.c +++ b/src/libc.c @@ -73,16 +73,18 @@ size_t _mi_strlen(const char* s) { } #ifdef MI_NO_GETENV -bool _mi_getenv(const char* name, char* result, size_t result_size) { +int _mi_getenv(const char* name, char* result, size_t result_size) { MI_UNUSED(name); MI_UNUSED(result); MI_UNUSED(result_size); - return false; + return ENOENT; } #else -bool _mi_getenv(const char* name, char* result, size_t result_size) { +int _mi_getenv(const char* name, char* result, size_t result_size) { if (name==NULL || result == NULL || result_size < 64) return false; - return _mi_prim_getenv(name,result,result_size); + // change the result of _mi_prim_getenv to an errno result + const int res = _mi_prim_getenv(name,result,result_size); + return (res > 0 ? 0 : (res == 0 ? ENOENT : EAGAIN)); } #endif @@ -102,13 +104,13 @@ bool _mi_atomic_once_enter(mi_atomic_once_t* once) { const mi_threadid_t current_tid = _mi_thread_id(); if (once_tid == current_tid) { return false; // recursive invocation; we need this for process_init for example - } + } mi_lock_acquire(&once->lock); uintptr_t expected = 0; if (mi_atomic_cas_strong_acq_rel(&once->tid, &expected, current_tid)) { // could use atomic_load/store as well return true; // should execute and release - } + } else { mi_lock_release(&once->lock); return false; // already another thread entered and released @@ -119,7 +121,7 @@ void _mi_atomic_once_release(mi_atomic_once_t* once) { if (mi_atomic_load_acquire(&once->tid)>1) { // paranoia mi_atomic_store_release(&once->tid,1); // done executing mi_lock_release(&once->lock); - } + } } diff --git a/src/options.c b/src/options.c index cac6b38b3..47b60232e 100644 --- a/src/options.c +++ b/src/options.c @@ -168,7 +168,7 @@ static mi_option_desc_t options[_mi_option_last] = { 0, UNINIT, MI_OPTION(guarded_sample_seed)}, { 0, UNINIT, MI_OPTION(target_segments_per_thread) }, // abandon segments beyond this point, or 0 to disable. { 10000, UNINIT, MI_OPTION(generic_collect) }, // collect heaps every N (=10000) generic allocation calls - { MI_DEFAULT_ALLOW_THP, + { MI_DEFAULT_ALLOW_THP, UNINIT, MI_OPTION(allow_thp) } // allow transparent huge pages? }; @@ -611,17 +611,17 @@ static void mi_option_init(mi_option_desc_t* desc) { char buf[64+1]; _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); _mi_strlcat(buf, desc->name, sizeof(buf)); - bool found = _mi_getenv(buf, s, sizeof(s)); - if (!found && desc->legacy_name != NULL) { + int err = _mi_getenv(buf, s, sizeof(s)); + if (err==ENOENT && desc->legacy_name != NULL) { _mi_strlcpy(buf, "mimalloc_", sizeof(buf)); _mi_strlcat(buf, desc->legacy_name, sizeof(buf)); - found = _mi_getenv(buf, s, sizeof(s)); - if (found) { + err = _mi_getenv(buf, s, sizeof(s)); + if (err==0) { _mi_warning_message("environment option \"mimalloc_%s\" is deprecated -- use \"mimalloc_%s\" instead.\n", desc->legacy_name, desc->name); } } - if (found) { + if (err==0) { size_t len = _mi_strnlen(s, sizeof(buf) - 1); for (size_t i = 0; i < len; i++) { buf[i] = _mi_toupper(s[i]); @@ -631,7 +631,7 @@ static void mi_option_init(mi_option_desc_t* desc) { desc->value = 1; desc->init = INITIALIZED; } - else if (_mi_streq(buf,"0") || _mi_streq(buf,"FALSE") || _mi_streq(buf,"NO") || _mi_streq(buf,"OFF")) { + else if (_mi_streq(buf,"0") || _mi_streq(buf,"FALSE") || _mi_streq(buf,"NO") || _mi_streq(buf,"OFF")) { desc->value = 0; desc->init = INITIALIZED; } @@ -673,7 +673,8 @@ static void mi_option_init(mi_option_desc_t* desc) { } mi_assert_internal(desc->init != UNINIT); } - else if (!_mi_preloading()) { + else if (err==ENOENT) { desc->init = DEFAULTED; } + // and on another error, keep unitialized to try again (can happen during preloading if getenv is not available) } diff --git a/src/prim/emscripten/prim.c b/src/prim/emscripten/prim.c index 93c76757d..a9b9266a9 100644 --- a/src/prim/emscripten/prim.c +++ b/src/prim/emscripten/prim.c @@ -184,12 +184,12 @@ void _mi_prim_out_stderr( const char* msg) { // Environment //---------------------------------------------------------------- -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { // For code size reasons, do not support environ customization for now. MI_UNUSED(name); MI_UNUSED(result); MI_UNUSED(result_size); - return false; + return 0; // not found } diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index a7a6b9292..1c08aa73a 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -800,28 +800,28 @@ static char** mi_get_environ(void) { return environ; } #endif -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { - if (name==NULL) return false; +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { + if (name==NULL) return -1; const size_t len = _mi_strlen(name); - if (len == 0) return false; + if (len == 0) return -1; char** env = mi_get_environ(); - if (env == NULL) return false; + if (env == NULL) return -1; // compare up to 10000 entries for (int i = 0; i < 10000 && env[i] != NULL; i++) { const char* s = env[i]; if (_mi_strnicmp(name, s, len) == 0 && s[len] == '=') { // case insensitive // found it _mi_strlcpy(result, s + len + 1, result_size); - return true; + return 1; // success } } - return false; + return 0; // not found } #else // fallback: use standard C `getenv` but this cannot be used while initializing the C runtime -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { // cannot call getenv() when still initializing the C runtime. - if (_mi_preloading()) return false; + if (_mi_preloading()) return -1; // error, try again later const char* s = getenv(name); if (s == NULL) { // we check the upper case name too. @@ -833,9 +833,9 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { buf[len] = 0; s = getenv(buf); } - if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false; + if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return 0; // not found _mi_strlcpy(result, s, result_size); - return true; + return 1; // success } #endif // !MI_USE_ENVIRON diff --git a/src/prim/wasi/prim.c b/src/prim/wasi/prim.c index 8fff0ea84..0719f12cb 100644 --- a/src/prim/wasi/prim.c +++ b/src/prim/wasi/prim.c @@ -238,9 +238,9 @@ void _mi_prim_out_stderr( const char* msg ) { // Environment //---------------------------------------------------------------- -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { // cannot call getenv() when still initializing the C runtime. - if (_mi_preloading()) return false; + if (_mi_preloading()) return -1; // error, try again later const char* s = getenv(name); if (s == NULL) { // we check the upper case name too. @@ -252,9 +252,9 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { buf[len] = 0; s = getenv(buf); } - if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false; + if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return 0; // not found _mi_strlcpy(result, s, result_size); - return true; + return 1; // found } diff --git a/src/prim/windows/prim.c b/src/prim/windows/prim.c index 0d9bb2ac9..1ecde738e 100644 --- a/src/prim/windows/prim.c +++ b/src/prim/windows/prim.c @@ -611,10 +611,10 @@ void _mi_prim_out_stderr( const char* msg ) // reliably even when this is invoked before the C runtime is initialized. // i.e. when `_mi_preloading() == true`. // Note: on windows, environment names are not case sensitive. -bool _mi_prim_getenv(const char* name, char* result, size_t result_size) { +int _mi_prim_getenv(const char* name, char* result, size_t result_size) { result[0] = 0; const size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size); - return (len > 0 && len < result_size); + return (len < result_size ? (len > 0 ? 1 /* success */ : 0 /* not found */) : -1 /* error */); } @@ -722,9 +722,9 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { ------------------------------------------------------------------------- */ #if !defined(MI_WIN_INIT_USE_CRT_TLS) && !defined(MI_WIN_INIT_USE_RAW_DLLMAIN) && !defined(MI_WIN_INIT_USE_TLS_DLLMAIN) && !defined(MI_WIN_INIT_USE_FLS) #if !defined(__INTEL_LLVM_COMPILER) && !defined(__INTEL_COMPILER) - #define MI_WIN_INIT_USE_CRT_TLS 1 + #define MI_WIN_INIT_USE_CRT_TLS 1 #else - #define MI_WIN_INIT_USE_TLS_DLLMAIN 1 /* default for Intel ICX, see issue #1268 */ + #define MI_WIN_INIT_USE_TLS_DLLMAIN 1 /* default for Intel ICX, see issue #1268 */ #endif #endif From dc34dcea06c06ea15a5d587ba50151e29607dd06 Mon Sep 17 00:00:00 2001 From: Daan Date: Mon, 22 Jun 2026 10:32:06 -0700 Subject: [PATCH 04/35] fix typo, pr #1308 --- include/mimalloc/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mimalloc/types.h b/include/mimalloc/types.h index f13cd331a..dd390ef0b 100644 --- a/include/mimalloc/types.h +++ b/include/mimalloc/types.h @@ -465,7 +465,7 @@ typedef struct mi_subproc_s mi_subproc_t; typedef struct mi_segment_s { // constant fields mi_memid_t memid; // memory id for arena/OS allocation - bool allow_decommit; // can we decommmit the memory + bool allow_decommit; // can we decommit the memory bool allow_purge; // can we purge the memory (reset or decommit) size_t segment_size; mi_subproc_t* subproc; // segment belongs to sub process From 79c1f75759ff07c86fdd1206cecafc161591af2b Mon Sep 17 00:00:00 2001 From: Daan Date: Mon, 22 Jun 2026 14:04:38 -0700 Subject: [PATCH 05/35] backport PR #1293 --- bin/bundle.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/bin/bundle.sh b/bin/bundle.sh index 954b135cc..bc864b359 100755 --- a/bin/bundle.sh +++ b/bin/bundle.sh @@ -24,7 +24,7 @@ warn() { } stop() { - warn $@ + warn "$@" exit 1 } @@ -103,7 +103,11 @@ process_options() { -p) shift PREFIX="$1";; -p=*|--prefix=*) - PREFIX=`eval echo $flag_arg`;; # no quotes so ~ gets expanded (issue #412) + case "$flag_arg" in + "~") PREFIX="$HOME" ;; + "~/"*) PREFIX="$HOME/${flag_arg#~/}" ;; + *) PREFIX="$flag_arg" ;; + esac;; -h|--help|-\?|help|\?) MODE="help";; *) case "$flag" in @@ -139,7 +143,7 @@ download_file() { # fi;; *) info "Copying: $1" - if ! cp $1 $2 ; then + if ! cp "$1" "$2" ; then stop "Unable to copy from $1" fi;; esac @@ -220,7 +224,7 @@ main_help() { main_start() { detect_osarch detect_git_tag - process_options $@ + process_options "$@" if [ "$MODE" = "help" ] ; then main_help else @@ -229,4 +233,4 @@ main_start() { } # note: only start executing commands now to guard against partial downloads -main_start $@ +main_start "$@" From 0f3d71611fc1cd927de175e22a23f0195e2820b1 Mon Sep 17 00:00:00 2001 From: Daan Date: Mon, 22 Jun 2026 14:38:48 -0700 Subject: [PATCH 06/35] apply PR #1294 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e7ee8b3d..82574287a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,7 +381,7 @@ endif() if(MI_USE_CXX) message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)") set_source_files_properties(${mi_sources} PROPERTIES LANGUAGE CXX ) - set_source_files_properties(src/static.c test/test-api.c test/test-api-fill test/test-stress PROPERTIES LANGUAGE CXX ) + set_source_files_properties(src/static.c test/test-api.c test/test-api-fill.c test/test-stress.c PROPERTIES LANGUAGE CXX ) if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang") list(APPEND mi_cflags -Wno-deprecated) endif() From 569be66f8841dbed0467a4551bf977f1445b5bb6 Mon Sep 17 00:00:00 2001 From: Daan Date: Mon, 22 Jun 2026 14:59:19 -0700 Subject: [PATCH 07/35] apply PR #1298 (by @cppcoffee) --- src/segment.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/segment.c b/src/segment.c index 7ae84616f..75bf81f69 100644 --- a/src/segment.c +++ b/src/segment.c @@ -92,9 +92,7 @@ size_t _mi_commit_mask_committed_size(const mi_commit_mask_t* cm, size_t total) count += MI_COMMIT_MASK_FIELD_BITS; } else { - for (; mask != 0; mask >>= 1) { // todo: use popcount - if ((mask&1)!=0) count++; - } + count += mi_popcount(mask); } } // we use total since for huge segments each commit bit may represent a larger size From e8e1e77bb669b12b45ee636c728e7a7eef2830ab Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Fri, 5 Jun 2026 11:00:59 +0800 Subject: [PATCH 08/35] add riscv64 TLS support --- include/mimalloc/prim.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/mimalloc/prim.h b/include/mimalloc/prim.h index 51b5c9a6f..c17236163 100644 --- a/include/mimalloc/prim.h +++ b/include/mimalloc/prim.h @@ -141,7 +141,7 @@ void _mi_prim_thread_associate_default_heap(mi_heap_t* heap); // but unfortunately we can not detect support reliably (see issue #883) // We also use it on Apple OS as we use a TLS slot for the default heap there. #if defined(__GNUC__) && ( \ - (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__))) \ + (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) || defined(__riscv)) \ || (defined(__APPLE__) && (defined(__x86_64__) || defined(__aarch64__) || defined(__POWERPC__))) \ || (defined(__BIONIC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__))) \ || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ @@ -173,6 +173,10 @@ static inline void* mi_prim_tls_slot(size_t slot) mi_attr_noexcept { __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); #endif res = tcb[slot]; + #elif defined(__riscv) + void** tcb; MI_UNUSED(ofs); + __asm__ volatile ("mv %0, tp" : "=r" (tcb)); + res = tcb[slot]; #elif defined(__APPLE__) && defined(__POWERPC__) // ppc, issue #781 MI_UNUSED(ofs); res = pthread_getspecific(slot); @@ -203,6 +207,10 @@ static inline void mi_prim_tls_slot_set(size_t slot, void* value) mi_attr_noexce __asm__ volatile ("mrs %0, tpidr_el0" : "=r" (tcb)); #endif tcb[slot] = value; + #elif defined(__riscv) + void** tcb; MI_UNUSED(ofs); + __asm__ volatile ("mv %0, tp" : "=r" (tcb)); + tcb[slot] = value; #elif defined(__APPLE__) && defined(__POWERPC__) // ppc, issue #781 MI_UNUSED(ofs); pthread_setspecific(slot, value); @@ -265,6 +273,7 @@ static inline void mi_prim_tls_slot_set(size_t slot, void* value) mi_attr_noexce && !defined(MI_LIBC_MUSL) \ && (!defined(__clang_major__) || __clang_major__ >= 14) /* older clang versions emit bad code; fall back to using the TLS slot () */ #if (defined(__GNUC__) && (__GNUC__ >= 7) && defined(__aarch64__)) /* aarch64 for older gcc versions (issue #851) */ \ + || (defined(__GNUC__) && (__GNUC__ >= 7) && defined(__riscv)) \ || (defined(__GNUC__) && (__GNUC__ >= 11) && defined(__x86_64__)) \ || (defined(__clang_major__) && (__clang_major__ >= 14) && (defined(__aarch64__) || defined(__x86_64__))) #define MI_USE_BUILTIN_THREAD_POINTER 1 From 021bc3a66b8ea2734370b75041bc4e727dfcd452 Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Fri, 5 Jun 2026 11:00:59 +0800 Subject: [PATCH 09/35] add riscv64 atomic yield (pause with Zihintpause fallback) --- include/mimalloc/atomic.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/mimalloc/atomic.h b/include/mimalloc/atomic.h index 5ac8da6f9..fd9507c06 100644 --- a/include/mimalloc/atomic.h +++ b/include/mimalloc/atomic.h @@ -416,7 +416,8 @@ static inline void mi_atomic_yield(void) { #elif (defined(__GNUC__) || defined(__clang__)) && \ (defined(__x86_64__) || defined(__i386__) || \ defined(__aarch64__) || defined(__arm__) || \ - defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__)) + defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__) || \ + defined(__riscv)) #if defined(__x86_64__) || defined(__i386__) static inline void mi_atomic_yield(void) { __asm__ volatile ("pause" ::: "memory"); @@ -445,6 +446,16 @@ static inline void mi_atomic_yield(void) { __asm__ __volatile__ ("or 27,27,27" ::: "memory"); } #endif +#elif defined(__riscv) +#if defined(__riscv_zihintpause) +static inline void mi_atomic_yield(void) { + __asm__ volatile("pause" ::: "memory"); +} +#else +static inline void mi_atomic_yield(void) { + __asm__ volatile("nop" ::: "memory"); +} +#endif #endif #elif defined(__sun) #include From 580c50391e04b73d0336cf5c64de90e9ba0dd793 Mon Sep 17 00:00:00 2001 From: Daan Date: Tue, 23 Jun 2026 14:12:52 -0700 Subject: [PATCH 10/35] only use delayed_free_all on abandonment, but otherwise prefer delayed_free_partial, see issue #1317 --- src/heap.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/heap.c b/src/heap.c index 84a26a55d..a46bd939a 100644 --- a/src/heap.c +++ b/src/heap.c @@ -149,7 +149,11 @@ static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect) // free all current thread delayed blocks. // (if abandoning, after this there are no more thread-delayed references into the pages.) - _mi_heap_delayed_free_all(heap); + if (collect >= MI_ABANDON) { + _mi_heap_delayed_free_all(heap); // free all + } else { + _mi_heap_delayed_free_partial(heap); // best-effort; contended blocks are retried next time + } // collect retired pages _mi_heap_collect_retired(heap, force); From 92854277385e2fe924313c361eef03a39db3b0e2 Mon Sep 17 00:00:00 2001 From: Daan Date: Tue, 23 Jun 2026 14:34:03 -0700 Subject: [PATCH 11/35] add backoff to atomic_yield with sleep(0) calls, see also issue #1317 --- include/mimalloc/atomic.h | 29 ++++++++++++++++++++++++++++- src/page.c | 6 ++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/include/mimalloc/atomic.h b/include/mimalloc/atomic.h index 5ac8da6f9..18a5c82a3 100644 --- a/include/mimalloc/atomic.h +++ b/include/mimalloc/atomic.h @@ -469,6 +469,32 @@ static inline void mi_atomic_yield(void) { } #endif +#if defined(_WIN32) +static inline void mi_sleep0(void) { + Sleep(0); +} +#else +#include +static inline void mi_sleep0(void) { + sleep(0); +} +#endif + +static inline void mi_atomic_yield_sleep( size_t* ticks, const size_t ticks_until_sleep ) { + const size_t n = *ticks; + if (n==0 || n > ticks_until_sleep) { + *ticks = ticks_until_sleep; // reset + } + else { + *ticks = n-1; + } + if (n>1) { + mi_atomic_yield(); + } + else { + mi_sleep0(); + } +} // ---------------------------------------------------------------------- // Locks @@ -589,9 +615,10 @@ static inline bool mi_lock_try_acquire(mi_lock_t* lock) { return mi_atomic_cas_strong_acq_rel(&lock->mutex, &expected, (uintptr_t)1); } static inline void mi_lock_acquire(mi_lock_t* lock) { + size_t ticks = 0; for (int i = 0; i < 10000; i++) { // for at most 10000 tries? if (mi_lock_try_acquire(lock)) return; - mi_atomic_yield(); + mi_atomic_yield_sleep(&ticks,100); } _mi_error_message(EFAULT, "internal error: lock cannot be acquired (due to lack of native lock primitives)\n"); } diff --git a/src/page.c b/src/page.c index 10b85e6db..8da504447 100644 --- a/src/page.c +++ b/src/page.c @@ -139,8 +139,9 @@ bool _mi_page_is_valid(mi_page_t* page) { #endif void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) { + size_t ticks = 0; while (!_mi_page_try_use_delayed_free(page, delay, override_never)) { - mi_atomic_yield(); + mi_atomic_yield_sleep(&ticks,10); } } @@ -318,8 +319,9 @@ static mi_page_t* mi_page_fresh(mi_heap_t* heap, mi_page_queue_t* pq) { (put there by other threads if they deallocated in a full page) ----------------------------------------------------------- */ void _mi_heap_delayed_free_all(mi_heap_t* heap) { + size_t ticks = 0; while (!_mi_heap_delayed_free_partial(heap)) { - mi_atomic_yield(); + mi_atomic_yield_sleep(&ticks,10); } } From 84243a748ae2f9d5f222a69bad24b5ee11a6f5f2 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 7 Jul 2026 14:48:55 -0700 Subject: [PATCH 12/35] fix format specifier for memid (pr #1321) --- src/arena.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arena.c b/src/arena.c index 17c03afb8..8d94a0128 100644 --- a/src/arena.c +++ b/src/arena.c @@ -698,12 +698,12 @@ void _mi_arena_free(void* p, size_t size, size_t committed_size, mi_memid_t memi // checks if (arena == NULL) { - _mi_error_message(EINVAL, "trying to free from an invalid arena: %p, size %zu, memid: 0x%zx\n", p, size, memid); + _mi_error_message(EINVAL, "trying to free from an invalid arena: %p, size %zu, memkind: 0x%x\n", p, size, memid.memkind); return; } mi_assert_internal(arena->field_count > mi_bitmap_index_field(bitmap_idx)); if (arena->field_count <= mi_bitmap_index_field(bitmap_idx)) { - _mi_error_message(EINVAL, "trying to free from an invalid arena block: %p, size %zu, memid: 0x%zx\n", p, size, memid); + _mi_error_message(EINVAL, "trying to free from an invalid arena block: %p, size %zu, memid: 0x%x\n", p, size, memid.memkind); return; } From f5b637a3b27c4f469da9b4f795f8f65207088e61 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 7 Jul 2026 14:50:49 -0700 Subject: [PATCH 13/35] apply format specifier fix (pr #1321 by @StanFromIreland) --- src/prim/unix/prim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index 1c08aa73a..bc0e20675 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -566,7 +566,7 @@ int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bo long err = mi_prim_mbind(*addr, size, MPOL_PREFERRED, &numa_mask, 8*MI_INTPTR_SIZE, 0); if (err != 0) { err = errno; - _mi_warning_message("failed to bind huge (1GiB) pages to numa node %d (error: %d (0x%x))\n", numa_node, err, err); + _mi_warning_message("failed to bind huge (1GiB) pages to numa node %d (error: %ld (0x%lx))\n", numa_node, err, err); } } return (*addr != NULL ? 0 : errno); From 414c8c4ba7dbd283fb1606adf533be28d96022b2 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 7 Jul 2026 14:58:30 -0700 Subject: [PATCH 14/35] fix missing parenthesis (pr #1319) --- include/mimalloc/prim.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mimalloc/prim.h b/include/mimalloc/prim.h index c17236163..51ef18cc2 100644 --- a/include/mimalloc/prim.h +++ b/include/mimalloc/prim.h @@ -141,7 +141,7 @@ void _mi_prim_thread_associate_default_heap(mi_heap_t* heap); // but unfortunately we can not detect support reliably (see issue #883) // We also use it on Apple OS as we use a TLS slot for the default heap there. #if defined(__GNUC__) && ( \ - (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) || defined(__riscv)) \ + (defined(__GLIBC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) || defined(__riscv))) \ || (defined(__APPLE__) && (defined(__x86_64__) || defined(__aarch64__) || defined(__POWERPC__))) \ || (defined(__BIONIC__) && (defined(__x86_64__) || defined(__i386__) || (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__))) \ || (defined(__FreeBSD__) && (defined(__x86_64__) || defined(__i386__) || defined(__aarch64__))) \ From aed99d3aadbab6aa5f30d6e1e2d83aa84b6048e3 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 10 Jul 2026 08:00:32 +0900 Subject: [PATCH 15/35] Advise MADV_HUGEPAGE independently of allow_large The THP advise was gated on the caller's allow_large parameter, which callers derive from segment laziness and which primarily exists to gate MAP_HUGETLB. hugetlbfs pages are physically committed at map time, so keeping them away from lazily-committed segments makes sense, but transparent huge pages materialize on fault and have no such cost. As a result of sharing the gate, huge objects (the OS singleton path) and each thread's first segment were never eligible for THP under Linux's madvise THP mode even with allow_thp enabled. With this change the advise depends only on the allow_thp option, matching the effective behavior of the dev3 branch, where singleton pages are committed eagerly and allow_large no longer depends on laziness. In the mold linker, whose largest data structures allocate through the huge-object path, this makes linking a Chromium debug build about 2% faster (3.37s to 3.29s) on a 64-core machine, and 15% faster (4.05s to 3.43s) when mold runs in its single-process mode, where the process exit cost is user-visible and freeing a heap of 2 MiB folios is much cheaper than freeing millions of 4 KiB folios. Co-Authored-By: Claude Fable 5 --- src/prim/unix/prim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index 358678187..2ab0cd73b 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -389,7 +389,7 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec *is_large = false; p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, flags, fd); #if !defined(MI_NO_THP) - if (p != NULL && allow_large && mi_option_is_enabled(mi_option_allow_thp) && _mi_os_canuse_large_page(size, try_alignment)) { + if (p != NULL && mi_option_is_enabled(mi_option_allow_thp) && _mi_os_canuse_large_page(size, try_alignment)) { #if defined(MADV_HUGEPAGE) // Many Linux systems don't allow MAP_HUGETLB but they support instead // transparent huge pages (THP). Generally, it is not required to call `madvise` with MADV_HUGE From acea8bcb71d3f35666e32b3b3d78544496200d7c Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 16:08:37 -0700 Subject: [PATCH 16/35] improve _mi_strnlen, issue #1271, issue 27 --- src/libc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libc.c b/src/libc.c index 197a27228..41903cd08 100644 --- a/src/libc.c +++ b/src/libc.c @@ -64,7 +64,7 @@ void _mi_strlcat(char* dest, const char* src, size_t dest_size) { size_t _mi_strnlen(const char* s, size_t max_len) { if (s==NULL) return 0; size_t len = 0; - while(s[len] != 0 && len < max_len) { len++; } + while(len < max_len && s[len] != 0) { len++; } return len; } From 3e21fef04daae11a5bcd07e42fe1c7a5f1cb48ef Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 15:29:33 -0700 Subject: [PATCH 17/35] check error on clock_gettime, #1271 issue 15 --- src/prim/unix/prim.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index bc0e20675..a64da92c4 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -665,22 +665,8 @@ size_t _mi_prim_numa_node_count(void) { #include -#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) - -mi_msecs_t _mi_prim_clock_now(void) { - struct timespec t; - #ifdef CLOCK_MONOTONIC - clock_gettime(CLOCK_MONOTONIC, &t); - #else - clock_gettime(CLOCK_REALTIME, &t); - #endif - return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); -} - -#else - // low resolution timer -mi_msecs_t _mi_prim_clock_now(void) { +static mi_msecs_t mi_prim_clock_now_lowres(void) { #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) return (mi_msecs_t)clock(); #elif (CLOCKS_PER_SEC < 1000) @@ -690,9 +676,20 @@ mi_msecs_t _mi_prim_clock_now(void) { #endif } -#endif - - +mi_msecs_t _mi_prim_clock_now(void) { + #if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) + #ifdef CLOCK_MONOTONIC + const clockid_t clockid = CLOCK_MONOTONIC; + #else + const clockid_t clockid = CLOCK_REALTIME; + #endif + struct timespec t; + if (clock_gettime(clockid,&t) == 0) { + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); + } + #endif + return mi_prim_clock_now_lowres(); +} //---------------------------------------------------------------- From cafd35116010d6177374815fb3fd445ec405157e Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 15:30:53 -0700 Subject: [PATCH 18/35] check error on clock_gettime for Wasi, #1271 issue 15 --- src/prim/wasi/prim.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/prim/wasi/prim.c b/src/prim/wasi/prim.c index 0719f12cb..0092ebe44 100644 --- a/src/prim/wasi/prim.c +++ b/src/prim/wasi/prim.c @@ -186,22 +186,8 @@ size_t _mi_prim_numa_node_count(void) { #include -#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) - -mi_msecs_t _mi_prim_clock_now(void) { - struct timespec t; - #ifdef CLOCK_MONOTONIC - clock_gettime(CLOCK_MONOTONIC, &t); - #else - clock_gettime(CLOCK_REALTIME, &t); - #endif - return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); -} - -#else - // low resolution timer -mi_msecs_t _mi_prim_clock_now(void) { +static mi_msecs_t mi_prim_clock_now_lowres(void) { #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) return (mi_msecs_t)clock(); #elif (CLOCKS_PER_SEC < 1000) @@ -211,7 +197,20 @@ mi_msecs_t _mi_prim_clock_now(void) { #endif } -#endif +mi_msecs_t _mi_prim_clock_now(void) { + #if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC) + #ifdef CLOCK_MONOTONIC + const clockid_t clockid = CLOCK_MONOTONIC; + #else + const clockid_t clockid = CLOCK_REALTIME; + #endif + struct timespec t; + if (clock_gettime(clockid,&t) == 0) { + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); + } + #endif + return mi_prim_clock_now_lowres(); +} //---------------------------------------------------------------- From 60173ff6ccc23a97ec2f4bf749603be325cdcc5d Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 15:40:33 -0700 Subject: [PATCH 19/35] check error on clock_gettime for Wasi and unix, #1271 issue 15 --- src/prim/unix/prim.c | 19 +++++++++++++------ src/prim/wasi/prim.c | 19 +++++++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index a64da92c4..7598b8994 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -667,12 +667,19 @@ size_t _mi_prim_numa_node_count(void) { // low resolution timer static mi_msecs_t mi_prim_clock_now_lowres(void) { - #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) - return (mi_msecs_t)clock(); - #elif (CLOCKS_PER_SEC < 1000) - return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + const int64_t ticks = (int64_t)clock(); + #if !defined(CLOCKS_PER_SEC) + return ticks; #else - return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + if (CLOCKS_PER_SEC <= 0 || CLOCKS_PER_SEC == 1000) { + return ticks; + } + else if (CLOCKS_PER_SEC > 0 && CLOCKS_PER_SEC < 1000) { + return ticks * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + } + else { + return ticks / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + } #endif } @@ -685,7 +692,7 @@ mi_msecs_t _mi_prim_clock_now(void) { #endif struct timespec t; if (clock_gettime(clockid,&t) == 0) { - return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000L); } #endif return mi_prim_clock_now_lowres(); diff --git a/src/prim/wasi/prim.c b/src/prim/wasi/prim.c index 0092ebe44..2d19846d8 100644 --- a/src/prim/wasi/prim.c +++ b/src/prim/wasi/prim.c @@ -188,12 +188,19 @@ size_t _mi_prim_numa_node_count(void) { // low resolution timer static mi_msecs_t mi_prim_clock_now_lowres(void) { - #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0) - return (mi_msecs_t)clock(); - #elif (CLOCKS_PER_SEC < 1000) - return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + const int64_t ticks = (int64_t)clock(); + #if !defined(CLOCKS_PER_SEC) + return ticks; #else - return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + if (CLOCKS_PER_SEC <= 0 || CLOCKS_PER_SEC == 1000) { + return ticks; + } + else if (CLOCKS_PER_SEC > 0 && CLOCKS_PER_SEC < 1000) { + return ticks * (1000 / (mi_msecs_t)CLOCKS_PER_SEC); + } + else { + return ticks / ((mi_msecs_t)CLOCKS_PER_SEC / 1000); + } #endif } @@ -206,7 +213,7 @@ mi_msecs_t _mi_prim_clock_now(void) { #endif struct timespec t; if (clock_gettime(clockid,&t) == 0) { - return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000); + return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000L); } #endif return mi_prim_clock_now_lowres(); From f4a927bc65197fd4c590cd08d57bf303a779c900 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 16:11:58 -0700 Subject: [PATCH 20/35] adjust mi_good_size for potential padding size, #1271, issue 18 --- src/page-queue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/page-queue.c b/src/page-queue.c index fb9da3482..3d7c1b709 100644 --- a/src/page-queue.c +++ b/src/page-queue.c @@ -107,10 +107,10 @@ size_t _mi_bin_size(size_t bin) { // Good size for allocation size_t mi_good_size(size_t size) mi_attr_noexcept { - if (size <= MI_LARGE_OBJ_SIZE_MAX) { + if (size <= MI_LARGE_OBJ_SIZE_MAX - MI_PADDING_SIZE) { return _mi_bin_size(mi_bin(size + MI_PADDING_SIZE)); } - else if (size <= MI_MAX_ALLOC_SIZE) { + else if (size <= MI_MAX_ALLOC_SIZE - MI_PADDING_SIZE) { return _mi_align_up(size + MI_PADDING_SIZE,_mi_os_page_size()); } else { From 2caaefddb778e80535df842057867e671e2c756e Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 16:22:34 -0700 Subject: [PATCH 21/35] fix missing semi-colon, issue #1271, issue 31 --- src/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/init.c b/src/init.c index 538e45a46..9cd18d4c4 100644 --- a/src/init.c +++ b/src/init.c @@ -678,7 +678,7 @@ void mi_cdecl mi_process_done(void) mi_attr_noexcept { #endif // done with tracking tools - mi_track_done() + mi_track_done(); // Forcefully release all retained memory; this can be dangerous in general if overriding regular malloc/free // since after process_done there might still be other code running that calls `free` (like at_exit routines, From 885c40855a34330166001fbd810ac6c7bb831953 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 17:01:14 -0700 Subject: [PATCH 22/35] check pthread key creation for error, issue #1271, issue 13 --- src/prim/unix/prim.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index 7598b8994..de59d8080 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -938,7 +938,11 @@ static void mi_pthread_done(void* value) { void _mi_prim_thread_init_auto_done(void) { mi_assert_internal(_mi_heap_default_key == (pthread_key_t)(-1)); - pthread_key_create(&_mi_heap_default_key, &mi_pthread_done); + const int err = pthread_key_create(&_mi_heap_default_key, &mi_pthread_done); + if (err!=0) { + _mi_error_message(err,"unable to create a pthread thread local key (error %d (0x%x))", err, err); + _mi_heap_default_key = (pthread_key_t)(-1); + }; } void _mi_prim_thread_done_auto_done(void) { From c4c3f75f47ffd63595209616e0e07510db46daf3 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 17:05:27 -0700 Subject: [PATCH 23/35] use atomic for huge 1Gib page available flag, issue #1271, issue 14 --- src/prim/unix/prim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index de59d8080..1da58daba 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -349,8 +349,8 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec lflags |= MAP_HUGETLB; #endif #ifdef MAP_HUGE_1GB - static bool mi_huge_pages_available = true; - if (large_only && (size % MI_GiB) == 0 && mi_huge_pages_available) { + static _Atomic(size_t) mi_huge_1gib_pages_unavailable; + if (large_only && (size % MI_GiB) == 0 && (mi_atomic_load_relaxed(&mi_huge_1gib_pages_unavailable)==0)) { lflags |= MAP_HUGE_1GB; } else @@ -369,7 +369,7 @@ static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protec p = unix_mmap_prim_aligned(addr, size, try_alignment, protect_flags, lflags, lfd); #ifdef MAP_HUGE_1GB if (p == NULL && (lflags & MAP_HUGE_1GB) == MAP_HUGE_1GB) { - mi_huge_pages_available = false; // don't try huge 1GiB pages again + mi_atomic_store_relaxed(&mi_huge_1gib_pages_unavailable,1); // don't try huge 1GiB pages again if (large_only) { _mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (errno: %i)\n", errno); } From 27037dfbc5812ece3d558a3dd59feb05b5a25d39 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 17:09:59 -0700 Subject: [PATCH 24/35] ensure process_done only runs once, #1271 issue 12 --- src/init.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/init.c b/src/init.c index 9cd18d4c4..207e338c9 100644 --- a/src/init.c +++ b/src/init.c @@ -651,8 +651,9 @@ void mi_process_init(void) mi_attr_noexcept { } } -// Called when the process is done (cdecl as it is used with `at_exit` on some platforms) -void mi_cdecl mi_process_done(void) mi_attr_noexcept { + +// Called when the process is done +static void mi_process_done_once(void) { // only shutdown if we were initialized if (!_mi_process_is_initialized) return; // ensure we are called once @@ -698,6 +699,14 @@ void mi_cdecl mi_process_done(void) mi_attr_noexcept { os_preloading = true; // don't call the C runtime anymore } + +// Called when the process is done (cdecl as it is used with `at_exit` on some platforms) +void mi_cdecl mi_process_done(void) mi_attr_noexcept { + mi_atomic_do_once { + mi_process_done_once(); + } +} + void mi_cdecl _mi_auto_process_done(void) mi_attr_noexcept { if (_mi_option_get_fast(mi_option_destroy_on_exit)>1) return; mi_process_done(); From 93ec401b289ab2157b1b3abcf027845f758eb669 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 17:59:55 -0700 Subject: [PATCH 25/35] update macos zone management to reflect dev3 changes --- src/prim/osx/alloc-override-zone.c | 43 +++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/prim/osx/alloc-override-zone.c b/src/prim/osx/alloc-override-zone.c index 543d85e64..0aa6cc7d2 100644 --- a/src/prim/osx/alloc-override-zone.c +++ b/src/prim/osx/alloc-override-zone.c @@ -41,10 +41,19 @@ extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_im malloc zone members ------------------------------------------------------ */ +static bool is_mimalloc_zone( malloc_zone_t* zone ); + static size_t zone_size(malloc_zone_t* zone, const void* p) { MI_UNUSED(zone); - if (!mi_is_in_heap_region(p)){ return 0; } // not our pointer, bail out - return mi_usable_size(p); + if (mi_is_in_heap_region(p)) { + return mi_usable_size(p); + } + else if (!is_mimalloc_zone(zone)) { // can happen due to interpose + return zone->size(zone,p); + } + else { + return 0; + } } static void* zone_malloc(malloc_zone_t* zone, size_t size) { @@ -63,13 +72,24 @@ static void* zone_valloc(malloc_zone_t* zone, size_t size) { } static void zone_free(malloc_zone_t* zone, void* p) { - MI_UNUSED(zone); - mi_cfree(p); + if (mi_is_in_heap_region(p)) { + mi_free(p); + } + else if (!is_mimalloc_zone(zone)) { // can happen due to interpose + zone->free(zone,p); + } } static void* zone_realloc(malloc_zone_t* zone, void* p, size_t newsize) { - MI_UNUSED(zone); - return mi_realloc(p, newsize); + if (p == NULL || mi_is_in_heap_region(p)) { + return mi_realloc(p, newsize); + } + else if (!is_mimalloc_zone(zone)) { // can happen due to interpose + return zone->realloc(zone,p,newsize); + } + else { + return NULL; + } } static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) { @@ -78,8 +98,9 @@ static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) { } static void zone_destroy(malloc_zone_t* zone) { - MI_UNUSED(zone); - // todo: ignore for now? + if (!is_mimalloc_zone(zone)) { + zone->destroy(zone); + } } static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size, void** ps, unsigned count) { @@ -114,7 +135,6 @@ static boolean_t zone_claimed_address(malloc_zone_t* zone, void* p) { return mi_is_in_heap_region(p); } - /* ------------------------------------------------------ Introspection members ------------------------------------------------------ */ @@ -245,6 +265,11 @@ static malloc_zone_t mi_malloc_zone = { #endif }; +static bool is_mimalloc_zone( malloc_zone_t* zone ) { + return (zone==NULL || zone==&mi_malloc_zone); +} + + #ifdef __cplusplus } #endif From 0f1380414c3f2de454cb702a90477e38e34cb2e7 Mon Sep 17 00:00:00 2001 From: daanx Date: Mon, 13 Jul 2026 18:04:11 -0700 Subject: [PATCH 26/35] disable dynamic override test in C++ on macos (for dev and dev2) --- .github/workflows/test.yaml | 2 +- CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 02dd0fa53..58fd50bd5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -59,7 +59,7 @@ jobs: ctest --test-dir out/debug-cxx --verbose --timeout 240 -C Debug - name: Release, C++ - if: matrix.tests == 'basic' && runner.os == 'Linux' # windows already uses C++ by default; macOS interpose does not work well with C++ + if: matrix.tests == 'basic' && runner.os != 'Windows' # windows already uses C++ by default run: | cmake . -B out/release-cxx -DCMAKE_BUILD_TYPE=Release -DMI_OPT_ARCH=ON -DMI_USE_CXX=ON cmake --build out/release-cxx --parallel 8 --config Release diff --git a/CMakeLists.txt b/CMakeLists.txt index 82574287a..1dc1e782c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -762,7 +762,7 @@ if (MI_BUILD_TESTS) endforeach() # dynamic override test - if(MI_BUILD_SHARED AND NOT (MI_TRACK_ASAN OR MI_DEBUG_TSAN OR MI_DEBUG_UBSAN)) + if(MI_BUILD_SHARED AND NOT (MI_TRACK_ASAN OR MI_DEBUG_TSAN OR MI_DEBUG_UBSAN) AND NOT (APPLE AND MI_USE_CXX)) add_executable(mimalloc-test-stress-dynamic test/test-stress.c) target_compile_definitions(mimalloc-test-stress-dynamic PRIVATE ${mi_defines} "USE_STD_MALLOC=1") target_compile_options(mimalloc-test-stress-dynamic PRIVATE ${mi_cflags}) From 82c1688674872347ed5c6faab8298af72c53078d Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 09:46:55 -0700 Subject: [PATCH 27/35] limit size for hint address only in secure mode, issue #1290 --- src/os.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/os.c b/src/os.c index 29d2ee658..0c84ef423 100644 --- a/src/os.c +++ b/src/os.c @@ -120,7 +120,7 @@ static mi_decl_cache_align _Atomic(uintptr_t)aligned_base; // Return a MI_SEGMENT_SIZE aligned address that is probably available. // If this returns NULL, the OS will determine the address but on some OS's that may not be // properly aligned which can be more costly as it needs to be adjusted afterwards. -// For a size > 1GiB this always returns NULL in order to guarantee good ASLR randomization; +// In secure mode, for a size > 1GiB this always returns NULL in order to guarantee good ASLR randomization. // (otherwise an initial large allocation of say 2TiB has a 50% chance to include (known) addresses // in the middle of the 2TiB - 6TiB address range (see issue #372)) @@ -133,7 +133,9 @@ void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size) if (try_alignment <= 1 || try_alignment > MI_SEGMENT_SIZE) return NULL; if (mi_os_mem_config.virtual_address_bits < 46) return NULL; // < 64TiB virtual address space size = _mi_align_up(size, MI_SEGMENT_SIZE); + #if (MI_SECURE>0) if (size > 1*MI_GiB) return NULL; // guarantee the chance of fixed valid address is at most 1/(MI_HINT_AREA / 1<<30) = 1/4096. + #endif size += MI_SEGMENT_SIZE; // put in `MI_SEGMENT_SIZE` virtual gaps between hinted blocks; this splits VLA's but increases guarded areas. uintptr_t hint = mi_atomic_add_acq_rel(&aligned_base, size); From 0748322e1fad6a38b77109087b41676927f93e8a Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 10:18:47 -0700 Subject: [PATCH 28/35] remove mi_attr_no_except from mi_posix_memalign (issue #794) --- include/mimalloc.h | 2 +- src/alloc-posix.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mimalloc.h b/include/mimalloc.h index 6522c9ebc..609c55660 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -466,7 +466,7 @@ mi_decl_nodiscard mi_decl_export size_t mi_malloc_size(const void* p) mi_ mi_decl_nodiscard mi_decl_export size_t mi_malloc_good_size(size_t size) mi_attr_noexcept; mi_decl_nodiscard mi_decl_export size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept; -mi_decl_export int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept; +mi_decl_export int mi_posix_memalign(void** p, size_t alignment, size_t size); // mi_attr_noexcept; mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(2) mi_attr_alloc_align(1); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_valloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); diff --git a/src/alloc-posix.c b/src/alloc-posix.c index 60639ff49..41135f38f 100644 --- a/src/alloc-posix.c +++ b/src/alloc-posix.c @@ -52,7 +52,7 @@ void mi_cfree(void* p) mi_attr_noexcept { } } -int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept { +int mi_posix_memalign(void** p, size_t alignment, size_t size) { // mi_attr_noexcept (issue #794) // Note: The spec dictates we should not modify `*p` on an error. (issue#27) // if (p == NULL) return EINVAL; From 6d4e8dc55e34c7b186c4ec25dae6476a9c9565f2 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 11:33:18 -0700 Subject: [PATCH 29/35] bump version to v1.9.11 --- cmake/mimalloc-config-version.cmake | 2 +- contrib/vcpkg/portfile.cmake | 2 +- contrib/vcpkg/vcpkg.json | 2 +- include/mimalloc.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/mimalloc-config-version.cmake b/cmake/mimalloc-config-version.cmake index 96c9f2c90..2f57e5a63 100644 --- a/cmake/mimalloc-config-version.cmake +++ b/cmake/mimalloc-config-version.cmake @@ -1,6 +1,6 @@ set(mi_version_major 1) set(mi_version_minor 9) -set(mi_version_patch 10) +set(mi_version_patch 11) set(mi_version ${mi_version_major}.${mi_version_minor}) set(PACKAGE_VERSION ${mi_version}) diff --git a/contrib/vcpkg/portfile.cmake b/contrib/vcpkg/portfile.cmake index 91249f0bb..f8b3b657c 100644 --- a/contrib/vcpkg/portfile.cmake +++ b/contrib/vcpkg/portfile.cmake @@ -3,7 +3,7 @@ vcpkg_from_github( REPO microsoft/mimalloc HEAD_REF master - # The "REF" can be a commit hash, branch name (dev3), or a version (v3.3.2). + # The "REF" can be a commit hash, branch name (dev3), or a version (v3.4.0). REF "v${VERSION}" # REF 866ce5b89db1dbc3e66bbf89041291fd16329518 diff --git a/contrib/vcpkg/vcpkg.json b/contrib/vcpkg/vcpkg.json index 84ae68f32..95ae18d2a 100644 --- a/contrib/vcpkg/vcpkg.json +++ b/contrib/vcpkg/vcpkg.json @@ -1,6 +1,6 @@ { "name": "mimalloc", - "version": "3.3.2", + "version": "3.4.0", "port-version": 0, "description": "Compact general purpose allocator with excellent performance", "homepage": "https://github.com/microsoft/mimalloc", diff --git a/include/mimalloc.h b/include/mimalloc.h index 609c55660..82a99875d 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_H #define MIMALLOC_H -#define MI_MALLOC_VERSION 10910 // major + 2 digits minor + 2 digits patch +#define MI_MALLOC_VERSION 10911 // major + 2 digits minor + 2 digits patch // ------------------------------------------------------ // Compiler specific attributes From 37ae20e2b465746758254e13759c8ab47fb0a1f7 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 11:34:16 -0700 Subject: [PATCH 30/35] bump release version to v3.4.0 --- .github/workflows/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 10edaea33..7a6b07c82 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,7 +9,7 @@ permissions: contents: write env: - RELEASE: Release v3.3.2 + RELEASE: Release v3.4.0 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true name: Release @@ -19,7 +19,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - branch: [v1.9.10,v2.3.2,v3.3.2] # [dev,dev2,dev3] + branch: [v1.9.11,v2.4.0,v3.4.0] # [dev,dev2,dev3] # we build on the oldest ubuntu version for better binary compatibility. os: [windows-latest, macOS-latest, macos-15-intel, ubuntu-22.04, ubuntu-22.04-arm] From ff11fa73452a1339ab95c8c39251d1c877041d89 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 11:46:22 -0700 Subject: [PATCH 31/35] update readme --- doc/release-notes.md | 2 +- readme.md | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index b467c1468..1e59f4a53 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -10,6 +10,6 @@ Notes: - Generally it is recommended to download sources (or use `vcpkg` etc.) and build mimalloc as part of your project. - Source releases can also be downloaded directly from github by the tag. - For example . + For example . - Binary releases include a release-, debug-, and secure build. - Linux binaries are built on Ubuntu 22. diff --git a/readme.md b/readme.md index 553041e9f..3d43dda96 100644 --- a/readme.md +++ b/readme.md @@ -15,9 +15,9 @@ is a general purpose allocator with excellent [performance](#performance) charac Initially developed by Daan Leijen for the runtime systems of the [Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages. -Latest release : `v3.3.2` (2026-04-29) recommended. -Latest v2 release: `v2.3.2` (2026-04-29) stable. -Latest v1 release: `v1.9.10` (2026-04-29) legacy. +Latest release : `v3.4.0` (2026-07-14) recommended. +Latest v2 release: `v2.4.0` (2026-07-14) stable. +Latest v1 release: `v1.9.11` (2026-07-14) legacy. mimalloc is a drop-in replacement for `malloc` and can be used in other programs without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as: @@ -88,6 +88,10 @@ New development is mostly on v3, while v1 and v2 are maintained with security an - __v1__: legacy version: initial design of mimalloc (release tags: `v1.9.x`, development branch `dev`). Send PR's against this version if possible. ### Releases +* 2026-07-14, `v1.9.11`, `v2.4.0`, `v3.4.0`: various bug and security fixes through LLM audit (by @Zoxc). + Fix issue with using OS memory instead of arenas for > 4GiB memory usage (v3), fix concurrency bug in concurrent + heap destroy (v3), detect riscV virtual address bits at runtime, add riscV TLS support, reduce spinlock waits (v2), + use TLS slots 126/127 on macOS (v3), and other small fixes. * 2026-04-29, `v1.9.10`, `v2.3.2`, `v3.3.2`: various bug and security fixes through LLM audit (by @Zoxc). Only increase minimal purge size automatically if allow_thp is set to 2. Enable large OS alignment on all platforms (fixing OS large pages on Windows). Fix accounting of committed memory on Linux/macOS. From 72b3fb2b22ff3155c6293f3214299c50b08b4475 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 17:06:11 -0700 Subject: [PATCH 32/35] update readme --- contrib/vcpkg/portfile.cmake | 2 +- contrib/vcpkg/vcpkg.json | 2 +- readme.md | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/vcpkg/portfile.cmake b/contrib/vcpkg/portfile.cmake index f8b3b657c..0732bb2c3 100644 --- a/contrib/vcpkg/portfile.cmake +++ b/contrib/vcpkg/portfile.cmake @@ -3,7 +3,7 @@ vcpkg_from_github( REPO microsoft/mimalloc HEAD_REF master - # The "REF" can be a commit hash, branch name (dev3), or a version (v3.4.0). + # The "REF" can be a commit hash, branch name (dev3), or a version (v3.4.1). REF "v${VERSION}" # REF 866ce5b89db1dbc3e66bbf89041291fd16329518 diff --git a/contrib/vcpkg/vcpkg.json b/contrib/vcpkg/vcpkg.json index 95ae18d2a..b797c18ff 100644 --- a/contrib/vcpkg/vcpkg.json +++ b/contrib/vcpkg/vcpkg.json @@ -1,6 +1,6 @@ { "name": "mimalloc", - "version": "3.4.0", + "version": "3.4.1", "port-version": 0, "description": "Compact general purpose allocator with excellent performance", "homepage": "https://github.com/microsoft/mimalloc", diff --git a/readme.md b/readme.md index 3d43dda96..374b6d48e 100644 --- a/readme.md +++ b/readme.md @@ -15,8 +15,8 @@ is a general purpose allocator with excellent [performance](#performance) charac Initially developed by Daan Leijen for the runtime systems of the [Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages. -Latest release : `v3.4.0` (2026-07-14) recommended. -Latest v2 release: `v2.4.0` (2026-07-14) stable. +Latest release : `v3.4.1` (2026-07-14) recommended. +Latest v2 release: `v2.4.1` (2026-07-14) stable. Latest v1 release: `v1.9.11` (2026-07-14) legacy. mimalloc is a drop-in replacement for `malloc` and can be used in other programs @@ -88,10 +88,10 @@ New development is mostly on v3, while v1 and v2 are maintained with security an - __v1__: legacy version: initial design of mimalloc (release tags: `v1.9.x`, development branch `dev`). Send PR's against this version if possible. ### Releases -* 2026-07-14, `v1.9.11`, `v2.4.0`, `v3.4.0`: various bug and security fixes through LLM audit (by @Zoxc). +* 2026-07-14, `v1.9.11`, `v2.4.1`, `v3.4.1`: various bug and security fixes through LLM audit (by @Zoxc). Fix issue with using OS memory instead of arenas for > 4GiB memory usage (v3), fix concurrency bug in concurrent heap destroy (v3), detect riscV virtual address bits at runtime, add riscV TLS support, reduce spinlock waits (v2), - use TLS slots 126/127 on macOS (v3), and other small fixes. + use TLS slots 126/127 on macOS (v3), and other small fixes. All metadata is now separated from heap objects in v3. * 2026-04-29, `v1.9.10`, `v2.3.2`, `v3.3.2`: various bug and security fixes through LLM audit (by @Zoxc). Only increase minimal purge size automatically if allow_thp is set to 2. Enable large OS alignment on all platforms (fixing OS large pages on Windows). Fix accounting of committed memory on Linux/macOS. From e39ada5eacbf6b5048022a140d480619982acc95 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 17:08:59 -0700 Subject: [PATCH 33/35] bump version to v2.4.1 --- cmake/mimalloc-config-version.cmake | 2 +- include/mimalloc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/mimalloc-config-version.cmake b/cmake/mimalloc-config-version.cmake index b140de575..261112af1 100644 --- a/cmake/mimalloc-config-version.cmake +++ b/cmake/mimalloc-config-version.cmake @@ -1,6 +1,6 @@ set(mi_version_major 2) set(mi_version_minor 4) -set(mi_version_patch 0) +set(mi_version_patch 1) set(mi_version ${mi_version_major}.${mi_version_minor}) set(PACKAGE_VERSION ${mi_version}) diff --git a/include/mimalloc.h b/include/mimalloc.h index ebe4f3095..aca8bd387 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_H #define MIMALLOC_H -#define MI_MALLOC_VERSION 20400 // major + 2 digits minor + 2 digits patch +#define MI_MALLOC_VERSION 20401 // major + 2 digits minor + 2 digits patch // ------------------------------------------------------ // Compiler specific attributes From e8e8306276960850b40fc0e2fd7b439ca4c57733 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 17:09:38 -0700 Subject: [PATCH 34/35] prepare release 3.4.1 --- .github/workflows/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7a6b07c82..fd884a01f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,7 +9,7 @@ permissions: contents: write env: - RELEASE: Release v3.4.0 + RELEASE: Release v3.4.1 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true name: Release @@ -19,7 +19,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - branch: [v1.9.11,v2.4.0,v3.4.0] # [dev,dev2,dev3] + branch: [v1.9.11,v2.4.1,v3.4.1] # [dev,dev2,dev3] # we build on the oldest ubuntu version for better binary compatibility. os: [windows-latest, macOS-latest, macos-15-intel, ubuntu-22.04, ubuntu-22.04-arm] From 578da0104e0621819c535b1f36eb93e0dc2b1032 Mon Sep 17 00:00:00 2001 From: daanx Date: Tue, 14 Jul 2026 17:36:02 -0700 Subject: [PATCH 35/35] update to softprops/action-gh-release v3 --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index fd884a01f..db0b7ca3f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -45,7 +45,7 @@ jobs: # Create a release: https://github.com/softprops/action-gh-release (MIT license) - name: Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 if: ${{ env.RELEASE != '' && env.RELEASE != 'no' }} with: name: ${{ env.RELEASE }}