diff --git a/CMakeLists.txt b/CMakeLists.txt index e16830aa..a0893007 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_HOST_SYSTEM_PROCESSOR} MATCHES "arm") + list(APPEND mi_cflags -march=native) +endif() + # extra needed libraries if(WIN32) list(APPEND mi_libraries psapi shell32 user32 bcrypt) @@ -216,7 +221,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() diff --git a/include/mimalloc-types.h b/include/mimalloc-types.h index a2b8d74b..fe4b71b3 100644 --- a/include/mimalloc-types.h +++ b/include/mimalloc-types.h @@ -332,6 +332,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/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 diff --git a/src/alloc.c b/src/alloc.c index 4651d2d7..449ea6fb 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -67,6 +67,11 @@ MI_ALLOC_API1(inline mi_decl_restrict void*, malloc_small, mi_heap_t*, heap, siz 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_SOURCE_XARG); mi_assert_internal(p==NULL || mi_usable_size(p) >= size); diff --git a/src/init.c b/src/init.c index ce797add..32470c1c 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 }; @@ -241,7 +243,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); @@ -483,6 +487,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 @@ -490,7 +498,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 } 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) diff --git a/src/page.c b/src/page.c index 8092455f..ff812e42 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; } diff --git a/test/main-override.cpp b/test/main-override.cpp index 040230a5..66996959 100644 --- a/test/main-override.cpp +++ b/test/main-override.cpp @@ -7,7 +7,7 @@ #include #include #include -// #include +#include #include #ifdef _WIN32 @@ -28,6 +28,7 @@ int main() { // heap_no_delete(); // issue #202 // heap_late_free(); // issue #204 // dangling_ptr_write(); + // padding_shrink(); // issue #209 various_tests(); mi_stats_print(NULL); return 0; @@ -159,3 +160,18 @@ static void heap_late_free() { t1.join(); } + +// 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); +} + diff --git a/test/test-api.c b/test/test-api.c index 166cfca6..e274fd96 100644 --- a/test/test-api.c +++ b/test/test-api.c @@ -123,7 +123,7 @@ int main() { }); CHECK_BODY("posix_memalign_nomem", { void* p = &p; - int err = mi_posix_memalign(&p, sizeof(void*), SIZE_MAX); + volatile int err = mi_posix_memalign(&p, sizeof(void*), SIZE_MAX); result = (err==ENOMEM && p==&p); });