#include "mimalloc.h"
#include "mimalloc/internal.h"
#include "mimalloc/prim.h"
#include "mimalloc/prim-tls.h"
#include <string.h>
#include <stdlib.h>
static const mi_page_t mi_page_empty = {
#if MI_PAGE_META_IS_ALIGNED
MI_ATOMIC_VAR_INIT(NULL), #endif
MI_ATOMIC_VAR_INIT(0), NULL, 0, NULL, 0, 0, 0, 0, 0, 0, false, MI_ATOMIC_VAR_INIT(0), NULL, NULL, NULL, NULL, MI_MEMID_STATIC, #if (MI_PADDING || MI_ENCODE_FREELIST)
#if MI_PAGE_KEY_COUNT==2
{ 0, 0 }, #else
{ 0 }, #endif
#endif
};
#define MI_PAGE_EMPTY() ((mi_page_t*)&mi_page_empty)
#if MI_SMALL_WSIZE_MAX == 128
#define MI_INIT_PAGES_DIRECT(p) MI_INIT128(p)
#elif MI_SMALL_WSIZE_MAX == 256
#define MI_INIT_PAGES_DIRECT(p) MI_INIT128(p), MI_INIT128(p)
#else
#error define initializer for direct pages
#endif
#if (MI_PADDING>0) && (MI_INTPTR_SIZE >= 8)
#define MI_SMALL_PAGES_EMPTY { MI_INIT_PAGES_DIRECT(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
#elif (MI_PADDING>0)
#define MI_SMALL_PAGES_EMPTY { MI_INIT_PAGES_DIRECT(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
#else
#define MI_SMALL_PAGES_EMPTY { MI_INIT_PAGES_DIRECT(MI_PAGE_EMPTY), MI_PAGE_EMPTY() }
#endif
#define QNULL(sz) { NULL, NULL, 0, (sz)*sizeof(uintptr_t) }
#define MI_PAGE_QUEUES_EMPTY \
{ QNULL(1), \
QNULL( 1), QNULL( 2), QNULL( 3), QNULL( 4), QNULL( 5), QNULL( 6), QNULL( 7), QNULL( 8), \
QNULL( 10), QNULL( 12), QNULL( 14), QNULL( 16), QNULL( 20), QNULL( 24), QNULL( 28), QNULL( 32), \
QNULL( 40), QNULL( 48), QNULL( 56), QNULL( 64), QNULL( 80), QNULL( 96), QNULL( 112), QNULL( 128), \
QNULL( 160), QNULL( 192), QNULL( 224), QNULL( 256), QNULL( 320), QNULL( 384), QNULL( 448), QNULL( 512), \
QNULL( 640), QNULL( 768), QNULL( 896), QNULL( 1024), QNULL( 1280), QNULL( 1536), QNULL( 1792), QNULL( 2048), \
QNULL( 2560), QNULL( 3072), QNULL( 3584), QNULL( 4096), QNULL( 5120), QNULL( 6144), QNULL( 7168), QNULL( 8192), \
QNULL( 10240), QNULL( 12288), QNULL( 14336), QNULL( 16384), QNULL( 20480), QNULL( 24576), QNULL( 28672), QNULL( 32768), \
QNULL( 40960), QNULL( 49152), QNULL( 57344), QNULL( 65536), QNULL( 81920), QNULL( 98304), QNULL(114688), QNULL(131072), \
QNULL(163840), QNULL(196608), QNULL(229376), QNULL(262144), QNULL(327680), QNULL(393216), QNULL(458752), QNULL(524288), \
QNULL(MI_LARGE_MAX_OBJ_WSIZE + 1 ), \
QNULL(MI_LARGE_MAX_OBJ_WSIZE + 2) }
#define MI_STAT_COUNT_NULL() {0,0,0}
#define MI_STAT_COUNT(stat) {0,0,0},
#define MI_STAT_COUNTER(stat) {0},
#define MI_STATS_FIELDS_NULL \
MI_STAT_FIELDS() \
{ MI_INIT4(MI_STAT_COUNT_NULL) }, \
{ { 0 }, { 0 }, { 0 }, { 0 } }, \
{ MI_INIT74(MI_STAT_COUNT_NULL) }, \
{ MI_INIT74(MI_STAT_COUNT_NULL) }, \
{ MI_INIT5(MI_STAT_COUNT_NULL) }
#define MI_STATS_NULL \
{ sizeof(mi_stats_t), MI_STAT_VERSION, MI_STATS_FIELDS_NULL }
static mi_decl_cache_align mi_tld_t mi_tld_detached = {
MI_THREADID_DETACHED, 0, 0, NULL, NULL, MI_LOCK_INITIALIZER, false, false, MI_MEMID_STATIC };
mi_decl_hidden mi_decl_cache_align const mi_theap_t _mi_theap_empty = {
MI_SMALL_PAGES_EMPTY, &mi_tld_detached, MI_ATOMIC_VAR_INIT(NULL), MI_ATOMIC_VAR_INIT(NULL), MI_ATOMIC_VAR_INIT(1), 0, 0, { {0}, {0}, 0, true }, 0, MI_BIN_FULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, 0, false, true, true, #if MI_GUARDED
0, 0, 0, 1, #endif
MI_PAGE_QUEUES_EMPTY,
MI_MEMID_STATIC,
MI_STATS_NULL, };
#undef MI_STAT_COUNT
#undef MI_STAT_COUNTER
static mi_decl_cache_align mi_heap_t mi_process_heap_main = mi_init_struct_zero;
static mi_decl_cache_align mi_theap_t mi_process_theap_meta = mi_init_struct_zero;
static mi_decl_cache_align mi_tld_t mi_process_tld_main = mi_init_struct_zero;
static mi_decl_cache_align mi_theap_t mi_process_theap_main = mi_init_struct_zero;
mi_decl_hidden mi_decl_cache_align mi_theap_t _mi_theap_empty_wrong = mi_init_struct_zero; mi_decl_hidden bool _mi_process_is_initialized = false;
mi_page_t* _mi_page_empty_get(void) {
return (mi_page_t*)&mi_page_empty;
}
mi_decl_cold mi_decl_noinline mi_theap_t* _mi_theap_empty_get(void) {
return (mi_theap_t*)&_mi_theap_empty;
}
bool _mi_is_empty_theap(const mi_theap_t* theap) {
return (theap == &_mi_theap_empty);
}
static mi_tld_t* mi_tld_init(mi_tld_t* tld, size_t tseq, mi_subproc_t* subproc);
static void mi_heap_main_init_once(void) {
mi_memid_t memid_static = _mi_memid_create(MI_MEM_STATIC);
_mi_memcpy(&_mi_theap_empty_wrong,&_mi_theap_empty,sizeof(_mi_theap_empty_wrong));
mi_subproc_t* subproc_main = _mi_subproc_main_init();
mi_tld_detached.memid = memid_static;
mi_tld_init(&mi_tld_detached, 0, subproc_main);
mi_process_heap_main.memid = memid_static;
mi_atomic_store_ptr_release(mi_heap_t,&subproc_main->heap_main,&mi_process_heap_main);
_mi_heap_init(&mi_process_heap_main,mi_thread_local_key_fast,subproc_main,0);
mi_process_theap_meta.memid = memid_static;
_mi_theap_init(&mi_process_theap_meta,&mi_process_heap_main,&mi_tld_detached);
mi_process_theap_meta.allow_page_abandon = false; mi_process_theap_meta.page_full_retain = 2;
subproc_main->theap_meta = &mi_process_theap_meta;
}
static void mi_heap_main_init(void) {
mi_atomic_do_once {
mi_heap_main_init_once();
}
}
mi_heap_t* _mi_subproc_heap_main(mi_subproc_t* subproc) {
mi_heap_t* heap = mi_atomic_load_ptr_acquire(mi_heap_t,&subproc->heap_main);
if mi_likely(heap!=NULL) {
return heap;
}
else if (_mi_subproc_is_main(subproc)) {
mi_heap_main_init();
mi_assert_internal(mi_atomic_load_ptr_acquire(mi_heap_t,&subproc->heap_main) != NULL);
return mi_atomic_load_ptr_acquire(mi_heap_t,&subproc->heap_main);
}
else {
mi_assert_internal(false);
return &mi_process_heap_main;
}
}
static mi_tld_t* mi_tld_init(mi_tld_t* tld, size_t tseq, mi_subproc_t* subproc) {
tld->subproc = subproc;
tld->theaps = NULL;
mi_lock_init(&tld->theaps_lock);
if (tld->thread_id == MI_THREADID_DETACHED) {
tld->numa_node = -1;
}
else {
tld->numa_node = _mi_os_numa_node();
tld->thread_id = _mi_prim_thread_id();
tld->is_in_threadpool = _mi_prim_thread_is_in_threadpool();
tld->thread_seq = tseq;
mi_atomic_increment_relaxed(&tld->subproc->thread_count);
}
return tld;
}
static mi_tld_t* mi_tld_create(mi_subproc_t* subproc) {
mi_assert_internal(subproc->theap_meta != NULL); const size_t tseq = mi_atomic_increment_relaxed(&subproc->thread_total_count);
mi_memid_t memid;
mi_tld_t* tld;
if (_mi_subproc_is_main(subproc) && tseq==0 ) {
tld = &mi_process_tld_main;
memid = _mi_memid_create_static(tld,sizeof(*tld));
}
else {
tld = (mi_tld_t*)_mi_meta_zalloc(subproc, sizeof(mi_tld_t), &memid);
}
if (tld==NULL) {
_mi_error_message(ENOMEM, "unable to allocate memory for thread local data\n");
return NULL;
}
tld->memid = memid;
return mi_tld_init(tld,tseq,subproc);
}
mi_decl_noinline static void mi_tld_free(mi_tld_t* tld) {
if (tld==NULL) return;
mi_atomic_decrement_relaxed(&tld->subproc->thread_count);
tld->thread_id = (mi_threadid_t)(~0); mi_lock_done(&tld->theaps_lock);
_mi_meta_free(tld->subproc, tld, tld->memid); }
#if MI_DEBUG || defined(MI_TLS_RECURSE_GUARD)
static mi_theap_t* mi_heap_check_for_existing_theap(mi_heap_t* heap) {
const mi_threadid_t tid = _mi_thread_id();
mi_theap_t* thread_theap = NULL;
mi_lock(&heap->theaps_lock) {
for(mi_theap_t* theap = heap->theaps; theap != NULL; theap = theap->hnext ) {
if (theap->tld->thread_id == tid) {
thread_theap = theap;
break;
}
}
}
return thread_theap;
}
#endif
mi_theap_t* _mi_thread_init_with_heap(mi_heap_t* heap_main)
{
mi_process_init();
mi_theap_t* theap = _mi_theap_default();
if (mi_theap_is_initialized(theap)) return theap;
if (heap_main==NULL) {
heap_main = mi_heap_main();
mi_assert_internal(heap_main == &mi_process_heap_main);
}
mi_assert_internal(heap_main!=NULL);
#if MI_DEBUG || defined(MI_TLS_RECURSE_GUARD)
theap = mi_heap_check_for_existing_theap(heap_main); #if !defined(MI_TLS_RECURSE_GUARD)
mi_assert_internal(theap==NULL);
#endif
#else
theap = NULL;
#endif
if (theap==NULL) {
mi_tld_t* tld = mi_tld_create(heap_main->subproc);
if (tld==NULL) return NULL; if (tld==&mi_process_tld_main) {
theap = &mi_process_theap_main; theap->memid = _mi_memid_create_static(theap,sizeof(*theap));
}
else {
theap = _mi_theap_alloc(heap_main,tld); if (theap==NULL) { mi_tld_free(tld); return NULL; } }
_mi_theap_init(theap,heap_main,tld);
}
_mi_theap_default_set(theap);
_mi_heap_theap_set(heap_main, theap);
mi_assert_internal(mi_theap_is_initialized(theap));
mi_theap_t* const heap_theap = (heap_main==NULL ? NULL : (mi_theap_t*)_mi_thread_local_get(heap_main->theap));
mi_assert_internal(heap_main==NULL || heap_theap == theap); MI_UNUSED_RELEASE(heap_theap);
mi_subproc_stat_increase(_mi_theap_subproc(theap), threads, 1); return theap;
}
mi_theap_t* _mi_thread_init(void) {
return _mi_thread_init_with_heap(NULL);
}
void mi_decl_noinline mi_thread_init(void) mi_attr_noexcept {
_mi_thread_init();
}
static void mi_thread_theaps_done(mi_tld_t* tld)
{
mi_lock(&tld->theaps_lock) {
mi_theap_t* theap = tld->theaps;
while (theap != NULL) {
mi_theap_t* next = theap->tnext;
_mi_theap_collect_abandon(theap);
mi_assert_internal(theap->page_count==0);
theap = next;
}
}
_mi_theap_default_set((mi_theap_t*)&_mi_theap_empty);
_mi_theap_cached_set((mi_theap_t*)&_mi_theap_empty);
_mi_tld_detach_theaps(tld);
mi_lock(&tld->theaps_lock) { mi_theap_t* theap = tld->theaps;
tld->theaps = NULL;
while (theap != NULL) {
mi_theap_t* next = theap->tnext;
mi_assert_internal(theap->page_count==0);
mi_assert_internal(_mi_theap_heap_peek(theap)==NULL);
theap->tld = NULL;
theap->tnext = NULL;
theap->tprev = NULL;
mi_assert_internal(mi_atomic_load_relaxed(&theap->refcount) == 1); _mi_theap_decref(theap);
theap = next;
}
}
mi_assert(_mi_theap_default()==(mi_theap_t*)&_mi_theap_empty); mi_assert(!mi_theap_is_initialized(_mi_theap_default()));
}
static void mi_process_setup_auto_thread_done(void) {
mi_atomic_do_once {
_mi_prim_thread_init_auto_done();
}
}
void mi_thread_done(void) mi_attr_noexcept {
_mi_thread_done(NULL);
}
void _mi_thread_done(mi_theap_t* _theap_main)
{
if (_theap_main==NULL) {
_theap_main = _mi_theap_default();
}
if (!mi_theap_is_initialized(_theap_main)) {
return;
}
mi_tld_t* const tld = _theap_main->tld;
_mi_thread_locals_thread_done();
mi_subproc_stat_decrease(tld->subproc, threads, 1);
if (tld->thread_id != _mi_prim_thread_id()) return;
mi_thread_theaps_done(tld);
mi_tld_free(tld);
}
void mi_thread_set_in_threadpool(void) mi_attr_noexcept {
mi_theap_t* theap = mi_theap_get_default();
theap->tld->is_in_threadpool = true;
}
static bool os_preloading = true;
bool mi_decl_noinline _mi_preloading(void) {
return os_preloading;
}
mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept {
return _mi_is_redirected();
}
void _mi_auto_process_init(void) {
os_preloading = false;
mi_process_init();
mi_process_setup_auto_thread_done();
_mi_options_post_init(); if (_mi_is_redirected()) _mi_verbose_message("malloc is redirected.\n");
const char* msg = NULL;
_mi_allocator_init(&msg);
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_theap_t* theap = _mi_theap_default();
if (theap != NULL) {
_mi_random_reinit_if_weak(&theap->random);
mi_subproc_t* subproc = _mi_theap_subproc(theap);
if (subproc->theap_meta != NULL) {
mi_lock(&subproc->theap_meta_lock) {
_mi_random_reinit_if_weak(&subproc->theap_meta->random);
}
}
}
}
static void mi_process_init_once(void) {
#if defined(__CYGWIN__)
__mi_thread_id_helper = NULL;
#endif
_mi_verbose_message("process init: 0x%zx\n", _mi_thread_id());
_mi_detect_cpu_features();
_mi_options_init(); _mi_stats_init(); _mi_os_init();
mi_heap_main_init(); _mi_page_map_init(); mi_thread_init();
_mi_tls_slots_init(); _mi_thread_locals_init(); _mi_process_is_initialized = true;
#if defined(_WIN32) && defined(MI_WIN_INIT_USE_FLS)
_mi_prim_thread_associate_default_theap(NULL);
#endif
mi_track_init();
if (mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
size_t pages = mi_option_get_clamp(mi_option_reserve_huge_os_pages, 0, 128*1024);
int reserve_at = (int)mi_option_get_clamp(mi_option_reserve_huge_os_pages_at, -1, INT_MAX);
if (reserve_at != -1) {
mi_reserve_huge_os_pages_at(pages, reserve_at, pages*500);
} else {
mi_reserve_huge_os_pages_interleave(pages, 0, pages*500);
}
}
if (mi_option_is_enabled(mi_option_reserve_os_memory)) {
long ksize = mi_option_get(mi_option_reserve_os_memory);
if (ksize > 0) {
mi_reserve_os_memory((size_t)ksize*MI_KiB, true, true);
}
}
#if MI_PAGE_META_IS_ALIGNED
mi_assert_internal((sizeof(mi_page_t)%MI_SIZE_SIZE) == 0); #endif
}
void mi_process_init(void) mi_attr_noexcept {
mi_atomic_do_once {
mi_process_init_once();
}
}
static void mi_process_done_once(void) {
if (!_mi_process_is_initialized) return;
static bool process_done = false;
if (process_done) return;
process_done = true;
_mi_theap_cached_set(_mi_theap_empty_get());
_mi_prim_thread_done_auto_done();
#ifndef MI_SKIP_COLLECT_ON_EXIT
#if (MI_DEBUG || !defined(MI_SHARED_LIB))
mi_theap_collect(_mi_theap_default(), true );
#endif
#endif
mi_track_done();
mi_subproc_t* subproc_main = _mi_subproc_main();
if (mi_option_is_enabled(mi_option_destroy_on_exit)) {
_mi_subprocs_unsafe_destroy_all(); }
else {
_mi_thread_locals_thread_done();
_mi_thread_locals_done();
if (subproc_main->heap_main != NULL) {
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
_mi_theap_merge_stats(subproc_main->theap_meta);
_mi_theap_merge_stats(_mi_theap_default()); mi_heap_stats_merge_to_subproc(subproc_main->heap_main);
mi_subproc_stats_print_out(mi_subproc_main(), NULL, NULL); }
}
}
_mi_tls_slots_done();
_mi_subproc_main_done();
_mi_allocator_done();
_mi_verbose_message("process done %zu\n", sizeof(mi_page_t)); os_preloading = true; }
void mi_cdecl mi_process_done(void) mi_attr_noexcept {
mi_atomic_do_once {
mi_process_done_once();
}
}
void mi_cdecl _mi_auto_process_done(void) mi_attr_noexcept {
if (_mi_option_get_fast(mi_option_destroy_on_exit)>=2) return; mi_process_done();
}