#include "mimalloc.h"
#include "mimalloc/internal.h"
#include "mimalloc/prim.h"
#include <string.h>
#include <stdlib.h>
#define MI_MEMID_INIT(kind) {{{NULL,0}}, kind, true , true , false }
#define MI_MEMID_STATIC MI_MEMID_INIT(MI_MEM_STATIC)
const mi_page_t _mi_page_empty = {
MI_ATOMIC_VAR_INIT(MI_PAGE_IN_FULL_QUEUE), NULL, 0, 0, 0, 0, 0, NULL, MI_ATOMIC_VAR_INIT(0), 0, NULL, 0, false, #if (MI_PADDING || MI_ENCODE_FREELIST)
{ 0, 0 }, #endif
NULL, NULL, NULL, MI_ARENA_SLICE_SIZE, MI_MEMID_STATIC };
#define MI_PAGE_EMPTY() ((mi_page_t*)&_mi_page_empty)
#if (MI_PADDING>0) && (MI_INTPTR_SIZE >= 8)
#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
#elif (MI_PADDING>0)
#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
#else
#define MI_SMALL_PAGES_EMPTY { MI_INIT128(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_STATS_NULL \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
{ 0 }, { 0 }, { 0 }, { 0 }, \
{ 0 }, { 0 }, { 0 }, { 0 }, \
\
{ 0 }, { 0 }, { 0 }, { 0 }, { 0 }, \
MI_INIT4(MI_STAT_COUNT_NULL), \
{ 0 }, { 0 }, { 0 }, { 0 }, \
\
{ 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) }
static mi_decl_cache_align mi_subproc_t subproc_main
#if __cplusplus
= { }; #else
= { 0 }; #endif
static mi_decl_cache_align mi_tld_t tld_empty = {
0, 0, &subproc_main, NULL, NULL, 0, false, false, { MI_STAT_VERSION, MI_STATS_NULL }, MI_MEMID_STATIC };
mi_decl_cache_align const mi_heap_t _mi_heap_empty = {
&tld_empty, NULL, 0, { {0}, {0}, 0, true }, 0, MI_BIN_FULL, 0, 0, NULL, 0, false, true, 0, #if MI_GUARDED
0, 0, 0, 1, #endif
MI_SMALL_PAGES_EMPTY,
MI_PAGE_QUEUES_EMPTY,
MI_MEMID_STATIC
};
extern mi_decl_hidden mi_decl_cache_align mi_heap_t heap_main;
static mi_decl_cache_align mi_tld_t tld_main = {
0, 0, &subproc_main, &heap_main, &heap_main, 0, false, false, { MI_STAT_VERSION, MI_STATS_NULL }, MI_MEMID_STATIC };
mi_decl_cache_align mi_heap_t heap_main = {
&tld_main, NULL, 0, { {0x846ca68b}, {0}, 0, true }, 0, MI_BIN_FULL, 0, 0, NULL, 2, true, true, 0, #if MI_GUARDED
0, 0, 0, 0,
#endif
MI_SMALL_PAGES_EMPTY,
MI_PAGE_QUEUES_EMPTY,
MI_MEMID_STATIC
};
mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {
return _mi_prim_thread_id();
}
mi_decl_thread mi_heap_t* _mi_heap_default = (mi_heap_t*)&_mi_heap_empty;
bool _mi_process_is_initialized = false;
mi_stats_t _mi_stats_main = { MI_STAT_VERSION, MI_STATS_NULL };
#if MI_GUARDED
mi_decl_export void mi_heap_guarded_set_sample_rate(mi_heap_t* heap, size_t sample_rate, size_t seed) {
heap->guarded_sample_rate = sample_rate;
heap->guarded_sample_count = sample_rate; if (heap->guarded_sample_rate > 1) {
if (seed == 0) {
seed = _mi_heap_random_next(heap);
}
heap->guarded_sample_count = (seed % heap->guarded_sample_rate) + 1; }
}
mi_decl_export void mi_heap_guarded_set_size_bound(mi_heap_t* heap, size_t min, size_t max) {
heap->guarded_size_min = min;
heap->guarded_size_max = (min > max ? min : max);
}
void _mi_heap_guarded_init(mi_heap_t* heap) {
mi_heap_guarded_set_sample_rate(heap,
(size_t)mi_option_get_clamp(mi_option_guarded_sample_rate, 0, LONG_MAX),
(size_t)mi_option_get(mi_option_guarded_sample_seed));
mi_heap_guarded_set_size_bound(heap,
(size_t)mi_option_get_clamp(mi_option_guarded_min, 0, LONG_MAX),
(size_t)mi_option_get_clamp(mi_option_guarded_max, 0, LONG_MAX) );
}
#else
mi_decl_export void mi_heap_guarded_set_sample_rate(mi_heap_t* heap, size_t sample_rate, size_t seed) {
MI_UNUSED(heap); MI_UNUSED(sample_rate); MI_UNUSED(seed);
}
mi_decl_export void mi_heap_guarded_set_size_bound(mi_heap_t* heap, size_t min, size_t max) {
MI_UNUSED(heap); MI_UNUSED(min); MI_UNUSED(max);
}
void _mi_heap_guarded_init(mi_heap_t* heap) {
MI_UNUSED(heap);
}
#endif
static void mi_subproc_main_init(void) {
if (subproc_main.memid.memkind != MI_MEM_STATIC) {
subproc_main.memid = _mi_memid_create(MI_MEM_STATIC);
mi_lock_init(&subproc_main.os_abandoned_pages_lock);
mi_lock_init(&subproc_main.arena_reserve_lock);
}
}
static void mi_tld_main_init(void) {
if (tld_main.thread_id == 0) {
tld_main.thread_id = _mi_prim_thread_id();
}
}
static void mi_heap_main_init(void) {
if (heap_main.cookie == 0) {
heap_main.cookie = 1;
#if defined(__APPLE__) || defined(_WIN32) && !defined(MI_SHARED_LIB)
_mi_random_init_weak(&heap_main.random); #else
_mi_random_init(&heap_main.random);
#endif
heap_main.cookie = _mi_heap_random_next(&heap_main);
_mi_heap_guarded_init(&heap_main);
heap_main.allow_page_reclaim = (mi_option_get(mi_option_page_reclaim_on_free) >= 0);
heap_main.allow_page_abandon = (mi_option_get(mi_option_page_full_retain) >= 0);
heap_main.page_full_retain = mi_option_get_clamp(mi_option_page_full_retain, -1, 32);
mi_subproc_main_init();
mi_tld_main_init();
}
}
mi_heap_t* _mi_heap_main_get(void) {
mi_heap_main_init();
return &heap_main;
}
static _Atomic(size_t) thread_count = MI_ATOMIC_VAR_INIT(1);
static _Atomic(size_t) thread_total_count;
size_t _mi_current_thread_count(void) {
return mi_atomic_load_relaxed(&thread_count);
}
mi_decl_thread mi_tld_t* thread_tld = &tld_empty;
static mi_tld_t* mi_tld_alloc(void) {
mi_atomic_increment_relaxed(&thread_count);
if (_mi_is_main_thread()) {
return &tld_main;
}
else {
mi_memid_t memid;
mi_tld_t* tld = (mi_tld_t*)_mi_meta_zalloc(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;
tld->heap_backing = NULL;
tld->heaps = NULL;
tld->subproc = &subproc_main;
tld->thread_id = _mi_prim_thread_id();
tld->thread_seq = mi_atomic_add_acq_rel(&thread_total_count, 1);
tld->is_in_threadpool = _mi_prim_thread_is_in_threadpool();
return tld;
}
}
#define MI_TLD_INVALID ((mi_tld_t*)1)
mi_decl_noinline static void mi_tld_free(mi_tld_t* tld) {
if (tld != NULL && tld != MI_TLD_INVALID) {
_mi_stats_done(&tld->stats);
_mi_meta_free(tld, sizeof(mi_tld_t), tld->memid);
}
#if 0 #endif
mi_atomic_decrement_relaxed(&thread_count);
}
static mi_tld_t* mi_tld(void) {
mi_tld_t* tld = thread_tld;
if (tld == MI_TLD_INVALID) {
_mi_error_message(EFAULT, "internal error: tld is accessed after the thread terminated\n");
thread_tld = &tld_empty;
}
if (tld==&tld_empty) {
thread_tld = tld = mi_tld_alloc();
}
return tld;
}
mi_subproc_t* _mi_subproc(void) {
mi_heap_t* heap = mi_prim_get_default_heap();
if (heap == NULL) {
return _mi_subproc_main();
}
else {
return heap->tld->subproc; }
}
mi_tld_t* _mi_thread_tld(void) mi_attr_noexcept {
mi_heap_t* heap = mi_prim_get_default_heap();
if (heap == NULL) {
return &tld_empty;
}
else {
return heap->tld;
}
}
mi_subproc_t* _mi_subproc_main(void) {
return &subproc_main;
}
mi_subproc_id_t mi_subproc_main(void) {
return NULL;
}
mi_subproc_id_t mi_subproc_new(void) {
mi_memid_t memid;
mi_subproc_t* subproc = (mi_subproc_t*)_mi_meta_zalloc(sizeof(mi_subproc_t),&memid);
if (subproc == NULL) return NULL;
subproc->memid = memid;
mi_lock_init(&subproc->os_abandoned_pages_lock);
mi_lock_init(&subproc->arena_reserve_lock);
return subproc;
}
mi_subproc_t* _mi_subproc_from_id(mi_subproc_id_t subproc_id) {
return (subproc_id == NULL ? &subproc_main : (mi_subproc_t*)subproc_id);
}
void mi_subproc_delete(mi_subproc_id_t subproc_id) {
if (subproc_id == NULL) return;
mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id);
bool safe_to_delete = false;
mi_lock(&subproc->os_abandoned_pages_lock) {
if (subproc->os_abandoned_pages == NULL) {
safe_to_delete = true;
}
}
if (!safe_to_delete) return;
_mi_stats_merge_from(&_mi_subproc_main()->stats, &subproc->stats);
mi_lock_done(&subproc->os_abandoned_pages_lock);
mi_lock_done(&subproc->arena_reserve_lock);
_mi_meta_free(subproc, sizeof(mi_subproc_t), subproc->memid);
}
void mi_subproc_add_current_thread(mi_subproc_id_t subproc_id) {
mi_tld_t* tld = mi_tld();
if (tld == NULL) return;
mi_assert(tld->subproc == &subproc_main);
if (tld->subproc != &subproc_main) return;
tld->subproc = _mi_subproc_from_id(subproc_id);
}
static bool _mi_thread_heap_init(void) {
if (mi_heap_is_initialized(mi_prim_get_default_heap())) return true;
if (_mi_is_main_thread()) {
mi_heap_main_init();
_mi_heap_set_default_direct(&heap_main);
}
else {
mi_tld_t* tld = mi_tld_alloc();
mi_heap_t* heap = _mi_heap_create(0 , false , _mi_arena_id_none(), tld);
_mi_heap_set_default_direct(heap);
thread_tld = tld;
}
return false;
}
static bool _mi_thread_heap_done(mi_heap_t* heap) {
if (!mi_heap_is_initialized(heap)) return true;
_mi_heap_set_default_direct(_mi_is_main_thread() ? &heap_main : (mi_heap_t*)&_mi_heap_empty);
heap = heap->tld->heap_backing;
if (!mi_heap_is_initialized(heap)) return false;
mi_heap_t* curr = heap->tld->heaps;
while (curr != NULL) {
mi_heap_t* next = curr->next; if (curr != heap) {
mi_assert_internal(!mi_heap_is_backing(curr));
mi_heap_delete(curr);
}
curr = next;
}
mi_assert_internal(heap->tld->heaps == heap && heap->next == NULL);
mi_assert_internal(mi_heap_is_backing(heap));
if (heap != &heap_main) {
_mi_heap_collect_abandon(heap);
}
_mi_meta_free(heap, sizeof(mi_heap_t), heap->memid);
if (heap == &heap_main) {
#if 0 #endif
}
return false;
}
static void mi_process_setup_auto_thread_done(void) {
static bool tls_initialized = false; if (tls_initialized) return;
tls_initialized = true;
_mi_prim_thread_init_auto_done();
_mi_heap_set_default_direct(&heap_main);
}
bool _mi_is_main_thread(void) {
return (tld_main.thread_id==0 || tld_main.thread_id == _mi_thread_id());
}
void mi_thread_init(void) mi_attr_noexcept
{
mi_process_init();
if (_mi_thread_heap_init()) return;
mi_subproc_stat_increase(_mi_subproc_main(), threads, 1);
}
void mi_thread_done(void) mi_attr_noexcept {
_mi_thread_done(NULL);
}
void _mi_thread_done(mi_heap_t* heap)
{
if (heap == NULL) {
heap = mi_prim_get_default_heap();
if (heap == NULL) return;
}
if (!mi_heap_is_initialized(heap)) {
return;
}
mi_subproc_stat_decrease(_mi_subproc_main(), threads, 1);
if (heap->tld->thread_id != _mi_prim_thread_id()) return;
mi_tld_t* tld = heap->tld;
_mi_thread_heap_done(heap);
mi_tld_free(tld);
}
void _mi_heap_set_default_direct(mi_heap_t* heap) {
mi_assert_internal(heap != NULL);
#if defined(MI_TLS_SLOT)
mi_prim_tls_slot_set(MI_TLS_SLOT,heap);
#elif defined(MI_TLS_PTHREAD_SLOT_OFS)
*mi_prim_tls_pthread_heap_slot() = heap;
#elif defined(MI_TLS_PTHREAD)
#else
_mi_heap_default = heap;
#endif
_mi_prim_thread_associate_default_heap(heap);
}
void mi_thread_set_in_threadpool(void) mi_attr_noexcept {
mi_tld_t* tld = mi_tld();
if (tld!=NULL) {
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_process_load(void) {
mi_heap_main_init();
#if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
volatile mi_heap_t* dummy = _mi_heap_default; if (dummy == NULL) return; #endif
os_preloading = false;
mi_assert_internal(_mi_is_main_thread());
_mi_options_init();
mi_process_setup_auto_thread_done();
mi_process_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_random_reinit_if_weak(&heap_main.random);
}
#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))
#include <intrin.h>
mi_decl_cache_align bool _mi_cpu_has_fsrm = false;
mi_decl_cache_align bool _mi_cpu_has_erms = false;
static void mi_detect_cpu_features(void) {
int32_t cpu_info[4];
__cpuid(cpu_info, 7);
_mi_cpu_has_fsrm = ((cpu_info[3] & (1 << 4)) != 0); _mi_cpu_has_erms = ((cpu_info[2] & (1 << 9)) != 0); }
#else
static void mi_detect_cpu_features(void) {
}
#endif
void mi_process_init(void) mi_attr_noexcept {
static mi_atomic_once_t process_init;
#if _MSC_VER < 1920
mi_heap_main_init(); #endif
if (!mi_atomic_once(&process_init)) return;
_mi_process_is_initialized = true;
_mi_verbose_message("process init: 0x%zx\n", _mi_thread_id());
mi_detect_cpu_features();
_mi_os_init();
_mi_page_map_init(); mi_heap_main_init();
mi_tld_main_init();
mi_subproc_main_init();
mi_process_setup_auto_thread_done();
#if MI_DEBUG
_mi_verbose_message("debug level : %d\n", MI_DEBUG);
#endif
_mi_verbose_message("secure level: %d\n", MI_SECURE);
_mi_verbose_message("mem tracking: %s\n", MI_TRACK_TOOL);
#if MI_TSAN
_mi_verbose_message("thread santizer enabled\n");
#endif
mi_thread_init();
#if defined(_WIN32) && defined(MI_WIN_USE_FLS)
_mi_prim_thread_associate_default_heap(NULL);
#endif
mi_stats_reset(); 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);
long reserve_at = mi_option_get(mi_option_reserve_huge_os_pages_at);
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);
}
}
}
void mi_cdecl _mi_process_done(void) {
if (!_mi_process_is_initialized) return;
static bool process_done = false;
if (process_done) return;
process_done = true;
mi_heap_t* heap = mi_prim_get_default_heap(); mi_assert_internal(heap != NULL);
_mi_prim_thread_done_auto_done();
#ifndef MI_SKIP_COLLECT_ON_EXIT
#if (MI_DEBUG || !defined(MI_SHARED_LIB))
mi_heap_collect(heap, true );
#endif
#endif
if (mi_option_is_enabled(mi_option_destroy_on_exit)) {
mi_heap_collect(heap, true );
_mi_heap_unsafe_destroy_all(heap); _mi_arenas_unsafe_destroy_all(heap->tld);
}
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
mi_stats_print(NULL);
}
_mi_allocator_done();
_mi_verbose_message("process done: 0x%zx\n", tld_main.thread_id);
os_preloading = true; }