Merge branch 'dev' into dev-slice

This commit is contained in:
Daan 2024-10-27 18:17:38 -07:00
commit d3455ea29e
13 changed files with 266 additions and 155 deletions

View file

@ -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.

View file

@ -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

View file

@ -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;
} }
} }

View file

@ -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.

View file

@ -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)) {

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -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);
// use thread local storage keys to detect thread ending MI_UNUSED(module);
// note: another design could be to use special linker sections (see issue #869) if (reason==DLL_PROCESS_ATTACH) {
#include <fibersapi.h> _mi_process_load();
#if (_WIN32_WINNT < 0x600) // before Windows Vista }
WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback ); else if (reason==DLL_PROCESS_DETACH) {
WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex ); _mi_process_done();
WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData ); }
WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex); else if (reason==DLL_THREAD_DETACH && !_mi_is_redirected()) {
#endif _mi_thread_done(NULL);
static DWORD mi_fls_key = (DWORD)(-1);
static void NTAPI mi_fls_done(PVOID value) {
mi_heap_t* heap = (mi_heap_t*)value;
if (heap != NULL) {
_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
} }
} }
void _mi_prim_thread_init_auto_done(void) {
mi_fls_key = FlsAlloc(&mi_fls_done);
}
void _mi_prim_thread_done_auto_done(void) { #if defined(MI_SHARED_LIB)
// call thread-done on all threads (except the main thread) to prevent #define MI_PRIM_HAS_PROCESS_ATTACH 1
// dangling callback pointer if statically linked with a DLL; Issue #208
FlsFree(mi_fls_key);
}
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { // Windows DLL: easy to hook into process_init and thread_done
mi_assert_internal(mi_fls_key != (DWORD)(-1)); __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
FlsSetValue(mi_fls_key, heap); mi_win_main((PVOID)inst,reason,reserved);
} return TRUE;
}
#else // 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);
}
// Dll; nothing to do as in that case thread_done is handled through the DLL_THREAD_DETACH event. #elif !defined(MI_WIN_USE_FLS)
#define MI_PRIM_HAS_PROCESS_ATTACH 1
void _mi_prim_thread_init_auto_done(void) { 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);
}
}
void _mi_prim_thread_done_auto_done(void) { // 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
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { #if defined(_WIN64)
MI_UNUSED(heap); #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;
if (heap != NULL) {
_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
}
}
void _mi_prim_thread_init_auto_done(void) {
mi_fls_key = FlsAlloc(&mi_fls_done);
}
void _mi_prim_thread_done_auto_done(void) {
// call thread-done on all threads (except the main thread) to prevent
// dangling callback pointer if statically linked with a DLL; Issue #208
FlsFree(mi_fls_key);
}
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
mi_assert_internal(mi_fls_key != (DWORD)(-1));
FlsSetValue(mi_fls_key, 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