#include "mimalloc.h"
#include "mimalloc/internal.h"
#include "mimalloc/prim-tls.h"
static mi_decl_cache_align mi_subproc_t mi_process_subproc_main = mi_init_struct_zero;
static mi_subproc_t* mi_subprocs = NULL;
static mi_lock_t mi_subprocs_lock = MI_LOCK_INITIALIZER;
void* _mi_meta_zalloc( mi_subproc_t* subproc, size_t size, mi_memid_t* memid ) {
mi_assert_internal(subproc->theap_meta != NULL);
void* p = NULL;
mi_lock(&subproc->theap_meta_lock) {
p = mi_theap_zalloc(subproc->theap_meta, size);
if (memid != NULL) { *memid = (p==NULL ? _mi_memid_none() : _mi_memid_create_malloc(p,size,true) ); }
}
return p;
}
void* _mi_meta_zalloc_aligned( mi_subproc_t* subproc, size_t size, size_t aligned, mi_memid_t* memid ) {
mi_assert_internal(subproc->theap_meta != NULL);
void* p = NULL;
mi_lock(&subproc->theap_meta_lock) {
p = mi_theap_zalloc_aligned(subproc->theap_meta, size, aligned);
if (memid != NULL) { *memid = (p==NULL ? _mi_memid_none() : _mi_memid_create_malloc(p,size,true) ); }
}
return p;
}
void* _mi_meta_rezalloc( mi_subproc_t* subproc, void* oldp, size_t newsize, mi_memid_t* memid ) {
mi_assert_internal(subproc->theap_meta != NULL);
void* p = NULL;
mi_lock(&subproc->theap_meta_lock) {
p = mi_theap_zalloc(subproc->theap_meta, newsize);
}
if (p!=NULL) {
if (oldp!=NULL) {
const size_t oldsize = mi_usable_size(oldp);
const size_t copysize = (newsize < oldsize ? newsize : oldsize);
_mi_memcpy(p,oldp,copysize);
if (memid!=NULL) { _mi_meta_free(subproc,oldp,*memid); }
else { mi_free(oldp); }
}
if (memid!=NULL) { *memid = _mi_memid_create_malloc(p,newsize,true); }
}
else {
if (memid!=NULL) { *memid = _mi_memid_none(); }
}
return p;
}
void _mi_meta_free(mi_subproc_t* subproc, void* p, mi_memid_t memid) {
if (p==NULL || mi_memid_needs_no_free(memid)) return;
if (memid.memkind == MI_MEM_MALLOC) {
mi_free(p);
}
else {
mi_assert_internal(subproc!=NULL);
_mi_arenas_free(subproc, p, _mi_memid_size(memid), memid);
}
}
bool _mi_meta_is_meta_page(const mi_subproc_t* subproc, const mi_page_t* page) {
if (page==NULL) return false;
mi_theap_t* theap = page->theap;
return (theap != NULL && theap == subproc->theap_meta);
}
mi_subproc_t* _mi_subproc_main(void) {
return &mi_process_subproc_main;
}
bool _mi_subproc_is_main(mi_subproc_t* subproc) {
return (subproc == &mi_process_subproc_main);
}
mi_subproc_t* _mi_subproc(void) {
mi_theap_t* theap = _mi_theap_default();
if (theap == NULL || theap->tld == NULL) { return _mi_subproc_main();
}
else {
return theap->tld->subproc;
}
}
mi_heap_t* mi_heap_main(void) {
return _mi_subproc_heap_main(_mi_subproc()); }
mi_subproc_t* _mi_subproc_from_id(mi_subproc_id_t subproc_id) {
return (mi_subproc_t*)(subproc_id._mi_subproc_id);
}
mi_subproc_id_t _mi_subproc_to_id(mi_subproc_t* subproc) {
mi_subproc_id_t id = { subproc };
return id;
}
mi_subproc_id_t mi_subproc_main(void) {
return _mi_subproc_to_id(_mi_subproc_main());
}
mi_subproc_id_t mi_subproc_current(void) {
return _mi_subproc_to_id(_mi_subproc());
}
static mi_subproc_t* mi_subproc_init(mi_subproc_t* subproc, mi_subproc_t* parent) {
static _Atomic(size_t) subproc_total_count;
subproc->parent = parent;
subproc->subproc_seq = mi_atomic_increment_relaxed(&subproc_total_count);
mi_stats_header_init(&subproc->stats);
mi_lock_init(&subproc->arena_reserve_lock);
mi_lock_init(&subproc->heaps_lock);
mi_lock_init(&subproc->theap_meta_lock);
mi_lock(&mi_subprocs_lock) {
subproc->next = mi_subprocs;
if (mi_subprocs!=NULL) { mi_subprocs->prev = subproc; }
mi_subprocs = subproc;
}
return subproc;
}
mi_subproc_id_t mi_subproc_new(void) {
mi_thread_init();
mi_subproc_t* const parent = _mi_subproc();
mi_memid_t memid;
mi_subproc_t* const subproc = (mi_subproc_t*)_mi_meta_zalloc(parent, sizeof(mi_subproc_t), &memid);
if (subproc == NULL) { return _mi_subproc_to_id(NULL); }
subproc->memid = memid;
mi_memid_t theap_memid;
mi_theap_t* const theap_meta = (mi_theap_t*)_mi_meta_zalloc(parent, sizeof(mi_theap_t), &theap_memid);
if (theap_meta==NULL) {
_mi_meta_free(parent, subproc, memid);
return _mi_subproc_to_id(NULL);
}
theap_meta->memid = memid;
mi_subproc_init(subproc,parent);
mi_heap_t* heap_main = _mi_heap_new_for_subproc(subproc,0,true);
if (heap_main==NULL) {
_mi_meta_free(parent, theap_meta, theap_meta->memid);
mi_subproc_destroy(_mi_subproc_to_id(subproc));
return _mi_subproc_to_id(NULL);
}
mi_assert_internal(subproc->heap_main == heap_main);
mi_assert_internal(parent->theap_meta!=NULL);
mi_assert_internal(parent->theap_meta->tld!=NULL);
mi_assert_internal(parent->theap_meta->tld->thread_id == MI_THREADID_DETACHED);
_mi_theap_init(theap_meta,heap_main,parent->theap_meta->tld );
subproc->theap_meta = theap_meta;
return _mi_subproc_to_id(subproc);
}
static void mi_subproc_unsafe_destroy(mi_subproc_t* subproc, bool acquire_subprocs_lock)
{
if (subproc==NULL) return;
mi_lock_maybe(&mi_subprocs_lock, acquire_subprocs_lock) {
if (subproc->next!=NULL) { subproc->next->prev = subproc->prev; }
if (subproc->prev!=NULL) { subproc->prev->next = subproc->next; }
else { mi_assert_internal(mi_subprocs==subproc); mi_subprocs = subproc->next; }
}
mi_lock(&subproc->heaps_lock) {
mi_heap_t* heap = subproc->heaps;
while (heap != NULL) {
mi_heap_t* next = heap->next;
if (heap!=subproc->heap_main) { _mi_heap_force_destroy(heap, false ); }
heap = next;
}
mi_assert_internal(subproc->heap_main==NULL || subproc->heaps == subproc->heap_main);
if (subproc->heap_main!=NULL) {
_mi_thread_locals_thread_done(); if (_mi_subproc_is_main(subproc)) {
_mi_thread_locals_done();
}
_mi_heap_force_destroy(subproc->heap_main, false ); }
}
subproc->theap_meta = NULL;
if (!_mi_subproc_is_main(subproc)) {
_mi_stats_merge_into(&mi_process_subproc_main.stats, &subproc->stats);
}
_mi_arenas_unsafe_destroy_all(subproc);
if (_mi_subproc_is_main(subproc)) {
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
mi_subproc_stats_print_out(mi_subproc_main(), NULL, NULL);
}
}
mi_lock_done(&subproc->arena_reserve_lock);
mi_lock_done(&subproc->heaps_lock);
mi_lock_done(&subproc->theap_meta_lock);
_mi_meta_free( subproc->parent, subproc, subproc->memid);
if (_mi_subproc_is_main(subproc)) {
_mi_page_map_unsafe_destroy();
}
}
void mi_subproc_destroy(mi_subproc_id_t subproc_id) {
mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id);
if (subproc==NULL || subproc==&mi_process_subproc_main) return;
mi_subproc_unsafe_destroy(subproc, true );
}
void _mi_subprocs_unsafe_destroy_all(void) {
mi_lock(&mi_subprocs_lock) {
mi_subproc_t* subproc = mi_subprocs;
while (subproc!=NULL) {
mi_subproc_t* next = subproc->next;
if (subproc!=&mi_process_subproc_main) {
mi_subproc_unsafe_destroy(subproc, false );
}
subproc = next;
}
}
mi_subproc_unsafe_destroy(&mi_process_subproc_main, true );
}
void mi_subproc_add_current_thread(mi_subproc_id_t subproc_id) {
mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id);
mi_assert_internal(subproc!=NULL);
if (subproc==NULL) return;
mi_assert_internal(subproc->heap_main!=NULL);
if (subproc->heap_main==NULL) return;
mi_theap_t* theap = _mi_theap_default();
if (mi_theap_is_initialized(theap)) {
if (theap->tld!=NULL && theap->tld->subproc != subproc) {
_mi_warning_message("unable to add thread to the subprocess as it was already in another subprocess (at %p)\n", theap->tld->subproc);
}
return;
}
_mi_thread_init_with_heap(subproc->heap_main);
}
bool mi_subproc_visit_heaps(mi_subproc_id_t subproc_id, mi_heap_visit_fun* visitor, void* arg) {
mi_subproc_t* subproc = _mi_subproc_from_id(subproc_id);
if (subproc==NULL) return false;
bool ok = true;
mi_lock(&subproc->heaps_lock) {
for (mi_heap_t* heap = subproc->heaps; heap!=NULL && ok; heap = heap->next) {
ok = (*visitor)(heap, arg);
}
}
return ok;
}
mi_subproc_t* _mi_subproc_main_init(void) {
mi_lock_init(&mi_subprocs_lock);
mi_memid_t memid = _mi_memid_create_static(&mi_process_subproc_main,sizeof(mi_subproc_t));
mi_process_subproc_main.memid = memid;
mi_subproc_init(&mi_process_subproc_main,NULL);
return &mi_process_subproc_main;
}
void _mi_subproc_main_done(void) {
mi_lock_done(&mi_subprocs_lock);
}