blob: 41081b88f07d0450b00ecb637af538c98e8a2f6a [file] [edit]
/* ----------------------------------------------------------------------------
Copyright (c) 2019-2026, Microsoft Research, Daan Leijen
This is free software; you can redistribute it and/or modify it under the
terms of the MIT license. A copy of the license can be found in the file
"LICENSE" at the root of this distribution.
-----------------------------------------------------------------------------*/
#pragma once
#ifndef MI_ARENA_H
#define MI_ARENA_H
#include "mimalloc/util/libc.h"
#include "mimalloc/arena/arena.h"
#include "bitmap.h"
bool _mi_arena_reserve(mi_subproc_t* subproc, size_t req_size, bool allow_large, mi_arena_id_t* arena_id);
mi_decl_nodiscard bool _mi_arena_commit(mi_subproc_t* subproc, mi_arena_t* arena, void* start, size_t size, bool* is_zero, size_t stat_already_committed);
mi_decl_noinline void* _mi_arenas_try_alloc(mi_heap_t* heap, size_t slice_count, size_t alignment, bool commit, bool allow_large, mi_arena_t* req_arena, size_t tseq, int numa_node, mi_memid_t* memid);
void* _mi_arena_os_alloc_aligned(mi_subproc_t* subproc, size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid);
// Check if an arena is suitable for a request arena
static inline bool mi_arena_is_suitable(mi_arena_t* arena, mi_arena_t* req_arena) {
if (arena == req_arena) return true; // they match
if (arena == NULL) return false;
if (req_arena == NULL && !arena->is_exclusive) return true; // or the arena is not exclusive, and we didn't request a specific one
mi_arena_t* const parent = mi_arena_parent(arena);
if (parent != NULL && parent == req_arena) return true; // sub-arena? (note that req_arena is never a sub arena)
return false;
}
// Get the number of arenas in a subproc
static inline size_t mi_arenas_get_count(mi_subproc_t* subproc) {
return mi_atomic_load_relaxed(&subproc->arena_count);
}
static inline mi_arena_t* mi_arena_from_index(mi_subproc_t* subproc, size_t idx) {
mi_assert_internal(idx < mi_arenas_get_count(subproc));
return mi_atomic_load_ptr_acquire(mi_arena_t, &subproc->arenas[idx]);
}
static inline size_t mi_arena_info_slices(mi_arena_t* arena) {
return arena->info_slices;
}
// slices of the aligned page meta data array at the start of each aligned area
static inline size_t mi_arena_page_meta_aligned_array_slices(void) {
#if MI_PAGE_META_IS_ALIGNED
return mi_divide_up(MI_PAGE_META_ALIGNED_COUNT * sizeof(mi_page_t), MI_ARENA_SLICE_SIZE);
#else
return 0;
#endif
}
// slices reserved at the start of each aligned area: the page meta data array (with a guard slice in front and after in secure mode)
static inline size_t mi_arena_page_meta_aligned_slice_count(void) {
#if MI_PAGE_META_IS_ALIGNED
return (MI_ARENA_GUARD_SLICES + mi_arena_page_meta_aligned_array_slices() + MI_ARENA_GUARD_SLICES);
#else
return 0;
#endif
}
// Start of a slice
static inline uint8_t* mi_arena_slice_start(mi_arena_t* arena, size_t slice_index) {
mi_assert_internal(slice_index < arena->slice_count);
return (mi_arena_start(arena) + _mi_size_of_slices(slice_index));
}
// Permute within chunks: arena slice counts need not be powers of two.
static inline mi_page_t* mi_arena_page_meta_at_slice(mi_arena_t* arena, size_t slice_index) {
mi_assert_internal(arena->pages_meta != NULL);
mi_assert_internal(slice_index < arena->slice_count);
mi_assert_internal((arena->slice_count % MI_BCHUNK_BITS) == 0);
mi_assert_internal(mi_is_power_of_two(MI_BCHUNK_BITS) && (MI_PAGE_META_IDX_MUL % 2) == 1); // (gcd(MI_BCHUNK_BITS, MI_PAGE_META_IDX_MUL) == 1) => one-to-one maping
const size_t idx = mi_align_down(slice_index,MI_BCHUNK_BITS) + (slice_index * MI_PAGE_META_IDX_MUL) % MI_BCHUNK_BITS; // unsigned multiply overflow is ok
return &arena->pages_meta[idx];
}
// get the arena and slice span
static inline mi_arena_t* mi_arena_from_memid(mi_memid_t memid, size_t* slice_index, size_t* slice_count) {
mi_assert_internal(memid.memkind == MI_MEM_ARENA);
mi_arena_t* arena = mi_memid_arena(memid);
if (slice_index!=NULL) { *slice_index = memid.mem.arena.slice_index; }
if (slice_count!=NULL) { *slice_count = memid.mem.arena.slice_count; }
return arena;
}
// With memory tagging, the tag class index of separate page meta data (alternating for adjacent page meta data)
static inline size_t mi_page_meta_memtag_idx(const mi_page_t* page) {
return (mi_addr(page) / sizeof(mi_page_t));
}
// With memory tagging, (re)tag the separate arena page meta entry `page` with a random tag that differs from its current tag
// and the adjacent arena meta data (see `arena.c:mi_arena_memtag_init`); returns `page` with its new tag.
// The entries are preceded by the arena bitmaps, but the last entry is followed by untagged memory (or a guard page in secure mode).
static inline mi_page_t* mi_arena_page_meta_memtag(mi_arena_t* arena, mi_page_t* page) {
const size_t i = (size_t)(mi_addr(page) - mi_addr(arena->pages_meta)) / sizeof(mi_page_t);
mi_assert_internal(i < arena->slice_count);
return (mi_page_t*)mi_memtag_meta_init_adjacent(page, sizeof(mi_page_t), mi_page_meta_memtag_idx(page), true /* has prev */, i + 1 < arena->slice_count /* has next */);
}
// initialize the bitmap for `slice_count` slices at base (and advance the base pointer)
static inline mi_bitmap_t* mi_arena_bitmap_init(size_t slice_count, uint8_t** base) {
mi_bitmap_t* bitmap = (mi_bitmap_t*)(*base);
const size_t size = mi_bitmap_init(bitmap, slice_count, true /* already zero */);
*base = (*base) + size;
return (mi_bitmap_t*)mi_ptr_bounded_data(bitmap, size);
}
typedef bool (mi_forall_arena_fun_t)(mi_arena_t* arena, const void* arg, void** result);
bool mi_forall_arenas(mi_heap_t* heap, mi_arena_t* req_arena, size_t tseq, mi_forall_arena_fun_t* visit, const void* arg, void** result);
bool mi_forall_suitable_arenas(mi_heap_t* heap, mi_arena_t* req_arena, size_t tseq, bool match_numa, int numa_node,
bool allow_large, mi_forall_arena_fun_t* visit, const void* arg, void** result);
#endif