Path: blob/main/system/lib/mimalloc/src/init.c
6175 views
/* ----------------------------------------------------------------------------1Copyright (c) 2018-2022, Microsoft Research, Daan Leijen2This is free software; you can redistribute it and/or modify it under the3terms of the MIT license. A copy of the license can be found in the file4"LICENSE" at the root of this distribution.5-----------------------------------------------------------------------------*/6#include "mimalloc.h"7#include "mimalloc/internal.h"8#include "mimalloc/prim.h"910#include <string.h> // memcpy, memset11#include <stdlib.h> // atexit121314// Empty page used to initialize the small free pages array15const mi_page_t _mi_page_empty = {160,17false, false, false, false,180, // capacity190, // reserved capacity20{ 0 }, // flags21false, // is_zero220, // retire_expire23NULL, // free24NULL, // local_free250, // used260, // block size shift270, // heap tag280, // block_size29NULL, // page_start30#if (MI_PADDING || MI_ENCODE_FREELIST)31{ 0, 0 },32#endif33MI_ATOMIC_VAR_INIT(0), // xthread_free34MI_ATOMIC_VAR_INIT(0), // xheap35NULL, NULL36, { 0 } // padding37};3839#define MI_PAGE_EMPTY() ((mi_page_t*)&_mi_page_empty)4041#if (MI_SMALL_WSIZE_MAX==128)42#if (MI_PADDING>0) && (MI_INTPTR_SIZE >= 8)43#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }44#elif (MI_PADDING>0)45#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }46#else47#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY() }48#endif49#else50#error "define right initialization sizes corresponding to MI_SMALL_WSIZE_MAX"51#endif5253// Empty page queues for every bin54#define QNULL(sz) { NULL, NULL, (sz)*sizeof(uintptr_t) }55#define MI_PAGE_QUEUES_EMPTY \56{ QNULL(1), \57QNULL( 1), QNULL( 2), QNULL( 3), QNULL( 4), QNULL( 5), QNULL( 6), QNULL( 7), QNULL( 8), /* 8 */ \58QNULL( 10), QNULL( 12), QNULL( 14), QNULL( 16), QNULL( 20), QNULL( 24), QNULL( 28), QNULL( 32), /* 16 */ \59QNULL( 40), QNULL( 48), QNULL( 56), QNULL( 64), QNULL( 80), QNULL( 96), QNULL( 112), QNULL( 128), /* 24 */ \60QNULL( 160), QNULL( 192), QNULL( 224), QNULL( 256), QNULL( 320), QNULL( 384), QNULL( 448), QNULL( 512), /* 32 */ \61QNULL( 640), QNULL( 768), QNULL( 896), QNULL( 1024), QNULL( 1280), QNULL( 1536), QNULL( 1792), QNULL( 2048), /* 40 */ \62QNULL( 2560), QNULL( 3072), QNULL( 3584), QNULL( 4096), QNULL( 5120), QNULL( 6144), QNULL( 7168), QNULL( 8192), /* 48 */ \63QNULL( 10240), QNULL( 12288), QNULL( 14336), QNULL( 16384), QNULL( 20480), QNULL( 24576), QNULL( 28672), QNULL( 32768), /* 56 */ \64QNULL( 40960), QNULL( 49152), QNULL( 57344), QNULL( 65536), QNULL( 81920), QNULL( 98304), QNULL(114688), QNULL(131072), /* 64 */ \65QNULL(163840), QNULL(196608), QNULL(229376), QNULL(262144), QNULL(327680), QNULL(393216), QNULL(458752), QNULL(524288), /* 72 */ \66QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 1 /* 655360, Huge queue */), \67QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 2) /* Full queue */ }6869#define MI_STAT_COUNT_NULL() {0,0,0,0}7071// Empty statistics72#if MI_STAT>173#define MI_STAT_COUNT_END_NULL() , { MI_STAT_COUNT_NULL(), MI_INIT32(MI_STAT_COUNT_NULL) }74#else75#define MI_STAT_COUNT_END_NULL()76#endif7778#define MI_STATS_NULL \79MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \80MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \81MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \82MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \83MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \84MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \85MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \86MI_STAT_COUNT_NULL(), \87{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \88{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \89{ 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \90{ 0, 0 } \91MI_STAT_COUNT_END_NULL()929394// Empty slice span queues for every bin95#define SQNULL(sz) { NULL, NULL, sz }96#define MI_SEGMENT_SPAN_QUEUES_EMPTY \97{ SQNULL(1), \98SQNULL( 1), SQNULL( 2), SQNULL( 3), SQNULL( 4), SQNULL( 5), SQNULL( 6), SQNULL( 7), SQNULL( 10), /* 8 */ \99SQNULL( 12), SQNULL( 14), SQNULL( 16), SQNULL( 20), SQNULL( 24), SQNULL( 28), SQNULL( 32), SQNULL( 40), /* 16 */ \100SQNULL( 48), SQNULL( 56), SQNULL( 64), SQNULL( 80), SQNULL( 96), SQNULL( 112), SQNULL( 128), SQNULL( 160), /* 24 */ \101SQNULL( 192), SQNULL( 224), SQNULL( 256), SQNULL( 320), SQNULL( 384), SQNULL( 448), SQNULL( 512), SQNULL( 640), /* 32 */ \102SQNULL( 768), SQNULL( 896), SQNULL( 1024) /* 35 */ }103104105// --------------------------------------------------------106// Statically allocate an empty heap as the initial107// thread local value for the default heap,108// and statically allocate the backing heap for the main109// thread so it can function without doing any allocation110// itself (as accessing a thread local for the first time111// may lead to allocation itself on some platforms)112// --------------------------------------------------------113114mi_decl_cache_align const mi_heap_t _mi_heap_empty = {115NULL,116MI_ATOMIC_VAR_INIT(NULL),1170, // tid1180, // cookie1190, // arena id120{ 0, 0 }, // keys121{ {0}, {0}, 0, true }, // random1220, // page count123MI_BIN_FULL, 0, // page retired min/max124NULL, // next125false, // can reclaim1260, // tag127MI_SMALL_PAGES_EMPTY,128MI_PAGE_QUEUES_EMPTY129};130131#define tld_empty_stats ((mi_stats_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,stats)))132#define tld_empty_os ((mi_os_tld_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,os)))133134mi_decl_cache_align static const mi_tld_t tld_empty = {1350,136false,137NULL, NULL,138{ MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, 0, tld_empty_stats, tld_empty_os }, // segments139{ 0, tld_empty_stats }, // os140{ MI_STATS_NULL } // stats141};142143mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {144return _mi_prim_thread_id();145}146147// the thread-local default heap for allocation148mi_decl_thread mi_heap_t* _mi_heap_default = (mi_heap_t*)&_mi_heap_empty;149150extern mi_heap_t _mi_heap_main;151152static mi_tld_t tld_main = {1530, false,154&_mi_heap_main, & _mi_heap_main,155{ MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, 0, &tld_main.stats, &tld_main.os }, // segments156{ 0, &tld_main.stats }, // os157{ MI_STATS_NULL } // stats158};159160mi_heap_t _mi_heap_main = {161&tld_main,162MI_ATOMIC_VAR_INIT(NULL),1630, // thread id1640, // initial cookie1650, // arena id166{ 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!)167{ {0x846ca68b}, {0}, 0, true }, // random1680, // page count169MI_BIN_FULL, 0, // page retired min/max170NULL, // next heap171false, // can reclaim1720, // tag173MI_SMALL_PAGES_EMPTY,174MI_PAGE_QUEUES_EMPTY175};176177bool _mi_process_is_initialized = false; // set to `true` in `mi_process_init`.178179mi_stats_t _mi_stats_main = { MI_STATS_NULL };180181182static void mi_heap_main_init(void) {183if (_mi_heap_main.cookie == 0) {184_mi_heap_main.thread_id = _mi_thread_id();185_mi_heap_main.cookie = 1;186#if defined(_WIN32) && !defined(MI_SHARED_LIB)187_mi_random_init_weak(&_mi_heap_main.random); // prevent allocation failure during bcrypt dll initialization with static linking188#else189_mi_random_init(&_mi_heap_main.random);190#endif191_mi_heap_main.cookie = _mi_heap_random_next(&_mi_heap_main);192_mi_heap_main.keys[0] = _mi_heap_random_next(&_mi_heap_main);193_mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main);194}195}196197mi_heap_t* _mi_heap_main_get(void) {198mi_heap_main_init();199return &_mi_heap_main;200}201202203/* -----------------------------------------------------------204Initialization and freeing of the thread local heaps205----------------------------------------------------------- */206207// note: in x64 in release build `sizeof(mi_thread_data_t)` is under 4KiB (= OS page size).208typedef struct mi_thread_data_s {209mi_heap_t heap; // must come first due to cast in `_mi_heap_done`210mi_tld_t tld;211mi_memid_t memid; // must come last due to zero'ing212} mi_thread_data_t;213214215// Thread meta-data is allocated directly from the OS. For216// some programs that do not use thread pools and allocate and217// destroy many OS threads, this may causes too much overhead218// per thread so we maintain a small cache of recently freed metadata.219220#define TD_CACHE_SIZE (16)221static _Atomic(mi_thread_data_t*) td_cache[TD_CACHE_SIZE];222223static mi_thread_data_t* mi_thread_data_zalloc(void) {224// try to find thread metadata in the cache225bool is_zero = false;226mi_thread_data_t* td = NULL;227for (int i = 0; i < TD_CACHE_SIZE; i++) {228td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]);229if (td != NULL) {230// found cached allocation, try use it231td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL);232if (td != NULL) {233break;234}235}236}237238// if that fails, allocate as meta data239if (td == NULL) {240mi_memid_t memid;241td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid, &_mi_stats_main);242if (td == NULL) {243// if this fails, try once more. (issue #257)244td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid, &_mi_stats_main);245if (td == NULL) {246// really out of memory247_mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t));248}249}250if (td != NULL) {251td->memid = memid;252is_zero = memid.initially_zero;253}254}255256if (td != NULL && !is_zero) {257_mi_memzero_aligned(td, offsetof(mi_thread_data_t,memid));258}259return td;260}261262static void mi_thread_data_free( mi_thread_data_t* tdfree ) {263// try to add the thread metadata to the cache264for (int i = 0; i < TD_CACHE_SIZE; i++) {265mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]);266if (td == NULL) {267mi_thread_data_t* expected = NULL;268if (mi_atomic_cas_ptr_weak_acq_rel(mi_thread_data_t, &td_cache[i], &expected, tdfree)) {269return;270}271}272}273// if that fails, just free it directly274_mi_os_free(tdfree, sizeof(mi_thread_data_t), tdfree->memid, &_mi_stats_main);275}276277void _mi_thread_data_collect(void) {278// free all thread metadata from the cache279for (int i = 0; i < TD_CACHE_SIZE; i++) {280mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]);281if (td != NULL) {282td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL);283if (td != NULL) {284_mi_os_free(td, sizeof(mi_thread_data_t), td->memid, &_mi_stats_main);285}286}287}288}289290// Initialize the thread local default heap, called from `mi_thread_init`291static bool _mi_thread_heap_init(void) {292if (mi_heap_is_initialized(mi_prim_get_default_heap())) return true;293if (_mi_is_main_thread()) {294// mi_assert_internal(_mi_heap_main.thread_id != 0); // can happen on freeBSD where alloc is called before any initialization295// the main heap is statically allocated296mi_heap_main_init();297_mi_heap_set_default_direct(&_mi_heap_main);298//mi_assert_internal(_mi_heap_default->tld->heap_backing == mi_prim_get_default_heap());299}300else {301// use `_mi_os_alloc` to allocate directly from the OS302mi_thread_data_t* td = mi_thread_data_zalloc();303if (td == NULL) return false;304305mi_tld_t* tld = &td->tld;306mi_heap_t* heap = &td->heap;307_mi_tld_init(tld, heap); // must be before `_mi_heap_init`308_mi_heap_init(heap, tld, _mi_arena_id_none(), false /* can reclaim */, 0 /* default tag */);309_mi_heap_set_default_direct(heap);310}311return false;312}313314// initialize thread local data315void _mi_tld_init(mi_tld_t* tld, mi_heap_t* bheap) {316_mi_memcpy_aligned(tld, &tld_empty, sizeof(mi_tld_t));317tld->heap_backing = bheap;318tld->heaps = NULL;319tld->segments.stats = &tld->stats;320tld->segments.os = &tld->os;321tld->os.stats = &tld->stats;322}323324// Free the thread local default heap (called from `mi_thread_done`)325static bool _mi_thread_heap_done(mi_heap_t* heap) {326if (!mi_heap_is_initialized(heap)) return true;327328// reset default heap329_mi_heap_set_default_direct(_mi_is_main_thread() ? &_mi_heap_main : (mi_heap_t*)&_mi_heap_empty);330331// switch to backing heap332heap = heap->tld->heap_backing;333if (!mi_heap_is_initialized(heap)) return false;334335// delete all non-backing heaps in this thread336mi_heap_t* curr = heap->tld->heaps;337while (curr != NULL) {338mi_heap_t* next = curr->next; // save `next` as `curr` will be freed339if (curr != heap) {340mi_assert_internal(!mi_heap_is_backing(curr));341mi_heap_delete(curr);342}343curr = next;344}345mi_assert_internal(heap->tld->heaps == heap && heap->next == NULL);346mi_assert_internal(mi_heap_is_backing(heap));347348// collect if not the main thread349if (heap != &_mi_heap_main) {350_mi_heap_collect_abandon(heap);351}352353// merge stats354_mi_stats_done(&heap->tld->stats);355356// free if not the main thread357if (heap != &_mi_heap_main) {358// the following assertion does not always hold for huge segments as those are always treated359// as abondened: one may allocate it in one thread, but deallocate in another in which case360// the count can be too large or negative. todo: perhaps not count huge segments? see issue #363361// mi_assert_internal(heap->tld->segments.count == 0 || heap->thread_id != _mi_thread_id());362mi_thread_data_free((mi_thread_data_t*)heap);363}364else {365#if 0366// never free the main thread even in debug mode; if a dll is linked statically with mimalloc,367// there may still be delete/free calls after the mi_fls_done is called. Issue #207368_mi_heap_destroy_pages(heap);369mi_assert_internal(heap->tld->heap_backing == &_mi_heap_main);370#endif371}372return false;373}374375376377// --------------------------------------------------------378// Try to run `mi_thread_done()` automatically so any memory379// owned by the thread but not yet released can be abandoned380// and re-owned by another thread.381//382// 1. windows dynamic library:383// call from DllMain on DLL_THREAD_DETACH384// 2. windows static library:385// use `FlsAlloc` to call a destructor when the thread is done386// 3. unix, pthreads:387// use a pthread key to call a destructor when a pthread is done388//389// In the last two cases we also need to call `mi_process_init`390// to set up the thread local keys.391// --------------------------------------------------------392393// Set up handlers so `mi_thread_done` is called automatically394static void mi_process_setup_auto_thread_done(void) {395static bool tls_initialized = false; // fine if it races396if (tls_initialized) return;397tls_initialized = true;398_mi_prim_thread_init_auto_done();399_mi_heap_set_default_direct(&_mi_heap_main);400}401402403bool _mi_is_main_thread(void) {404return (_mi_heap_main.thread_id==0 || _mi_heap_main.thread_id == _mi_thread_id());405}406407static _Atomic(size_t) thread_count = MI_ATOMIC_VAR_INIT(1);408409size_t _mi_current_thread_count(void) {410return mi_atomic_load_relaxed(&thread_count);411}412413// This is called from the `mi_malloc_generic`414void mi_thread_init(void) mi_attr_noexcept415{416// ensure our process has started already417mi_process_init();418419// initialize the thread local default heap420// (this will call `_mi_heap_set_default_direct` and thus set the421// fiber/pthread key to a non-zero value, ensuring `_mi_thread_done` is called)422if (_mi_thread_heap_init()) return; // returns true if already initialized423424_mi_stat_increase(&_mi_stats_main.threads, 1);425mi_atomic_increment_relaxed(&thread_count);426//_mi_verbose_message("thread init: 0x%zx\n", _mi_thread_id());427}428429void mi_thread_done(void) mi_attr_noexcept {430_mi_thread_done(NULL);431}432433void _mi_thread_done(mi_heap_t* heap)434{435// calling with NULL implies using the default heap436if (heap == NULL) {437heap = mi_prim_get_default_heap();438if (heap == NULL) return;439}440441// prevent re-entrancy through heap_done/heap_set_default_direct (issue #699)442if (!mi_heap_is_initialized(heap)) {443return;444}445446// adjust stats447mi_atomic_decrement_relaxed(&thread_count);448_mi_stat_decrease(&_mi_stats_main.threads, 1);449450// check thread-id as on Windows shutdown with FLS the main (exit) thread may call this on thread-local heaps...451if (heap->thread_id != _mi_thread_id()) return;452453// abandon the thread local heap454if (_mi_thread_heap_done(heap)) return; // returns true if already ran455}456457void _mi_heap_set_default_direct(mi_heap_t* heap) {458mi_assert_internal(heap != NULL);459#if defined(MI_TLS_SLOT)460mi_prim_tls_slot_set(MI_TLS_SLOT,heap);461#elif defined(MI_TLS_PTHREAD_SLOT_OFS)462*mi_prim_tls_pthread_heap_slot() = heap;463#elif defined(MI_TLS_PTHREAD)464// we use _mi_heap_default_key465#else466_mi_heap_default = heap;467#endif468469// ensure the default heap is passed to `_mi_thread_done`470// setting to a non-NULL value also ensures `mi_thread_done` is called.471_mi_prim_thread_associate_default_heap(heap);472}473474475// --------------------------------------------------------476// Run functions on process init/done, and thread init/done477// --------------------------------------------------------478static void mi_cdecl mi_process_done(void);479480static bool os_preloading = true; // true until this module is initialized481static bool mi_redirected = false; // true if malloc redirects to mi_malloc482483// Returns true if this module has not been initialized; Don't use C runtime routines until it returns false.484bool mi_decl_noinline _mi_preloading(void) {485return os_preloading;486}487488mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept {489return mi_redirected;490}491492// Communicate with the redirection module on Windows493#if defined(_WIN32) && defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT)494#ifdef __cplusplus495extern "C" {496#endif497mi_decl_export void _mi_redirect_entry(DWORD reason) {498// called on redirection; careful as this may be called before DllMain499if (reason == DLL_PROCESS_ATTACH) {500mi_redirected = true;501}502else if (reason == DLL_PROCESS_DETACH) {503mi_redirected = false;504}505else if (reason == DLL_THREAD_DETACH) {506mi_thread_done();507}508}509__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message);510__declspec(dllimport) void mi_cdecl mi_allocator_done(void);511#ifdef __cplusplus512}513#endif514#else515static bool mi_allocator_init(const char** message) {516if (message != NULL) *message = NULL;517return true;518}519static void mi_allocator_done(void) {520// nothing to do521}522#endif523524// Called once by the process loader525static void mi_process_load(void) {526mi_heap_main_init();527#if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)528volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true;529if (dummy == NULL) return; // use dummy or otherwise the access may get optimized away (issue #697)530#endif531os_preloading = false;532mi_assert_internal(_mi_is_main_thread());533#if !(defined(_WIN32) && defined(MI_SHARED_LIB)) // use Dll process detach (see below) instead of atexit (issue #521)534atexit(&mi_process_done);535#endif536_mi_options_init();537mi_process_setup_auto_thread_done();538mi_process_init();539if (mi_redirected) _mi_verbose_message("malloc is redirected.\n");540541// show message from the redirector (if present)542const char* msg = NULL;543mi_allocator_init(&msg);544if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) {545_mi_fputs(NULL,NULL,NULL,msg);546}547548// reseed random549_mi_random_reinit_if_weak(&_mi_heap_main.random);550}551552#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))553#include <intrin.h>554mi_decl_cache_align bool _mi_cpu_has_fsrm = false;555556static void mi_detect_cpu_features(void) {557// FSRM for fast rep movsb support (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017))558int32_t cpu_info[4];559__cpuid(cpu_info, 7);560_mi_cpu_has_fsrm = ((cpu_info[3] & (1 << 4)) != 0); // bit 4 of EDX : see <https://en.wikipedia.org/wiki/CPUID#EAX=7,_ECX=0:_Extended_Features>561}562#else563static void mi_detect_cpu_features(void) {564// nothing565}566#endif567568// Initialize the process; called by thread_init or the process loader569void mi_process_init(void) mi_attr_noexcept {570// ensure we are called once571static mi_atomic_once_t process_init;572#if _MSC_VER < 1920573mi_heap_main_init(); // vs2017 can dynamically re-initialize _mi_heap_main574#endif575if (!mi_atomic_once(&process_init)) return;576_mi_process_is_initialized = true;577_mi_verbose_message("process init: 0x%zx\n", _mi_thread_id());578mi_process_setup_auto_thread_done();579580mi_detect_cpu_features();581_mi_os_init();582mi_heap_main_init();583#if MI_DEBUG584_mi_verbose_message("debug level : %d\n", MI_DEBUG);585#endif586_mi_verbose_message("secure level: %d\n", MI_SECURE);587_mi_verbose_message("mem tracking: %s\n", MI_TRACK_TOOL);588#if MI_TSAN589_mi_verbose_message("thread santizer enabled\n");590#endif591mi_thread_init();592593#if defined(_WIN32)594// On windows, when building as a static lib the FLS cleanup happens to early for the main thread.595// To avoid this, set the FLS value for the main thread to NULL so the fls cleanup596// will not call _mi_thread_done on the (still executing) main thread. See issue #508.597_mi_prim_thread_associate_default_heap(NULL);598#endif599600mi_stats_reset(); // only call stat reset *after* thread init (or the heap tld == NULL)601mi_track_init();602603if (mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {604size_t pages = mi_option_get_clamp(mi_option_reserve_huge_os_pages, 0, 128*1024);605long reserve_at = mi_option_get(mi_option_reserve_huge_os_pages_at);606if (reserve_at != -1) {607mi_reserve_huge_os_pages_at(pages, reserve_at, pages*500);608} else {609mi_reserve_huge_os_pages_interleave(pages, 0, pages*500);610}611}612if (mi_option_is_enabled(mi_option_reserve_os_memory)) {613long ksize = mi_option_get(mi_option_reserve_os_memory);614if (ksize > 0) {615mi_reserve_os_memory((size_t)ksize*MI_KiB, true /* commit? */, true /* allow large pages? */);616}617}618}619620// Called when the process is done (through `at_exit`)621static void mi_cdecl mi_process_done(void) {622// only shutdown if we were initialized623if (!_mi_process_is_initialized) return;624// ensure we are called once625static bool process_done = false;626if (process_done) return;627process_done = true;628629// release any thread specific resources and ensure _mi_thread_done is called on all but the main thread630_mi_prim_thread_done_auto_done();631632#ifndef MI_SKIP_COLLECT_ON_EXIT633#if (MI_DEBUG || !defined(MI_SHARED_LIB))634// free all memory if possible on process exit. This is not needed for a stand-alone process635// but should be done if mimalloc is statically linked into another shared library which636// is repeatedly loaded/unloaded, see issue #281.637mi_collect(true /* force */ );638#endif639#endif640641// Forcefully release all retained memory; this can be dangerous in general if overriding regular malloc/free642// since after process_done there might still be other code running that calls `free` (like at_exit routines,643// or C-runtime termination code.644if (mi_option_is_enabled(mi_option_destroy_on_exit)) {645mi_collect(true /* force */);646_mi_heap_unsafe_destroy_all(); // forcefully release all memory held by all heaps (of this thread only!)647_mi_arena_unsafe_destroy_all(& _mi_heap_main_get()->tld->stats);648}649650if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {651mi_stats_print(NULL);652}653mi_allocator_done();654_mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id);655os_preloading = true; // don't call the C runtime anymore656}657658659660#if defined(_WIN32) && defined(MI_SHARED_LIB)661// Windows DLL: easy to hook into process_init and thread_done662__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {663MI_UNUSED(reserved);664MI_UNUSED(inst);665if (reason==DLL_PROCESS_ATTACH) {666mi_process_load();667}668else if (reason==DLL_PROCESS_DETACH) {669mi_process_done();670}671else if (reason==DLL_THREAD_DETACH) {672if (!mi_is_redirected()) {673mi_thread_done();674}675}676return TRUE;677}678679#elif defined(_MSC_VER)680// MSVC: use data section magic for static libraries681// See <https://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm>682static int _mi_process_init(void) {683mi_process_load();684return 0;685}686typedef int(*_mi_crt_callback_t)(void);687#if defined(_M_X64) || defined(_M_ARM64)688__pragma(comment(linker, "/include:" "_mi_msvc_initu"))689#pragma section(".CRT$XIU", long, read)690#else691__pragma(comment(linker, "/include:" "__mi_msvc_initu"))692#endif693#pragma data_seg(".CRT$XIU")694mi_decl_externc _mi_crt_callback_t _mi_msvc_initu[] = { &_mi_process_init };695#pragma data_seg()696697#elif defined(__cplusplus)698// C++: use static initialization to detect process start699static bool _mi_process_init(void) {700mi_process_load();701return (_mi_heap_main.thread_id != 0);702}703static bool mi_initialized = _mi_process_init();704705#elif defined(__GNUC__) || defined(__clang__)706// GCC,Clang: use the constructor attribute707static void __attribute__((constructor)) _mi_process_init(void) {708mi_process_load();709}710711#else712#pragma message("define a way to call mi_process_load on your platform")713#endif714715716