mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-06 23:39:31 +03:00
Merge branch 'dev' into dev-slice
This commit is contained in:
commit
d3455ea29e
13 changed files with 266 additions and 155 deletions
|
@ -34,6 +34,7 @@ option(MI_NO_THP "Disable transparent huge pages support on Linux/And
|
||||||
option(MI_EXTRA_CPPDEFS "Extra pre-processor definitions (use as `-DMI_EXTRA_CPPDEFS=\"opt1=val1;opt2=val2\"`)" "")
|
option(MI_EXTRA_CPPDEFS "Extra pre-processor definitions (use as `-DMI_EXTRA_CPPDEFS=\"opt1=val1;opt2=val2\"`)" "")
|
||||||
|
|
||||||
# deprecated options
|
# deprecated options
|
||||||
|
option(MI_WIN_USE_FLS "Use Fiber local storage on Windows to detect thread termination" OFF)
|
||||||
option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF)
|
option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode (deprecated, use MI_DEBUG_FULL instead)" OFF)
|
||||||
option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF)
|
option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF)
|
||||||
|
|
||||||
|
@ -313,6 +314,11 @@ if(MI_LIBC_MUSL)
|
||||||
list(APPEND mi_defines MI_LIBC_MUSL=1)
|
list(APPEND mi_defines MI_LIBC_MUSL=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(MI_WIN_USE_FLS)
|
||||||
|
message(STATUS "Use the Fiber API to detect thread termination")
|
||||||
|
list(APPEND mi_defines MI_WIN_USE_FLS=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
# On Haiku use `-DCMAKE_INSTALL_PREFIX` instead, issue #788
|
# On Haiku use `-DCMAKE_INSTALL_PREFIX` instead, issue #788
|
||||||
# if(CMAKE_SYSTEM_NAME MATCHES "Haiku")
|
# if(CMAKE_SYSTEM_NAME MATCHES "Haiku")
|
||||||
# SET(CMAKE_INSTALL_LIBDIR ~/config/non-packaged/lib)
|
# SET(CMAKE_INSTALL_LIBDIR ~/config/non-packaged/lib)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -77,6 +77,11 @@ static inline uintptr_t _mi_random_shuffle(uintptr_t x);
|
||||||
// init.c
|
// init.c
|
||||||
extern mi_decl_cache_align mi_stats_t _mi_stats_main;
|
extern mi_decl_cache_align mi_stats_t _mi_stats_main;
|
||||||
extern mi_decl_cache_align const mi_page_t _mi_page_empty;
|
extern mi_decl_cache_align const mi_page_t _mi_page_empty;
|
||||||
|
void _mi_process_load(void);
|
||||||
|
void mi_cdecl _mi_process_done(void);
|
||||||
|
bool _mi_is_redirected(void);
|
||||||
|
bool _mi_allocator_init(const char** message);
|
||||||
|
void _mi_allocator_done(void);
|
||||||
bool _mi_is_main_thread(void);
|
bool _mi_is_main_thread(void);
|
||||||
size_t _mi_current_thread_count(void);
|
size_t _mi_current_thread_count(void);
|
||||||
bool _mi_preloading(void); // true while the C runtime is not initialized yet
|
bool _mi_preloading(void); // true while the C runtime is not initialized yet
|
||||||
|
|
|
@ -237,7 +237,7 @@ static mi_segment_t* mi_arena_segment_clear_abandoned_at(mi_arena_t* arena, mi_s
|
||||||
static mi_segment_t* mi_arena_segment_clear_abandoned_next_field(mi_arena_field_cursor_t* previous) {
|
static mi_segment_t* mi_arena_segment_clear_abandoned_next_field(mi_arena_field_cursor_t* previous) {
|
||||||
const size_t max_arena = mi_arena_get_count();
|
const size_t max_arena = mi_arena_get_count();
|
||||||
size_t field_idx = mi_bitmap_index_field(previous->bitmap_idx);
|
size_t field_idx = mi_bitmap_index_field(previous->bitmap_idx);
|
||||||
size_t bit_idx = mi_bitmap_index_bit_in_field(previous->bitmap_idx) + 1;
|
size_t bit_idx = mi_bitmap_index_bit_in_field(previous->bitmap_idx);
|
||||||
// visit arena's (from the previous cursor)
|
// visit arena's (from the previous cursor)
|
||||||
for (; previous->start < previous->end; previous->start++, field_idx = 0, bit_idx = 0) {
|
for (; previous->start < previous->end; previous->start++, field_idx = 0, bit_idx = 0) {
|
||||||
// index wraps around
|
// index wraps around
|
||||||
|
@ -266,11 +266,12 @@ static mi_segment_t* mi_arena_segment_clear_abandoned_next_field(mi_arena_field_
|
||||||
// pre-check if the bit is set
|
// pre-check if the bit is set
|
||||||
size_t mask = ((size_t)1 << bit_idx);
|
size_t mask = ((size_t)1 << bit_idx);
|
||||||
if mi_unlikely((field & mask) == mask) {
|
if mi_unlikely((field & mask) == mask) {
|
||||||
previous->bitmap_idx = mi_bitmap_index_create(field_idx, bit_idx);
|
mi_bitmap_index_t bitmap_idx = mi_bitmap_index_create(field_idx, bit_idx);
|
||||||
mi_segment_t* const segment = mi_arena_segment_clear_abandoned_at(arena, previous->subproc, previous->bitmap_idx);
|
mi_segment_t* const segment = mi_arena_segment_clear_abandoned_at(arena, previous->subproc, bitmap_idx);
|
||||||
if (segment != NULL) {
|
if (segment != NULL) {
|
||||||
//mi_assert_internal(arena->blocks_committed == NULL || _mi_bitmap_is_claimed(arena->blocks_committed, arena->field_count, 1, bitmap_idx));
|
//mi_assert_internal(arena->blocks_committed == NULL || _mi_bitmap_is_claimed(arena->blocks_committed, arena->field_count, 1, bitmap_idx));
|
||||||
if (has_lock) { mi_lock_release(&arena->abandoned_visit_lock); }
|
if (has_lock) { mi_lock_release(&arena->abandoned_visit_lock); }
|
||||||
|
previous->bitmap_idx = mi_bitmap_index_create_ex(field_idx, bit_idx + 1); // start at next one for the next iteration
|
||||||
return segment;
|
return segment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,13 @@ typedef mi_bitmap_field_t* mi_bitmap_t;
|
||||||
typedef size_t mi_bitmap_index_t;
|
typedef size_t mi_bitmap_index_t;
|
||||||
|
|
||||||
// Create a bit index.
|
// Create a bit index.
|
||||||
|
static inline mi_bitmap_index_t mi_bitmap_index_create_ex(size_t idx, size_t bitidx) {
|
||||||
|
mi_assert_internal(bitidx <= MI_BITMAP_FIELD_BITS);
|
||||||
|
return (idx*MI_BITMAP_FIELD_BITS) + bitidx;
|
||||||
|
}
|
||||||
static inline mi_bitmap_index_t mi_bitmap_index_create(size_t idx, size_t bitidx) {
|
static inline mi_bitmap_index_t mi_bitmap_index_create(size_t idx, size_t bitidx) {
|
||||||
mi_assert_internal(bitidx < MI_BITMAP_FIELD_BITS);
|
mi_assert_internal(bitidx < MI_BITMAP_FIELD_BITS);
|
||||||
return (idx*MI_BITMAP_FIELD_BITS) + bitidx;
|
return mi_bitmap_index_create_ex(idx,bitidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a bit index.
|
// Create a bit index.
|
||||||
|
|
|
@ -244,11 +244,12 @@ static void mi_decl_noinline mi_free_block_delayed_mt( mi_page_t* page, mi_block
|
||||||
static void mi_decl_noinline mi_free_block_mt(mi_page_t* page, mi_segment_t* segment, mi_block_t* block)
|
static void mi_decl_noinline mi_free_block_mt(mi_page_t* page, mi_segment_t* segment, mi_block_t* block)
|
||||||
{
|
{
|
||||||
// first see if the segment was abandoned and if we can reclaim it into our thread
|
// first see if the segment was abandoned and if we can reclaim it into our thread
|
||||||
if (mi_option_is_enabled(mi_option_abandoned_reclaim_on_free) &&
|
if (_mi_option_get_fast(mi_option_abandoned_reclaim_on_free) != 0 &&
|
||||||
#if MI_HUGE_PAGE_ABANDON
|
#if MI_HUGE_PAGE_ABANDON
|
||||||
segment->page_kind != MI_PAGE_HUGE &&
|
segment->page_kind != MI_PAGE_HUGE &&
|
||||||
#endif
|
#endif
|
||||||
mi_atomic_load_relaxed(&segment->thread_id) == 0)
|
mi_atomic_load_relaxed(&segment->thread_id) == 0 && // segment is abandoned?
|
||||||
|
mi_prim_get_default_heap() != (mi_heap_t*)&_mi_heap_empty) // and we did not already exit this thread (without this check, a fresh heap will be initalized (issue #944))
|
||||||
{
|
{
|
||||||
// the segment is abandoned, try to reclaim it into our heap
|
// the segment is abandoned, try to reclaim it into our heap
|
||||||
if (_mi_segment_attempt_reclaim(mi_heap_get_default(), segment)) {
|
if (_mi_segment_attempt_reclaim(mi_heap_get_default(), segment)) {
|
||||||
|
|
|
@ -555,13 +555,14 @@ void _mi_heap_area_init(mi_heap_area_t* area, mi_page_t* page) {
|
||||||
|
|
||||||
static void mi_get_fast_divisor(size_t divisor, uint64_t* magic, size_t* shift) {
|
static void mi_get_fast_divisor(size_t divisor, uint64_t* magic, size_t* shift) {
|
||||||
mi_assert_internal(divisor > 0 && divisor <= UINT32_MAX);
|
mi_assert_internal(divisor > 0 && divisor <= UINT32_MAX);
|
||||||
*shift = 64 - mi_clz(divisor - 1);
|
*shift = MI_INTPTR_BITS - mi_clz(divisor - 1);
|
||||||
*magic = ((((uint64_t)1 << 32) * (((uint64_t)1 << *shift) - divisor)) / divisor + 1);
|
*magic = ((((uint64_t)1 << 32) * (((uint64_t)1 << *shift) - divisor)) / divisor + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t mi_fast_divide(size_t n, uint64_t magic, size_t shift) {
|
static size_t mi_fast_divide(size_t n, uint64_t magic, size_t shift) {
|
||||||
mi_assert_internal(n <= UINT32_MAX);
|
mi_assert_internal(n <= UINT32_MAX);
|
||||||
return ((((uint64_t)n * magic) >> 32) + n) >> shift;
|
const uint64_t hi = ((uint64_t)n * magic) >> 32;
|
||||||
|
return (size_t)((hi + n) >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _mi_heap_area_visit_blocks(const mi_heap_area_t* area, mi_page_t* page, mi_block_visit_fun* visitor, void* arg) {
|
bool _mi_heap_area_visit_blocks(const mi_heap_area_t* area, mi_page_t* page, mi_block_visit_fun* visitor, void* arg) {
|
||||||
|
|
110
src/init.c
110
src/init.c
|
@ -533,54 +533,15 @@ void _mi_heap_set_default_direct(mi_heap_t* heap) {
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
// Run functions on process init/done, and thread init/done
|
// Run functions on process init/done, and thread init/done
|
||||||
// --------------------------------------------------------
|
// --------------------------------------------------------
|
||||||
static void mi_cdecl mi_process_done(void);
|
|
||||||
|
|
||||||
static bool os_preloading = true; // true until this module is initialized
|
static bool os_preloading = true; // true until this module is initialized
|
||||||
static bool mi_redirected = false; // true if malloc redirects to mi_malloc
|
|
||||||
|
|
||||||
// Returns true if this module has not been initialized; Don't use C runtime routines until it returns false.
|
// Returns true if this module has not been initialized; Don't use C runtime routines until it returns false.
|
||||||
bool mi_decl_noinline _mi_preloading(void) {
|
bool mi_decl_noinline _mi_preloading(void) {
|
||||||
return os_preloading;
|
return os_preloading;
|
||||||
}
|
}
|
||||||
|
|
||||||
mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept {
|
// Called once by the process loader from `src/prim/prim.c`
|
||||||
return mi_redirected;
|
void _mi_process_load(void) {
|
||||||
}
|
|
||||||
|
|
||||||
// Communicate with the redirection module on Windows
|
|
||||||
#if defined(_WIN32) && defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT)
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
mi_decl_export void _mi_redirect_entry(DWORD reason) {
|
|
||||||
// called on redirection; careful as this may be called before DllMain
|
|
||||||
if (reason == DLL_PROCESS_ATTACH) {
|
|
||||||
mi_redirected = true;
|
|
||||||
}
|
|
||||||
else if (reason == DLL_PROCESS_DETACH) {
|
|
||||||
mi_redirected = false;
|
|
||||||
}
|
|
||||||
else if (reason == DLL_THREAD_DETACH) {
|
|
||||||
mi_thread_done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message);
|
|
||||||
__declspec(dllimport) void mi_cdecl mi_allocator_done(void);
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
static bool mi_allocator_init(const char** message) {
|
|
||||||
if (message != NULL) *message = NULL;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static void mi_allocator_done(void) {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Called once by the process loader
|
|
||||||
static void mi_process_load(void) {
|
|
||||||
mi_heap_main_init();
|
mi_heap_main_init();
|
||||||
#if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
|
#if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
|
||||||
volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true;
|
volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true;
|
||||||
|
@ -588,17 +549,14 @@ static void mi_process_load(void) {
|
||||||
#endif
|
#endif
|
||||||
os_preloading = false;
|
os_preloading = false;
|
||||||
mi_assert_internal(_mi_is_main_thread());
|
mi_assert_internal(_mi_is_main_thread());
|
||||||
#if !(defined(_WIN32) && defined(MI_SHARED_LIB)) // use Dll process detach (see below) instead of atexit (issue #521)
|
|
||||||
atexit(&mi_process_done);
|
|
||||||
#endif
|
|
||||||
_mi_options_init();
|
_mi_options_init();
|
||||||
mi_process_setup_auto_thread_done();
|
mi_process_setup_auto_thread_done();
|
||||||
mi_process_init();
|
mi_process_init();
|
||||||
if (mi_redirected) _mi_verbose_message("malloc is redirected.\n");
|
if (_mi_is_redirected()) _mi_verbose_message("malloc is redirected.\n");
|
||||||
|
|
||||||
// show message from the redirector (if present)
|
// show message from the redirector (if present)
|
||||||
const char* msg = NULL;
|
const char* msg = NULL;
|
||||||
mi_allocator_init(&msg);
|
_mi_allocator_init(&msg);
|
||||||
if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) {
|
if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) {
|
||||||
_mi_fputs(NULL,NULL,NULL,msg);
|
_mi_fputs(NULL,NULL,NULL,msg);
|
||||||
}
|
}
|
||||||
|
@ -676,7 +634,7 @@ void mi_process_init(void) mi_attr_noexcept {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the process is done (through `at_exit`)
|
// Called when the process is done (through `at_exit`)
|
||||||
static void mi_cdecl mi_process_done(void) {
|
void mi_cdecl _mi_process_done(void) {
|
||||||
// only shutdown if we were initialized
|
// only shutdown if we were initialized
|
||||||
if (!_mi_process_is_initialized) return;
|
if (!_mi_process_is_initialized) return;
|
||||||
// ensure we are called once
|
// ensure we are called once
|
||||||
|
@ -708,64 +666,8 @@ static void mi_cdecl mi_process_done(void) {
|
||||||
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
|
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
|
||||||
mi_stats_print(NULL);
|
mi_stats_print(NULL);
|
||||||
}
|
}
|
||||||
mi_allocator_done();
|
_mi_allocator_done();
|
||||||
_mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id);
|
_mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id);
|
||||||
os_preloading = true; // don't call the C runtime anymore
|
os_preloading = true; // don't call the C runtime anymore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(_WIN32) && defined(MI_SHARED_LIB)
|
|
||||||
// Windows DLL: easy to hook into process_init and thread_done
|
|
||||||
__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
|
||||||
MI_UNUSED(reserved);
|
|
||||||
MI_UNUSED(inst);
|
|
||||||
if (reason==DLL_PROCESS_ATTACH) {
|
|
||||||
mi_process_load();
|
|
||||||
}
|
|
||||||
else if (reason==DLL_PROCESS_DETACH) {
|
|
||||||
mi_process_done();
|
|
||||||
}
|
|
||||||
else if (reason==DLL_THREAD_DETACH) {
|
|
||||||
if (!mi_is_redirected()) {
|
|
||||||
mi_thread_done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
// MSVC: use data section magic for static libraries
|
|
||||||
// See <https://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm>
|
|
||||||
static int _mi_process_init(void) {
|
|
||||||
mi_process_load();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
typedef int(*_mi_crt_callback_t)(void);
|
|
||||||
#if defined(_M_X64) || defined(_M_ARM64)
|
|
||||||
__pragma(comment(linker, "/include:" "_mi_msvc_initu"))
|
|
||||||
#pragma section(".CRT$XIU", long, read)
|
|
||||||
#else
|
|
||||||
__pragma(comment(linker, "/include:" "__mi_msvc_initu"))
|
|
||||||
#endif
|
|
||||||
#pragma data_seg(".CRT$XIU")
|
|
||||||
mi_decl_externc _mi_crt_callback_t _mi_msvc_initu[] = { &_mi_process_init };
|
|
||||||
#pragma data_seg()
|
|
||||||
|
|
||||||
#elif defined(__cplusplus)
|
|
||||||
// C++: use static initialization to detect process start
|
|
||||||
static bool _mi_process_init(void) {
|
|
||||||
mi_process_load();
|
|
||||||
return (_mi_heap_main.thread_id != 0);
|
|
||||||
}
|
|
||||||
static bool mi_initialized = _mi_process_init();
|
|
||||||
|
|
||||||
#elif defined(__GNUC__) || defined(__clang__)
|
|
||||||
// GCC,Clang: use the constructor attribute
|
|
||||||
static void __attribute__((constructor)) _mi_process_init(void) {
|
|
||||||
mi_process_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#pragma message("define a way to call mi_process_load on your platform")
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -25,3 +25,60 @@ terms of the MIT license. A copy of the license can be found in the file
|
||||||
#include "unix/prim.c" // mmap() (Linux, macOSX, BSD, Illumnos, Haiku, DragonFly, etc.)
|
#include "unix/prim.c" // mmap() (Linux, macOSX, BSD, Illumnos, Haiku, DragonFly, etc.)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Generic process initialization
|
||||||
|
#ifndef MI_PRIM_HAS_PROCESS_ATTACH
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
// gcc,clang: use the constructor/destructor attribute
|
||||||
|
// which for both seem to run before regular constructors/destructors
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define mi_attr_constructor __attribute__((constructor(101)))
|
||||||
|
#define mi_attr_destructor __attribute__((destructor(101)))
|
||||||
|
#else
|
||||||
|
#define mi_attr_constructor __attribute__((constructor))
|
||||||
|
#define mi_attr_destructor __attribute__((destructor))
|
||||||
|
#endif
|
||||||
|
static void mi_attr_constructor mi_process_attach(void) {
|
||||||
|
_mi_process_load();
|
||||||
|
}
|
||||||
|
static void mi_attr_destructor mi_process_detach(void) {
|
||||||
|
_mi_process_done();
|
||||||
|
}
|
||||||
|
#elif defined(__cplusplus)
|
||||||
|
// C++: use static initialization to detect process start/end
|
||||||
|
struct mi_init_done_t {
|
||||||
|
mi_init_done_t() {
|
||||||
|
_mi_process_load();
|
||||||
|
}
|
||||||
|
~mi_init_done_t() {
|
||||||
|
_mi_process_done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static mi_init_done_t mi_init_done;
|
||||||
|
/*
|
||||||
|
extern mi_heap_t _mi_heap_main;
|
||||||
|
static bool mi_process_attach(void) {
|
||||||
|
_mi_process_load();
|
||||||
|
atexit(&_mi_process_done);
|
||||||
|
return (_mi_heap_main.thread_id != 0);
|
||||||
|
}
|
||||||
|
static bool mi_initialized = mi_process_attach();
|
||||||
|
*/
|
||||||
|
#else
|
||||||
|
#pragma message("define a way to call _mi_process_load/done on your platform")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Generic allocator init/done callback
|
||||||
|
#ifndef MI_PRIM_HAS_ALLOCATOR_INIT
|
||||||
|
bool _mi_is_redirected(void) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool _mi_allocator_init(const char** message) {
|
||||||
|
if (message != NULL) { *message = NULL; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void _mi_allocator_done(void) {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -499,8 +499,7 @@ void _mi_prim_process_info(mi_process_info_t* pinfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get process info
|
// get process info
|
||||||
PROCESS_MEMORY_COUNTERS info;
|
PROCESS_MEMORY_COUNTERS info; _mi_memzero_var(info);
|
||||||
memset(&info, 0, sizeof(info));
|
|
||||||
if (pGetProcessMemoryInfo != NULL) {
|
if (pGetProcessMemoryInfo != NULL) {
|
||||||
pGetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
pGetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
|
||||||
}
|
}
|
||||||
|
@ -602,60 +601,195 @@ bool _mi_prim_random_buf(void* buf, size_t buf_len) {
|
||||||
|
|
||||||
#endif // MI_USE_RTLGENRANDOM
|
#endif // MI_USE_RTLGENRANDOM
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
// Thread init/done
|
// Process & Thread Init/Done
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
#if !defined(MI_SHARED_LIB)
|
static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) {
|
||||||
|
MI_UNUSED(reserved);
|
||||||
|
MI_UNUSED(module);
|
||||||
|
if (reason==DLL_PROCESS_ATTACH) {
|
||||||
|
_mi_process_load();
|
||||||
|
}
|
||||||
|
else if (reason==DLL_PROCESS_DETACH) {
|
||||||
|
_mi_process_done();
|
||||||
|
}
|
||||||
|
else if (reason==DLL_THREAD_DETACH && !_mi_is_redirected()) {
|
||||||
|
_mi_thread_done(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// use thread local storage keys to detect thread ending
|
|
||||||
// note: another design could be to use special linker sections (see issue #869)
|
|
||||||
#include <fibersapi.h>
|
|
||||||
#if (_WIN32_WINNT < 0x600) // before Windows Vista
|
|
||||||
WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback );
|
|
||||||
WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex );
|
|
||||||
WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData );
|
|
||||||
WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static DWORD mi_fls_key = (DWORD)(-1);
|
#if defined(MI_SHARED_LIB)
|
||||||
|
#define MI_PRIM_HAS_PROCESS_ATTACH 1
|
||||||
|
|
||||||
static void NTAPI mi_fls_done(PVOID value) {
|
// Windows DLL: easy to hook into process_init and thread_done
|
||||||
|
__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
|
||||||
|
mi_win_main((PVOID)inst,reason,reserved);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing to do since `_mi_thread_done` is handled through the DLL_THREAD_DETACH event.
|
||||||
|
void _mi_prim_thread_init_auto_done(void) { }
|
||||||
|
void _mi_prim_thread_done_auto_done(void) { }
|
||||||
|
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
|
||||||
|
MI_UNUSED(heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif !defined(MI_WIN_USE_FLS)
|
||||||
|
#define MI_PRIM_HAS_PROCESS_ATTACH 1
|
||||||
|
|
||||||
|
static void NTAPI mi_win_main_attach(PVOID module, DWORD reason, LPVOID reserved) {
|
||||||
|
if (reason == DLL_PROCESS_ATTACH || reason == DLL_THREAD_ATTACH) {
|
||||||
|
mi_win_main(module, reason, reserved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void NTAPI mi_win_main_detach(PVOID module, DWORD reason, LPVOID reserved) {
|
||||||
|
if (reason == DLL_PROCESS_DETACH || reason == DLL_THREAD_DETACH) {
|
||||||
|
mi_win_main(module, reason, reserved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up TLS callbacks in a statically linked library by using special data sections.
|
||||||
|
// See <https://stackoverflow.com/questions/14538159/tls-callback-in-windows>
|
||||||
|
// We use 2 entries to ensure we call attach events before constructors
|
||||||
|
// are called, and detach events after destructors are called.
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
|
#pragma comment(linker, "/INCLUDE:_tls_used")
|
||||||
|
#pragma comment(linker, "/INCLUDE:_mi_tls_callback_pre")
|
||||||
|
#pragma comment(linker, "/INCLUDE:_mi_tls_callback_post")
|
||||||
|
#pragma const_seg(".CRT$XLB")
|
||||||
|
extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[];
|
||||||
|
const PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_win_main_attach };
|
||||||
|
#pragma const_seg()
|
||||||
|
#pragma const_seg(".CRT$XLY")
|
||||||
|
extern const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[];
|
||||||
|
const PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_win_main_detach };
|
||||||
|
#pragma const_seg()
|
||||||
|
#else
|
||||||
|
#pragma comment(linker, "/INCLUDE:__tls_used")
|
||||||
|
#pragma comment(linker, "/INCLUDE:__mi_tls_callback_pre")
|
||||||
|
#pragma comment(linker, "/INCLUDE:__mi_tls_callback_post")
|
||||||
|
#pragma data_seg(".CRT$XLB")
|
||||||
|
PIMAGE_TLS_CALLBACK _mi_tls_callback_pre[] = { &mi_win_main_attach };
|
||||||
|
#pragma data_seg()
|
||||||
|
#pragma data_seg(".CRT$XLY")
|
||||||
|
PIMAGE_TLS_CALLBACK _mi_tls_callback_post[] = { &mi_win_main_detach };
|
||||||
|
#pragma data_seg()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// nothing to do since `_mi_thread_done` is handled through the DLL_THREAD_DETACH event.
|
||||||
|
void _mi_prim_thread_init_auto_done(void) { }
|
||||||
|
void _mi_prim_thread_done_auto_done(void) { }
|
||||||
|
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
|
||||||
|
MI_UNUSED(heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // deprecated: statically linked, use fiber api
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) // on clang/gcc use the constructor attribute (in `src/prim/prim.c`)
|
||||||
|
// MSVC: use data section magic for static libraries
|
||||||
|
// See <https://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm>
|
||||||
|
#define MI_PRIM_HAS_PROCESS_ATTACH 1
|
||||||
|
|
||||||
|
static int mi_process_attach(void) {
|
||||||
|
mi_win_main(NULL,DLL_PROCESS_ATTACH,NULL);
|
||||||
|
atexit(&_mi_process_done);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
typedef int(*mi_crt_callback_t)(void);
|
||||||
|
#if defined(_WIN64)
|
||||||
|
#pragma comment(linker, "/INCLUDE:_mi_tls_callback")
|
||||||
|
#pragma section(".CRT$XIU", long, read)
|
||||||
|
#else
|
||||||
|
#pragma comment(linker, "/INCLUDE:__mi_tls_callback")
|
||||||
|
#endif
|
||||||
|
#pragma data_seg(".CRT$XIU")
|
||||||
|
mi_decl_externc mi_crt_callback_t _mi_tls_callback[] = { &mi_process_attach };
|
||||||
|
#pragma data_seg()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// use the fiber api for calling `_mi_thread_done`.
|
||||||
|
#include <fibersapi.h>
|
||||||
|
#if (_WIN32_WINNT < 0x600) // before Windows Vista
|
||||||
|
WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback );
|
||||||
|
WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex );
|
||||||
|
WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData );
|
||||||
|
WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static DWORD mi_fls_key = (DWORD)(-1);
|
||||||
|
|
||||||
|
static void NTAPI mi_fls_done(PVOID value) {
|
||||||
mi_heap_t* heap = (mi_heap_t*)value;
|
mi_heap_t* heap = (mi_heap_t*)value;
|
||||||
if (heap != NULL) {
|
if (heap != NULL) {
|
||||||
_mi_thread_done(heap);
|
_mi_thread_done(heap);
|
||||||
FlsSetValue(mi_fls_key, NULL); // prevent recursion as _mi_thread_done may set it back to the main heap, issue #672
|
FlsSetValue(mi_fls_key, NULL); // prevent recursion as _mi_thread_done may set it back to the main heap, issue #672
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _mi_prim_thread_init_auto_done(void) {
|
void _mi_prim_thread_init_auto_done(void) {
|
||||||
mi_fls_key = FlsAlloc(&mi_fls_done);
|
mi_fls_key = FlsAlloc(&mi_fls_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _mi_prim_thread_done_auto_done(void) {
|
void _mi_prim_thread_done_auto_done(void) {
|
||||||
// call thread-done on all threads (except the main thread) to prevent
|
// call thread-done on all threads (except the main thread) to prevent
|
||||||
// dangling callback pointer if statically linked with a DLL; Issue #208
|
// dangling callback pointer if statically linked with a DLL; Issue #208
|
||||||
FlsFree(mi_fls_key);
|
FlsFree(mi_fls_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
|
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
|
||||||
mi_assert_internal(mi_fls_key != (DWORD)(-1));
|
mi_assert_internal(mi_fls_key != (DWORD)(-1));
|
||||||
FlsSetValue(mi_fls_key, heap);
|
FlsSetValue(mi_fls_key, heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// Dll; nothing to do as in that case thread_done is handled through the DLL_THREAD_DETACH event.
|
|
||||||
|
|
||||||
void _mi_prim_thread_init_auto_done(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void _mi_prim_thread_done_auto_done(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
|
|
||||||
MI_UNUSED(heap);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Communicate with the redirection module on Windows
|
||||||
|
// ----------------------------------------------------
|
||||||
|
#if defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT)
|
||||||
|
#define MI_PRIM_HAS_ALLOCATOR_INIT 1
|
||||||
|
|
||||||
|
static bool mi_redirected = false; // true if malloc redirects to mi_malloc
|
||||||
|
|
||||||
|
bool _mi_is_redirected(void) {
|
||||||
|
return mi_redirected;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
mi_decl_export void _mi_redirect_entry(DWORD reason) {
|
||||||
|
// called on redirection; careful as this may be called before DllMain
|
||||||
|
if (reason == DLL_PROCESS_ATTACH) {
|
||||||
|
mi_redirected = true;
|
||||||
|
}
|
||||||
|
else if (reason == DLL_PROCESS_DETACH) {
|
||||||
|
mi_redirected = false;
|
||||||
|
}
|
||||||
|
else if (reason == DLL_THREAD_DETACH) {
|
||||||
|
_mi_thread_done(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message);
|
||||||
|
__declspec(dllimport) void mi_cdecl mi_allocator_done(void);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bool _mi_allocator_init(const char** message) {
|
||||||
|
return mi_allocator_init(message);
|
||||||
|
}
|
||||||
|
void _mi_allocator_done(void) {
|
||||||
|
mi_allocator_done();
|
||||||
|
}
|
||||||
|
#endif
|
Loading…
Add table
Reference in a new issue