Merge branch 'dev' into cmake-build-variants

This commit is contained in:
Daan 2020-05-04 09:51:09 -07:00 committed by GitHub
commit ff4f1c3e5d
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 165 additions and 72 deletions

View file

@ -18,6 +18,8 @@ option(MI_PADDING "Enable padding to detect heap block overflow (only
option(MI_BUILD_SHARED "Build shared library" ON) option(MI_BUILD_SHARED "Build shared library" ON)
option(MI_BUILD_STATIC "Build static library" ON) option(MI_BUILD_STATIC "Build static library" ON)
option(MI_BUILD_OBJECT "Build object" ON) option(MI_BUILD_OBJECT "Build object" ON)
option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF)
option(MI_SHOW_ERRORS "Show error and warning messages by default" OFF)
include("cmake/mimalloc-config-version.cmake") include("cmake/mimalloc-config-version.cmake")
@ -70,6 +72,7 @@ if(MI_OVERRIDE MATCHES "ON")
# use zone's on macOS # use zone's on macOS
message(STATUS " Use malloc zone to override malloc (MI_OSX_ZONE=ON)") message(STATUS " Use malloc zone to override malloc (MI_OSX_ZONE=ON)")
list(APPEND mi_sources src/alloc-override-osx.c) list(APPEND mi_sources src/alloc-override-osx.c)
list(APPEND mi_defines MI_OSX_ZONE=1)
if(NOT MI_INTERPOSE MATCHES "ON") if(NOT MI_INTERPOSE MATCHES "ON")
message(STATUS " (enabling INTERPOSE as well since zone's require this)") message(STATUS " (enabling INTERPOSE as well since zone's require this)")
set(MI_INTERPOSE "ON") set(MI_INTERPOSE "ON")
@ -108,6 +111,16 @@ if(MI_PADDING MATCHES "OFF")
list(APPEND mi_defines MI_PADDING=0) list(APPEND mi_defines MI_PADDING=0)
endif() endif()
if(MI_XMALLOC MATCHES "ON")
message(STATUS "Enable abort() calls on memory allocation failure (MI_XMALLOC=ON)")
list(APPEND mi_defines MI_XMALLOC=1)
endif()
if(MI_SHOW_ERRORS MATCHES "ON")
message(STATUS "Enable printing of error and warning messages by default (MI_SHOW_ERRORS=ON)")
list(APPEND mi_defines MI_SHOW_ERRORS=1)
endif()
if(MI_USE_CXX MATCHES "ON") if(MI_USE_CXX MATCHES "ON")
message(STATUS "Use the C++ compiler to compile (MI_USE_CXX=ON)") 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(${mi_sources} PROPERTIES LANGUAGE CXX )
@ -212,6 +225,7 @@ if (MI_BUILD_STATIC)
message(STATUS "Static library will be built") message(STATUS "Static library will be built")
add_library(mimalloc-static STATIC ${mi_sources}) add_library(mimalloc-static STATIC ${mi_sources})
set_property(TARGET mimalloc-static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB) target_compile_definitions(mimalloc-static PRIVATE ${mi_defines} MI_STATIC_LIB)
target_compile_options(mimalloc-static PRIVATE ${mi_cflags}) target_compile_options(mimalloc-static PRIVATE ${mi_cflags})
target_link_libraries(mimalloc-static PUBLIC ${mi_libraries}) target_link_libraries(mimalloc-static PUBLIC ${mi_libraries})
@ -219,7 +233,7 @@ if (MI_BUILD_STATIC)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${mi_install_dir}/include> $<INSTALL_INTERFACE:${mi_install_dir}/include>
) )
if(WIN32 AND MI_BUILD_SHARED) if(WIN32)
# When building both static and shared libraries on Windows, a static library should use a # When building both static and shared libraries on Windows, a static library should use a
# different output name to avoid the conflict with the import library of a shared one. # different output name to avoid the conflict with the import library of a shared one.
string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename}) string(REPLACE "mimalloc" "mimalloc-static" mi_output_name ${mi_basename})
@ -251,6 +265,7 @@ if (MI_BUILD_OBJECT)
message(STATUS "Library object will be built") message(STATUS "Library object will be built")
add_library(mimalloc-obj OBJECT src/static.c) add_library(mimalloc-obj OBJECT src/static.c)
set_property(TARGET mimalloc-obj PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines}) target_compile_definitions(mimalloc-obj PRIVATE ${mi_defines})
target_compile_options(mimalloc-obj PRIVATE ${mi_cflags}) target_compile_options(mimalloc-obj PRIVATE ${mi_cflags})
target_include_directories(mimalloc-obj PUBLIC target_include_directories(mimalloc-obj PUBLIC

View file

@ -40,8 +40,8 @@ jobs:
cd $(BuildType) cd $(BuildType)
ctest ctest
displayName: CTest displayName: CTest
- upload: $(Build.SourcesDirectory)/$(BuildType) # - upload: $(Build.SourcesDirectory)/$(BuildType)
artifact: mimalloc-windows-$(BuildType) # artifact: mimalloc-windows-$(BuildType)
- job: - job:
displayName: Linux displayName: Linux
@ -99,8 +99,8 @@ jobs:
displayName: Make displayName: Make
- script: make test -C $(BuildType) - script: make test -C $(BuildType)
displayName: CTest displayName: CTest
- upload: $(Build.SourcesDirectory)/$(BuildType) # - upload: $(Build.SourcesDirectory)/$(BuildType)
artifact: mimalloc-ubuntu-$(BuildType) # artifact: mimalloc-ubuntu-$(BuildType)
- job: - job:
displayName: macOS displayName: macOS
@ -127,5 +127,5 @@ jobs:
displayName: Make displayName: Make
- script: make test -C $(BuildType) - script: make test -C $(BuildType)
displayName: CTest displayName: CTest
- upload: $(Build.SourcesDirectory)/$(BuildType) # - upload: $(Build.SourcesDirectory)/$(BuildType)
artifact: mimalloc-macos-$(BuildType) # artifact: mimalloc-macos-$(BuildType)

View file

@ -61,7 +61,6 @@ terms of the MIT license. A copy of the license can be found in the file
#define MI_ENCODE_FREELIST 1 #define MI_ENCODE_FREELIST 1
#endif #endif
// ------------------------------------------------------ // ------------------------------------------------------
// Platform specific values // Platform specific values
// ------------------------------------------------------ // ------------------------------------------------------

View file

@ -308,8 +308,7 @@ typedef enum mi_option_e {
mi_option_use_numa_nodes, mi_option_use_numa_nodes,
mi_option_os_tag, mi_option_os_tag,
mi_option_max_errors, mi_option_max_errors,
_mi_option_last, _mi_option_last
mi_option_eager_page_commit = mi_option_eager_commit
} mi_option_t; } mi_option_t;

View file

@ -255,6 +255,32 @@ OS will copy the entire 1GiB huge page (or 2MiB large page) which can cause the
[linux-huge]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5 [linux-huge]: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/tuning_and_optimizing_red_hat_enterprise_linux_for_oracle_9i_and_10g_databases/sect-oracle_9i_and_10g_tuning_guide-large_memory_optimization_big_pages_and_huge_pages-configuring_huge_pages_in_red_hat_enterprise_linux_4_or_5
[windows-huge]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017 [windows-huge]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows?view=sql-server-2017
## Secure Mode
_mimalloc_ can be build in secure mode by using the `-DMI_SECURE=ON` flags in `cmake`. This build enables various mitigations
to make mimalloc more robust against exploits. In particular:
- All internal mimalloc pages are surrounded by guard pages and the heap metadata is behind a guard page as well (so a buffer overflow
exploit cannot reach into the metadata),
- All free list pointers are
[encoded](https://github.com/microsoft/mimalloc/blob/783e3377f79ee82af43a0793910a9f2d01ac7863/include/mimalloc-internal.h#L396)
with per-page keys which is used both to prevent overwrites with a known pointer, as well as to detect heap corruption,
- Double free's are detected (and ignored),
- The free lists are initialized in a random order and allocation randomly chooses between extension and reuse within a page to
mitigate against attacks that rely on a predicable allocation order. Similarly, the larger heap blocks allocated by mimalloc
from the OS are also address randomized.
As always, evaluate with care as part of an overall security strategy as all of the above are mitigations but not guarantees.
## Debug Mode
When _mimalloc_ is built using debug mode, various checks are done at runtime to catch development errors.
- Statistics are maintained in detail for each object size. They can be shown using `MIMALLOC_SHOW_STATS=1` at runtime.
- All objects have padding at the end to detect (byte precise) heap block overflows.
- Double free's, and freeing invalid heap pointers are detected.
- Corrupted free-lists and some forms of use-after-free are detected.
# Overriding Malloc # Overriding Malloc

View file

@ -41,8 +41,11 @@ extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_im
------------------------------------------------------ */ ------------------------------------------------------ */
static size_t zone_size(malloc_zone_t* zone, const void* p) { static size_t zone_size(malloc_zone_t* zone, const void* p) {
UNUSED(zone); UNUSED(p); UNUSED(zone);
return 0; // as we cannot guarantee that `p` comes from us, just return 0 if (!mi_is_in_heap_region(p))
return 0; // not our pointer, bail out
return mi_usable_size(p);
} }
static void* zone_malloc(malloc_zone_t* zone, size_t size) { static void* zone_malloc(malloc_zone_t* zone, size_t size) {

View file

@ -103,7 +103,7 @@ terms of the MIT license. A copy of the license can be found in the file
void operator delete[](void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n); void operator delete[](void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n);
#endif #endif
#if (__cplusplus > 201402L || defined(__cpp_aligned_new)) && (!defined(__GNUC__) || (__GNUC__ > 5)) #if (__cplusplus > 201402L && defined(__cpp_aligned_new)) && (!defined(__GNUC__) || (__GNUC__ > 5))
void operator delete (void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); } void operator delete (void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); } void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); }; void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
@ -165,7 +165,7 @@ extern "C" {
void cfree(void* p) MI_FORWARD0(mi_free, p); void cfree(void* p) MI_FORWARD0(mi_free, p);
void* reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize); void* reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize);
size_t malloc_size(void* p) MI_FORWARD1(mi_usable_size,p); size_t malloc_size(const void* p) MI_FORWARD1(mi_usable_size,p);
#if !defined(__ANDROID__) #if !defined(__ANDROID__)
size_t malloc_usable_size(void *p) MI_FORWARD1(mi_usable_size,p); size_t malloc_usable_size(void *p) MI_FORWARD1(mi_usable_size,p);
#else #else

View file

@ -36,6 +36,7 @@ of 256MiB in practice.
// os.c // os.c
void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* large, mi_os_tld_t* tld); void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool* large, mi_os_tld_t* tld);
void _mi_os_free_ex(void* p, size_t size, bool was_committed, mi_stats_t* stats);
void _mi_os_free(void* p, size_t size, mi_stats_t* stats); void _mi_os_free(void* p, size_t size, mi_stats_t* stats);
void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_secs, size_t* pages_reserved, size_t* psize); void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_secs, size_t* pages_reserved, size_t* psize);
@ -213,13 +214,13 @@ void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_zero, siz
Arena free Arena free
----------------------------------------------------------- */ ----------------------------------------------------------- */
void _mi_arena_free(void* p, size_t size, size_t memid, mi_stats_t* stats) { void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_stats_t* stats) {
mi_assert_internal(size > 0 && stats != NULL); mi_assert_internal(size > 0 && stats != NULL);
if (p==NULL) return; if (p==NULL) return;
if (size==0) return; if (size==0) return;
if (memid == MI_MEMID_OS) { if (memid == MI_MEMID_OS) {
// was a direct OS allocation, pass through // was a direct OS allocation, pass through
_mi_os_free(p, size, stats); _mi_os_free_ex(p, size, all_committed, stats);
} }
else { else {
// allocated in an arena // allocated in an arena

View file

@ -51,7 +51,11 @@ typedef struct mi_option_desc_s {
static mi_option_desc_t options[_mi_option_last] = static mi_option_desc_t options[_mi_option_last] =
{ {
// stable options // stable options
{ MI_DEBUG, UNINIT, MI_OPTION(show_errors) }, #if MI_DEBUG || defined(MI_SHOW_ERRORS)
{ 1, UNINIT, MI_OPTION(show_errors) },
#else
{ 0, UNINIT, MI_OPTION(show_errors) },
#endif
{ 0, UNINIT, MI_OPTION(show_stats) }, { 0, UNINIT, MI_OPTION(show_stats) },
{ 0, UNINIT, MI_OPTION(verbose) }, { 0, UNINIT, MI_OPTION(verbose) },
@ -260,13 +264,17 @@ static void mi_recurse_exit(void) {
} }
void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message) { void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message) {
if (!mi_recurse_enter()) return;
if (out==NULL || (FILE*)out==stdout || (FILE*)out==stderr) { // TODO: use mi_out_stderr for stderr? if (out==NULL || (FILE*)out==stdout || (FILE*)out==stderr) { // TODO: use mi_out_stderr for stderr?
if (!mi_recurse_enter()) return;
out = mi_out_get_default(&arg); out = mi_out_get_default(&arg);
if (prefix != NULL) out(prefix, arg);
out(message, arg);
mi_recurse_exit();
}
else {
if (prefix != NULL) out(prefix, arg);
out(message, arg);
} }
if (prefix != NULL) out(prefix,arg);
out(message,arg);
mi_recurse_exit();
} }
// Define our own limited `fprintf` that avoids memory allocation. // Define our own limited `fprintf` that avoids memory allocation.
@ -348,6 +356,11 @@ static void mi_error_default(int err) {
abort(); abort();
} }
#endif #endif
#if defined(MI_XMALLOC)
if (err==ENOMEM || err==EOVERFLOW) { // abort on memory allocation fails in xmalloc mode
abort();
}
#endif
} }
void mi_register_error(mi_error_fun* fun, void* arg) { void mi_register_error(mi_error_fun* fun, void* arg) {

View file

@ -618,7 +618,7 @@ static void mi_mprotect_hint(int err) {
} }
// Commit/Decommit memory. // Commit/Decommit memory.
// Usuelly commit is aligned liberal, while decommit is aligned conservative. // Usually commit is aligned liberal, while decommit is aligned conservative.
// (but not for the reset version where we want commit to be conservative as well) // (but not for the reset version where we want commit to be conservative as well)
static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservative, bool* is_zero, mi_stats_t* stats) { static bool mi_os_commitx(void* addr, size_t size, bool commit, bool conservative, bool* is_zero, mi_stats_t* stats) {
// page align in the range, commit liberally, decommit conservative // page align in the range, commit liberally, decommit conservative

View file

@ -381,7 +381,7 @@ void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) {
} }
#define MI_MAX_RETIRE_SIZE MI_LARGE_OBJ_SIZE_MAX #define MI_MAX_RETIRE_SIZE MI_LARGE_OBJ_SIZE_MAX
#define MI_RETIRE_CYCLES (16) #define MI_RETIRE_CYCLES (8)
// Retire a page with no more used blocks // Retire a page with no more used blocks
// Important to not retire too quickly though as new // Important to not retire too quickly though as new
@ -406,7 +406,7 @@ 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 (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? if (pq->last==page && pq->first==page) { // the only page in the queue?
mi_stat_counter_increase(_mi_stats_main.page_no_retire,1); mi_stat_counter_increase(_mi_stats_main.page_no_retire,1);
page->retire_expire = MI_RETIRE_CYCLES; page->retire_expire = (page->xblock_size <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4);
mi_heap_t* heap = mi_page_heap(page); mi_heap_t* heap = mi_page_heap(page);
mi_assert_internal(pq >= heap->pages); mi_assert_internal(pq >= heap->pages);
const size_t index = pq - heap->pages; const size_t index = pq - heap->pages;
@ -570,7 +570,8 @@ static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld)
if (page->capacity >= page->reserved) return; if (page->capacity >= page->reserved) return;
size_t page_size; size_t page_size;
uint8_t* page_start = _mi_page_start(_mi_page_segment(page), page, &page_size); //uint8_t* page_start =
_mi_page_start(_mi_page_segment(page), page, &page_size);
mi_stat_counter_increase(tld->stats.pages_extended, 1); mi_stat_counter_increase(tld->stats.pages_extended, 1);
// calculate the extend count // calculate the extend count
@ -588,12 +589,6 @@ static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld)
mi_assert_internal(extend > 0 && extend + page->capacity <= page->reserved); mi_assert_internal(extend > 0 && extend + page->capacity <= page->reserved);
mi_assert_internal(extend < (1UL<<16)); mi_assert_internal(extend < (1UL<<16));
// commit on-demand for large and huge pages?
if (_mi_page_segment(page)->page_kind >= MI_PAGE_LARGE && !mi_option_is_enabled(mi_option_eager_page_commit)) {
uint8_t* start = page_start + (page->capacity * bsize);
_mi_mem_commit(start, extend * bsize, NULL, &tld->os);
}
// and append the extend the free list // and append the extend the free list
if (extend < MI_MIN_SLICES || MI_SECURE==0) { //!mi_option_is_enabled(mi_option_secure)) { if (extend < MI_MIN_SLICES || MI_SECURE==0) { //!mi_option_is_enabled(mi_option_secure)) {
mi_page_free_list_extend(page, bsize, extend, &tld->stats ); mi_page_free_list_extend(page, bsize, extend, &tld->stats );

View file

@ -49,7 +49,7 @@ bool _mi_os_reset(void* p, size_t size, mi_stats_t* stats);
bool _mi_os_unreset(void* p, size_t size, bool* is_zero, mi_stats_t* stats); bool _mi_os_unreset(void* p, size_t size, bool* is_zero, mi_stats_t* stats);
// arena.c // arena.c
void _mi_arena_free(void* p, size_t size, size_t memid, mi_stats_t* stats); void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_stats_t* stats);
void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld); void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld);
void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld); void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_zero, size_t* memid, mi_os_tld_t* tld);
@ -187,7 +187,7 @@ static bool mi_region_try_alloc_os(size_t blocks, bool commit, bool allow_large,
const uintptr_t idx = mi_atomic_increment(&regions_count); const uintptr_t idx = mi_atomic_increment(&regions_count);
if (idx >= MI_REGION_MAX) { if (idx >= MI_REGION_MAX) {
mi_atomic_decrement(&regions_count); mi_atomic_decrement(&regions_count);
_mi_arena_free(start, MI_REGION_SIZE, arena_memid, tld->stats); _mi_arena_free(start, MI_REGION_SIZE, arena_memid, region_commit, tld->stats);
_mi_warning_message("maximum regions used: %zu GiB (perhaps recompile with a larger setting for MI_HEAP_REGION_MAX_SIZE)", _mi_divide_up(MI_HEAP_REGION_MAX_SIZE, GiB)); _mi_warning_message("maximum regions used: %zu GiB (perhaps recompile with a larger setting for MI_HEAP_REGION_MAX_SIZE)", _mi_divide_up(MI_HEAP_REGION_MAX_SIZE, GiB));
return false; return false;
} }
@ -205,6 +205,7 @@ static bool mi_region_try_alloc_os(size_t blocks, bool commit, bool allow_large,
// and share it // and share it
mi_region_info_t info; mi_region_info_t info;
info.value = 0; // initialize the full union to zero
info.x.valid = true; info.x.valid = true;
info.x.is_large = region_large; info.x.is_large = region_large;
info.x.numa_node = (short)_mi_os_numa_node(tld); info.x.numa_node = (short)_mi_os_numa_node(tld);
@ -391,7 +392,7 @@ void _mi_mem_free(void* p, size_t size, size_t id, bool full_commit, bool any_re
mem_region_t* region; mem_region_t* region;
if (mi_memid_is_arena(id,&region,&bit_idx,&arena_memid)) { if (mi_memid_is_arena(id,&region,&bit_idx,&arena_memid)) {
// was a direct arena allocation, pass through // was a direct arena allocation, pass through
_mi_arena_free(p, size, arena_memid, tld->stats); _mi_arena_free(p, size, arena_memid, full_commit, tld->stats);
} }
else { else {
// allocated in a region // allocated in a region
@ -454,12 +455,13 @@ void _mi_mem_collect(mi_os_tld_t* tld) {
// on success, free the whole region // on success, free the whole region
uint8_t* start = mi_atomic_read_ptr(uint8_t,&regions[i].start); uint8_t* start = mi_atomic_read_ptr(uint8_t,&regions[i].start);
size_t arena_memid = mi_atomic_read_relaxed(&regions[i].arena_memid); size_t arena_memid = mi_atomic_read_relaxed(&regions[i].arena_memid);
uintptr_t commit = mi_atomic_read_relaxed(&regions[i].commit);
memset(&regions[i], 0, sizeof(mem_region_t)); memset(&regions[i], 0, sizeof(mem_region_t));
// and release the whole region // and release the whole region
mi_atomic_write(&region->info, 0); mi_atomic_write(&region->info, 0);
if (start != NULL) { // && !_mi_os_is_huge_reserved(start)) { if (start != NULL) { // && !_mi_os_is_huge_reserved(start)) {
_mi_abandoned_await_readers(); // ensure no pending reads _mi_abandoned_await_readers(); // ensure no pending reads
_mi_arena_free(start, MI_REGION_SIZE, arena_memid, tld->stats); _mi_arena_free(start, MI_REGION_SIZE, arena_memid, (~commit == 0), tld->stats);
} }
} }
} }

View file

@ -204,9 +204,9 @@ static void mi_segment_protect(mi_segment_t* segment, bool protect, mi_os_tld_t*
mi_segment_protect_range((uint8_t*)segment + segment->segment_info_size - os_page_size, os_page_size, protect); mi_segment_protect_range((uint8_t*)segment + segment->segment_info_size - os_page_size, os_page_size, protect);
if (MI_SECURE <= 1 || segment->capacity == 1) { if (MI_SECURE <= 1 || segment->capacity == 1) {
// and protect the last (or only) page too // and protect the last (or only) page too
mi_assert_internal(segment->page_kind >= MI_PAGE_LARGE); mi_assert_internal(MI_SECURE <= 1 || segment->page_kind >= MI_PAGE_LARGE);
uint8_t* start = (uint8_t*)segment + segment->segment_size - os_page_size; uint8_t* start = (uint8_t*)segment + segment->segment_size - os_page_size;
if (protect && !mi_option_is_enabled(mi_option_eager_page_commit)) { if (protect && !segment->mem_is_committed) {
// ensure secure page is committed // ensure secure page is committed
_mi_mem_commit(start, os_page_size, NULL, tld); _mi_mem_commit(start, os_page_size, NULL, tld);
} }
@ -236,12 +236,8 @@ static void mi_page_reset(mi_segment_t* segment, mi_page_t* page, size_t size, m
void* start = mi_segment_raw_page_start(segment, page, &psize); void* start = mi_segment_raw_page_start(segment, page, &psize);
page->is_reset = true; page->is_reset = true;
mi_assert_internal(size <= psize); mi_assert_internal(size <= psize);
size_t reset_size = (size == 0 || size > psize ? psize : size); size_t reset_size = ((size == 0 || size > psize) ? psize : size);
if (size == 0 && segment->page_kind >= MI_PAGE_LARGE && !mi_option_is_enabled(mi_option_eager_page_commit)) { if (reset_size > 0) _mi_mem_reset(start, reset_size, tld->os);
mi_assert_internal(page->xblock_size > 0);
reset_size = page->capacity * mi_page_block_size(page);
}
_mi_mem_reset(start, reset_size, tld->os);
} }
static void mi_page_unreset(mi_segment_t* segment, mi_page_t* page, size_t size, mi_segments_tld_t* tld) static void mi_page_unreset(mi_segment_t* segment, mi_page_t* page, size_t size, mi_segments_tld_t* tld)
@ -253,12 +249,8 @@ static void mi_page_unreset(mi_segment_t* segment, mi_page_t* page, size_t size,
size_t psize; size_t psize;
uint8_t* start = mi_segment_raw_page_start(segment, page, &psize); uint8_t* start = mi_segment_raw_page_start(segment, page, &psize);
size_t unreset_size = (size == 0 || size > psize ? psize : size); size_t unreset_size = (size == 0 || size > psize ? psize : size);
if (size == 0 && segment->page_kind >= MI_PAGE_LARGE && !mi_option_is_enabled(mi_option_eager_page_commit)) {
mi_assert_internal(page->xblock_size > 0);
unreset_size = page->capacity * mi_page_block_size(page);
}
bool is_zero = false; bool is_zero = false;
_mi_mem_unreset(start, unreset_size, &is_zero, tld->os); if (unreset_size > 0) _mi_mem_unreset(start, unreset_size, &is_zero, tld->os);
if (is_zero) page->is_zero_init = true; if (is_zero) page->is_zero_init = true;
} }
@ -475,10 +467,7 @@ static void mi_segment_os_free(mi_segment_t* segment, size_t segment_size, mi_se
if (any_reset && mi_option_is_enabled(mi_option_reset_decommits)) { if (any_reset && mi_option_is_enabled(mi_option_reset_decommits)) {
fully_committed = false; fully_committed = false;
} }
if (segment->page_kind >= MI_PAGE_LARGE && !mi_option_is_enabled(mi_option_eager_page_commit)) {
fully_committed = false;
}
_mi_mem_free(segment, segment_size, segment->memid, fully_committed, any_reset, tld->os); _mi_mem_free(segment, segment_size, segment->memid, fully_committed, any_reset, tld->os);
} }
@ -627,8 +616,13 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_
if (!commit) { if (!commit) {
// ensure the initial info is committed // ensure the initial info is committed
bool commit_zero = false; bool commit_zero = false;
_mi_mem_commit(segment, pre_size, &commit_zero, tld->os); bool ok = _mi_mem_commit(segment, pre_size, &commit_zero, tld->os);
if (commit_zero) is_zero = true; if (commit_zero) is_zero = true;
if (!ok) {
// commit failed; we cannot touch the memory: free the segment directly and return `NULL`
_mi_mem_free(segment, MI_SEGMENT_SIZE, memid, false, false, os_tld);
return NULL;
}
} }
segment->memid = memid; segment->memid = memid;
segment->mem_is_fixed = mem_large; segment->mem_is_fixed = mem_large;
@ -714,28 +708,28 @@ static bool mi_segment_has_free(const mi_segment_t* segment) {
return (segment->used < segment->capacity); return (segment->used < segment->capacity);
} }
static void mi_segment_page_claim(mi_segment_t* segment, mi_page_t* page, mi_segments_tld_t* tld) { static bool mi_segment_page_claim(mi_segment_t* segment, mi_page_t* page, mi_segments_tld_t* tld) {
mi_assert_internal(_mi_page_segment(page) == segment); mi_assert_internal(_mi_page_segment(page) == segment);
mi_assert_internal(!page->segment_in_use); mi_assert_internal(!page->segment_in_use);
// set in-use before doing unreset to prevent delayed reset
mi_pages_reset_remove(page, tld); mi_pages_reset_remove(page, tld);
page->segment_in_use = true; // check commit
segment->used++;
if (!page->is_committed) { if (!page->is_committed) {
mi_assert_internal(!segment->mem_is_fixed); mi_assert_internal(!segment->mem_is_fixed);
mi_assert_internal(!page->is_reset); mi_assert_internal(!page->is_reset);
size_t psize;
uint8_t* start = mi_segment_raw_page_start(segment, page, &psize);
bool is_zero = false;
const size_t gsize = (MI_SECURE >= 2 ? _mi_os_page_size() : 0);
bool ok = _mi_mem_commit(start, psize + gsize, &is_zero, tld->os);
if (!ok) return false; // failed to commit!
if (gsize > 0) { mi_segment_protect_range(start + psize, gsize, true); }
if (is_zero) { page->is_zero_init = true; }
page->is_committed = true; page->is_committed = true;
if (segment->page_kind < MI_PAGE_LARGE
|| !mi_option_is_enabled(mi_option_eager_page_commit)) {
size_t psize;
uint8_t* start = mi_segment_raw_page_start(segment, page, &psize);
bool is_zero = false;
const size_t gsize = (MI_SECURE >= 2 ? _mi_os_page_size() : 0);
_mi_mem_commit(start, psize + gsize, &is_zero, tld->os);
if (gsize > 0) { mi_segment_protect_range(start + psize, gsize, true); }
if (is_zero) { page->is_zero_init = true; }
}
} }
// set in-use before doing unreset to prevent delayed reset
page->segment_in_use = true;
segment->used++;
// check reset
if (page->is_reset) { if (page->is_reset) {
mi_page_unreset(segment, page, 0, tld); // todo: only unreset the part that was reset? mi_page_unreset(segment, page, 0, tld); // todo: only unreset the part that was reset?
} }
@ -746,6 +740,7 @@ static void mi_segment_page_claim(mi_segment_t* segment, mi_page_t* page, mi_seg
mi_assert_internal(!mi_segment_has_free(segment)); mi_assert_internal(!mi_segment_has_free(segment));
mi_segment_remove_from_free_queue(segment, tld); mi_segment_remove_from_free_queue(segment, tld);
} }
return true;
} }
@ -1212,8 +1207,8 @@ static mi_page_t* mi_segment_find_free(mi_segment_t* segment, mi_segments_tld_t*
for (size_t i = 0; i < segment->capacity; i++) { // TODO: use a bitmap instead of search? for (size_t i = 0; i < segment->capacity; i++) { // TODO: use a bitmap instead of search?
mi_page_t* page = &segment->pages[i]; mi_page_t* page = &segment->pages[i];
if (!page->segment_in_use) { if (!page->segment_in_use) {
mi_segment_page_claim(segment, page, tld); bool ok = mi_segment_page_claim(segment, page, tld);
return page; if (ok) return page;
} }
} }
mi_assert(false); mi_assert(false);

View file

@ -24,5 +24,8 @@ terms of the MIT license. A copy of the license can be found in the file
#include "alloc.c" #include "alloc.c"
#include "alloc-aligned.c" #include "alloc-aligned.c"
#include "alloc-posix.c" #include "alloc-posix.c"
#if MI_OSX_ZONE
#include "alloc-override-osx.c"
#endif
#include "init.c" #include "init.c"
#include "options.c" #include "options.c"

View file

@ -237,9 +237,51 @@ static void mi_stats_print_bins(mi_stat_count_t* all, const mi_stat_count_t* bin
#endif #endif
//------------------------------------------------------------
// Use an output wrapper for line-buffered output
// (which is nice when using loggers etc.)
//------------------------------------------------------------
typedef struct buffered_s {
mi_output_fun* out; // original output function
void* arg; // and state
char* buf; // local buffer of at least size `count+1`
size_t used; // currently used chars `used <= count`
size_t count; // total chars available for output
} buffered_t;
static void mi_buffered_flush(buffered_t* buf) {
buf->buf[buf->used] = 0;
_mi_fputs(buf->out, buf->arg, NULL, buf->buf);
buf->used = 0;
}
static void mi_buffered_out(const char* msg, void* arg) {
buffered_t* buf = (buffered_t*)arg;
if (msg==NULL || buf==NULL) return;
for (const char* src = msg; *src != 0; src++) {
char c = *src;
if (buf->used >= buf->count) mi_buffered_flush(buf);
mi_assert_internal(buf->used < buf->count);
buf->buf[buf->used++] = c;
if (c == '\n') mi_buffered_flush(buf);
}
}
//------------------------------------------------------------
// Print statistics
//------------------------------------------------------------
static void mi_process_info(mi_msecs_t* utime, mi_msecs_t* stime, size_t* peak_rss, size_t* page_faults, size_t* page_reclaim, size_t* peak_commit); static void mi_process_info(mi_msecs_t* utime, mi_msecs_t* stime, size_t* peak_rss, size_t* page_faults, size_t* page_reclaim, size_t* peak_commit);
static void _mi_stats_print(mi_stats_t* stats, mi_msecs_t elapsed, mi_output_fun* out, void* arg) mi_attr_noexcept { static void _mi_stats_print(mi_stats_t* stats, mi_msecs_t elapsed, mi_output_fun* out0, void* arg0) mi_attr_noexcept {
// wrap the output function to be line buffered
char buf[256];
buffered_t buffer = { out0, arg0, buf, 0, 255 };
mi_output_fun* out = &mi_buffered_out;
void* arg = &buffer;
// and print using that
mi_print_header(out,arg); mi_print_header(out,arg);
#if MI_STAT>1 #if MI_STAT>1
mi_stat_count_t normal = { 0,0,0,0 }; mi_stat_count_t normal = { 0,0,0,0 };
@ -287,7 +329,7 @@ static void _mi_stats_print(mi_stats_t* stats, mi_msecs_t elapsed, mi_output_fun
_mi_fprintf(out, arg, ", commit charge: "); _mi_fprintf(out, arg, ", commit charge: ");
mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s"); mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s");
} }
_mi_fprintf(out, arg, "\n"); _mi_fprintf(out, arg, "\n");
} }
static mi_msecs_t mi_time_start; // = 0 static mi_msecs_t mi_time_start; // = 0