From 6e1ca96a4965c776c10698c24dae576523178ef5 Mon Sep 17 00:00:00 2001 From: Daan Date: Mon, 17 Feb 2020 10:19:29 -0800 Subject: [PATCH 1/9] Update readme.md --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index e4e96ba7..423c91b9 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ mimalloc (pronounced "me-malloc") is a general purpose allocator with excellent [performance](#performance) characteristics. Initially developed by Daan Leijen for the run-time systems of the [Koka](https://github.com/koka-lang/koka) and [Lean](https://github.com/leanprover/lean) languages. -Latest release:`v1.6.0` (2020-02-09). +Latest release:`v1.6.1` (2020-02-17). It 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: @@ -57,6 +57,7 @@ Enjoy! ### Releases +* 2020-02-17, `v1.6.1`: stable release 1.6: minor updates (build with clang-cl, fix alignment issue for small objects). * 2020-02-09, `v1.6.0`: stable release 1.6: fixed potential memory leak, improved overriding and thread local support on FreeBSD, NetBSD, DragonFly, and macOSX. New byte-precise heap block overflow detection in debug mode (besides the double-free detection and free-list @@ -275,8 +276,7 @@ resolved to the _mimalloc_ library. Note that certain security restrictions may apply when doing this from the [shell](https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash). -Note: unfortunately, at this time, dynamic overriding on macOS seems broken but it is -actively worked on to fix this (see issue [`#50`](https://github.com/microsoft/mimalloc/issues/50)). +(Note: macOS support for dynamic overriding is recent, please report any issues.) ### Override on Windows From ec61224db0cf6b851b1b116d4387bd8404242c95 Mon Sep 17 00:00:00 2001 From: daan Date: Tue, 18 Feb 2020 20:05:30 -0800 Subject: [PATCH 2/9] fix padding issue with zero sized allocation (issue #209) --- src/alloc.c | 5 +++++ test/main-override.cpp | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/alloc.c b/src/alloc.c index b1c4cd34..efa35f58 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -62,6 +62,11 @@ extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_ mi_assert(heap!=NULL); mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local mi_assert(size <= MI_SMALL_SIZE_MAX); + #if (MI_PADDING) + if (size == 0) { + size = sizeof(void*); + } + #endif mi_page_t* page = _mi_heap_get_free_small_page(heap,size + MI_PADDING_SIZE); void* p = _mi_page_malloc(heap, page, size + MI_PADDING_SIZE); mi_assert_internal(p==NULL || mi_usable_size(p) >= size); diff --git a/test/main-override.cpp b/test/main-override.cpp index 957b7872..18d49df3 100644 --- a/test/main-override.cpp +++ b/test/main-override.cpp @@ -22,12 +22,14 @@ static void msleep(unsigned long msecs) { usleep(msecs * 1000UL); } void heap_no_delete(); void heap_late_free(); +void padding_shrink(); void various_tests(); int main() { mi_stats_reset(); // ignore earlier allocations // heap_no_delete(); // issue #202 // heap_late_free(); // issue #204 + padding_shrink(); // issue #209 various_tests(); mi_stats_print(NULL); return 0; @@ -140,4 +142,18 @@ void heap_late_free() { mi_free((void*)global_p); t1.join(); -} \ No newline at end of file +} + +// issue #209 +static void* shared_p; +static void alloc0(/* void* arg */) +{ + shared_p = mi_malloc(8); +} + +void padding_shrink(void) +{ + auto t1 = std::thread(alloc0); + t1.join(); + mi_free(shared_p); +} From b41183e8a36bbe815ff5a8897d27aa2581c44601 Mon Sep 17 00:00:00 2001 From: Orkhan Hasanli Date: Wed, 4 Mar 2020 20:45:20 -0500 Subject: [PATCH 3/9] Creation of symbolic link was failing. #166 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e16830aa..c268b7a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,7 +216,7 @@ if(NOT WIN32) # install a symlink in the /usr/local/lib to the versioned library set(mi_symlink "${CMAKE_SHARED_MODULE_PREFIX}${mi_basename}${CMAKE_SHARED_LIBRARY_SUFFIX}") set(mi_soname "mimalloc-${mi_version}/${mi_symlink}.${mi_version}") - install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${mi_soname} ${mi_symlink} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${mi_install_dir}/..)") + install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${mi_soname} ${mi_symlink} WORKING_DIRECTORY ${mi_install_dir}/..)") install(CODE "MESSAGE(\"-- Symbolic link: ${CMAKE_INSTALL_PREFIX}/lib/${mi_symlink} -> ${mi_soname}\")") endif() From ab202fbe7399d5851c6913fc11626b234020b551 Mon Sep 17 00:00:00 2001 From: daan Date: Fri, 6 Mar 2020 15:53:07 -0800 Subject: [PATCH 4/9] never free the main heap structures; issues #207 --- src/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/init.c b/src/init.c index 2c9dec1a..0fd5454c 100644 --- a/src/init.c +++ b/src/init.c @@ -241,7 +241,9 @@ static bool _mi_heap_done(mi_heap_t* heap) { mi_assert_internal(heap->tld->segments.count == 0); _mi_os_free(heap, sizeof(mi_thread_data_t), &_mi_stats_main); } -#if (MI_DEBUG > 0) +#if 0 + // never free the main thread even in debug mode; if a dll is linked statically with mimalloc, + // there may still be delete/free calls after the mi_fls_done is called. Issue #207 else { _mi_heap_destroy_pages(heap); mi_assert_internal(heap->tld->heap_backing == &_mi_heap_main); From 5bc276c23b1d54f4aad92eae7fe7f572028a56ba Mon Sep 17 00:00:00 2001 From: daan Date: Fri, 6 Mar 2020 16:24:51 -0800 Subject: [PATCH 5/9] fix issue #208: dynamic unloading of DLL with statically linked mimalloc --- src/init.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/init.c b/src/init.c index 0fd5454c..cfb44ecf 100644 --- a/src/init.c +++ b/src/init.c @@ -485,6 +485,10 @@ static void mi_process_done(void) { if (process_done) return; process_done = true; + #if defined(_WIN32) && !defined(MI_SHARED_LIB) + FlsSetValue(mi_fls_key, NULL); // don't call main-thread callback + FlsFree(mi_fls_key); // call thread-done on all threads to prevent dangling callback pointer if statically linked with a DLL; Issue #208 + #endif #ifndef NDEBUG mi_collect(true); #endif @@ -492,7 +496,7 @@ static void mi_process_done(void) { mi_option_is_enabled(mi_option_verbose)) { mi_stats_print(NULL); } - mi_allocator_done(); + mi_allocator_done(); _mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id); os_preloading = true; // don't call the C runtime anymore } From 72f758c433d9a8ece82404a711b474aae8c7cf7e Mon Sep 17 00:00:00 2001 From: daan Date: Fri, 6 Mar 2020 16:43:39 -0800 Subject: [PATCH 6/9] fix issue #210 where multiple static instances of mimalloc in DLL's compete for the same virtual memory area --- src/os.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/os.c b/src/os.c index 0aa85bd6..89fd349b 100644 --- a/src/os.c +++ b/src/os.c @@ -209,7 +209,12 @@ static void* mi_win_virtual_allocx(void* addr, size_t size, size_t try_alignment // on 64-bit systems, try to use the virtual address area after 4TiB for 4MiB aligned allocations void* hint; if (addr == NULL && (hint = mi_os_get_aligned_hint(try_alignment,size)) != NULL) { - return VirtualAlloc(hint, size, flags, PAGE_READWRITE); + void* p = VirtualAlloc(hint, size, flags, PAGE_READWRITE); + if (p != NULL) return p; + DWORD err = GetLastError(); + if (err != ERROR_INVALID_ADDRESS) { // if linked with multiple instances, we may have tried to allocate at an already allocated area + return NULL; + } } #endif #if defined(MEM_EXTENDED_PARAMETER_TYPE_BITS) From 854e81c11dfc865584299515b70d0b91a5e4191b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 7 Mar 2020 09:18:45 +0000 Subject: [PATCH 7/9] build fix for arm, adding native arch flag to be able to generate yield asm instruction. --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c268b7a9..fffac46a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,11 @@ if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel") endif() endif() +# Architecture flags +if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + list(APPEND mi_cflags -march=native) +endif() + # extra needed libraries if(WIN32) list(APPEND mi_libraries psapi shell32 user32 bcrypt) From 2884affbd70da897473603b4009aca2d63bc0a7b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 7 Mar 2020 12:19:48 +0000 Subject: [PATCH 8/9] Using host data rather. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fffac46a..a0893007 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,7 +132,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU|Intel") endif() # Architecture flags -if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") +if(${CMAKE_HOST_SYSTEM_PROCESSOR} MATCHES "arm") list(APPEND mi_cflags -march=native) endif() From 7745dde8d257010da34b4eb8d7d6246b99631ac9 Mon Sep 17 00:00:00 2001 From: daan Date: Mon, 16 Mar 2020 15:31:37 -0700 Subject: [PATCH 9/9] allow retirement for all object sizes (issue #212) --- include/mimalloc-types.h | 2 ++ src/init.c | 2 ++ src/page.c | 27 ++++++++++++++++++++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/include/mimalloc-types.h b/include/mimalloc-types.h index dc85bbcd..28606668 100644 --- a/include/mimalloc-types.h +++ b/include/mimalloc-types.h @@ -329,6 +329,8 @@ struct mi_heap_s { uintptr_t keys[2]; // two random keys used to encode the `thread_delayed_free` list mi_random_ctx_t random; // random number context used for secure allocation size_t page_count; // total number of pages in the `pages` queues. + size_t page_retired_min; // smallest retired index (retired pages are fully free, but still in the page queues) + size_t page_retired_max; // largest retired index into the `pages` array. mi_heap_t* next; // list of heaps per thread bool no_reclaim; // `true` if this heap should not reclaim abandoned pages }; diff --git a/src/init.c b/src/init.c index cfb44ecf..2e94935c 100644 --- a/src/init.c +++ b/src/init.c @@ -97,6 +97,7 @@ const mi_heap_t _mi_heap_empty = { { 0, 0 }, // keys { {0}, {0}, 0 }, 0, // page count + MI_BIN_FULL, 0, // page retired min/max NULL, // next false }; @@ -131,6 +132,7 @@ mi_heap_t _mi_heap_main = { { 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!) { {0x846ca68b}, {0}, 0 }, // random 0, // page count + MI_BIN_FULL, 0, // page retired min/max NULL, // next heap false // can reclaim }; diff --git a/src/page.c b/src/page.c index ef8a69e5..2903b258 100644 --- a/src/page.c +++ b/src/page.c @@ -380,7 +380,8 @@ void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) { _mi_segment_page_free(page, force, segments_tld); } -#define MI_MAX_RETIRE_SIZE (4*MI_SMALL_SIZE_MAX) +#define MI_MAX_RETIRE_SIZE MI_LARGE_OBJ_SIZE_MAX +#define MI_RETIRE_CYCLES (16) // Retire a page with no more used blocks // Important to not retire too quickly though as new @@ -405,7 +406,13 @@ void _mi_page_retire(mi_page_t* page) { if (mi_likely(page->xblock_size <= MI_MAX_RETIRE_SIZE && !mi_page_is_in_full(page))) { if (pq->last==page && pq->first==page) { // the only page in the queue? mi_stat_counter_increase(_mi_stats_main.page_no_retire,1); - page->retire_expire = 16; + page->retire_expire = MI_RETIRE_CYCLES; + mi_heap_t* heap = mi_page_heap(page); + mi_assert_internal(pq >= heap->pages); + const size_t index = pq - heap->pages; + mi_assert_internal(index < MI_BIN_FULL && index < MI_BIN_HUGE); + if (index < heap->page_retired_min) heap->page_retired_min = index; + if (index > heap->page_retired_max) heap->page_retired_max = index; mi_assert_internal(mi_page_all_free(page)); return; // dont't free after all } @@ -415,22 +422,32 @@ void _mi_page_retire(mi_page_t* page) { } // free retired pages: we don't need to look at the entire queues -// since we only retire pages that are the last one in a queue. +// since we only retire pages that are at the head position in a queue. void _mi_heap_collect_retired(mi_heap_t* heap, bool force) { - for(mi_page_queue_t* pq = heap->pages; pq->block_size <= MI_MAX_RETIRE_SIZE; pq++) { - mi_page_t* page = pq->first; + size_t min = MI_BIN_FULL; + size_t max = 0; + for(size_t bin = heap->page_retired_min; bin <= heap->page_retired_max; bin++) { + mi_page_queue_t* pq = &heap->pages[bin]; + mi_page_t* page = pq->first; if (page != NULL && page->retire_expire != 0) { if (mi_page_all_free(page)) { page->retire_expire--; if (force || page->retire_expire == 0) { _mi_page_free(pq->first, pq, force); } + else { + // keep retired, update min/max + if (bin < min) min = bin; + if (bin > max) max = bin; + } } else { page->retire_expire = 0; } } } + heap->page_retired_min = min; + heap->page_retired_max = max; }