29#elif defined(__unix__) || defined(__APPLE__)
31#elif defined(__wasm__)
32# define WASM_PAGE_SIZE 65536U
36# define MM_LOG(FMT, ...) DBG("MM", FMT __VA_OPT__(, ) __VA_ARGS__)
38# define MM_LOG(FMT, ...)
46 PANIC_IF(bit >= 256,
"Bit position is out of block_bitmap boundaries.");
49 uint64_t index = bit >> 6U;
51 unsigned offset = bit & 63U;
53 return ((mm->
block_bitmap[index] >> offset) & 1ULL) == 0;
57 PANIC_IF(bit >= 256,
"Bit position is out of block_bitmap boundaries.");
60 uint64_t index = bit >> 6U;
62 unsigned offset = bit & 63U;
68 PANIC_IF(bit >= 256,
"Bit position is out of block_bitmap boundaries.");
70 uint64_t index = bit >> 6U;
71 unsigned offset = bit & 63U;
72 uint64_t mask = ~(1ULL << offset);
77#if defined(__GNUC__) || defined(__clang__)
78 for (
int i = 0; i < 4; i++) {
83 int bit = __builtin_ctzll(~word);
84 return (i * 64) + bit;
90 for (
int i = 0; i < 4; i++) {
93 uint64_t mask = ~word;
95 while ((mask & 1ULL) == 0) {
99 return (i * 64) + bit;
113 return mm->
blocks[block_id];
124 PANIC_IF(size % alignment != 0,
"'size' should be a multiple of alignment");
127 return _aligned_malloc(size, alignment);
130 return aligned_alloc(alignment, size);
162 return realloc(ptr, new_size);
187 block->
next =
nullptr;
223 while (block !=
nullptr) {
230 mm->
blocks[root_id] =
nullptr;
245 alignment == 0 || (alignment & (alignment - 1)) != 0,
246 "'alignment' must be a nonzero power of two"
254 TODO(
"We need to allocate a larger block");
256 MM_LOG(
"Allocating %zu with %zu alignment", size, alignment);
258 MM_LOG(
"Root block: %p", (
void *)root_block);
260 MM_LOG(
"Walking the block chain");
262 MM_LOG(
"Next block: %p", (
void *)target_block);
267 uintptr_t base = (uintptr_t)target_block->base;
269 (base + target_block->offset + (alignment - 1)) & ~((uintptr_t)alignment - 1);
270 size_t aligned_offset = (size_t)(aligned - base);
273 MM_LOG(
"Calculated aligned offset 0x%zx", aligned_offset);
275 if (aligned_offset + size > capacity) {
276 MM_LOG(
"We can't allocate in this block");
277 if (target_block->next !=
nullptr) {
279 target_block = target_block->next;
283 MM_LOG(
"We have to allocate a new block");
286 target_block->next = b;
288 MM_LOG(
"New block: %p", (
void *)target_block);
291 MM_LOG(
"We have found enough space");
293 void *ptr = (
void *)aligned;
294 target_block->offset = aligned_offset + size;
307 return (
size_t)si.dwPageSize;
308#elif defined(__unix__) || defined(__APPLE__)
309 long sz = sysconf(_SC_PAGESIZE);
311#elif defined(__wasm__)
312 return WASM_PAGE_SIZE;
323 if (config !=
nullptr) {
336 const size_t magnitude =
345 "Wrong block size. Configure mm.block_size_magnitude to a larger number"
366 mm->stats.allocated_pages = 0;
367 mm->stats.total_allocations = 0;
368 mm->stats.total_os_allocations = 0;
369 mm->stats.total_blocks = 0;
379 if (mm->
blocks[i] !=
nullptr) {
386 while (block !=
nullptr) {
399 MM_LOG(
"Allocating %zu bytes with %zu bytes alignment in block: %zu", size, alignment, block_id);
431 PANIC_IF(index == -1,
"Out of memory: all block ids are in use");
437 "Miscalculated the block id. It is not free. This is a bug!"
445 mm->stats.total_blocks++;
446 mm->stats.total_os_allocations++;
461 printf(
"Block: %zu@%" PRIXPTR
"\n",
id, (uintptr_t)block);
462 printf(
"======================================================\n");
464 if (block->
next ==
nullptr) {
467 printf(
"%" PRIXPTR
"\n", (uintptr_t)block->
next);
471void srn_mm_print_blocks_summary(
srn_mm_t *mm) {
473 if (mm->
blocks[d] !=
nullptr) {
474 srn_mm_print_block_summary(mm, d);
void srn_config_validate(const srn_configuration_t *config)
A configuration with every field set to its default.
#define SRN_CONFIG_DEFAULT_BLOCK_SIZE_MAGNITUDE
Magnitude of one memory-manager block.
size_t srn_block_id_t
The block id is effectively just an index in the blocks array in srn_mm_t.
void srn_mm_release_block(srn_mm_t *mm, srn_block_id_t id)
Release the given block id and free the memory for later allocations.
void * srn_mm_allocate_in_block_aligned(srn_mm_t *mm, srn_block_id_t block_id, size_t size, size_t alignment)
Allocate memory on a block with the given block_id.
static void destroy_chain(srn_mm_t *mm, srn_block_id_t root_id)
static void * alloc_block_internal(srn_mm_t *mm)
Allocate a block worth of memory using the memory provider.
static void stdlib_releaser(void *ptr)
static srn_block_t * get_block(const srn_mm_t *mm, srn_block_id_t block_id)
An abstraction over ID->Block operation.
static void init_block(srn_mm_t *mm, srn_block_t *block)
static srn_memory_provider_t stdlib_provider
static void deallocated_block_id(srn_mm_t *mm, uint16_t bit)
void srn_lock_memory_manager(srn_mm_t *mm)
Locks the memory manager.
static int find_a_free_block_id(const srn_mm_t *mm)
srn_block_t * srn_mm_get_block(srn_mm_t *mm, srn_block_id_t block_id)
Return the block object associated by the given block_id.
void * srn_mm_reallocate(srn_mm_t *mm, void *ptr, size_t new_size)
void * srn_mm_immortal_allocate_aligned(srn_mm_t *mm, size_t size, size_t alignment)
Allocate memory on the importal block which will never gets freed.
void srn_mm_free(srn_mm_t *mm, void *ptr)
Release a pointer previously returned by srn_mm_malloc or srn_mm_reallocate.
srn_block_id_t srn_mm_allocate_block(srn_mm_t *mm)
Allocate a new block in the memory manager and return its ID.
size_t srn_mm_get_os_page_size(void)
Retutrns the OS page size.
static void allocated_block_id(srn_mm_t *mm, uint16_t bit)
void srn_mm_shutdown(srn_mm_t *mm)
Shut down the memory manager and release the resources.
static size_t block_remaining(const srn_block_t *block)
Return the number of unused payload bytes left in the block.
static size_t block_capacity(const srn_block_t *block)
Return the size of the payload section of the block.
srn_mm_t * srn_mm_init(const srn_configuration_t *config)
Initialize the memory manager, this function will panic on error.
void * srn_mm_malloc(srn_mm_t *mm, size_t size)
Generic allocations that do not participate in the block based pools.
static void * alloc_in_block(srn_mm_t *mm, srn_block_t *root_block, size_t size, size_t alignment)
This is the main allocation logic that allocates the space in the given block.
static void * stdlib_allocator(size_t size, size_t alignment)
Just a proxy functions to the standard stdlib malloc/free later on if we decided to use mimalloc or s...
void srn_unlock_memory_manager(srn_mm_t *mm)
Unocks the memory manager.
static bool is_block_id_free(const srn_mm_t *mm, uint16_t bit)
#define FALLBACK_PAGE_SIZE
#define MAX_NUMBER_OF_BLOCKS
array of blocks is enough for us, we can tweak the size as we see fit.
#define DEFAULT_BLOCK_ALIGNMENT
We strictly use 16 bytes alignment for blocks.
size_t offset
Offset from the base.
size_t size
This is the TOTAL size of the block, header + payloud.
struct srn_block_t * next
when the block does not have space to allocate a request, we will allocate a new block and point to i...
Every runtime knob, in one place.
This interface is here to abstract over the allocator.
void *(* allocate)(size_t size, size_t alignment)
size_t block_size_magnitude
Magnitude of one block the arena hands out from.
Main memory manager structure that will own all the allocated blocks and data.
srn_block_t * blocks[MAX_NUMBER_OF_BLOCKS]
srn_spinlock_t lock
This spinlock is here to protect the srn_mm_t when allocating/deallocating new blocks.
srn_block_t * immortal_block
Immortal block is a chain of blocks which will never die.
size_t block_count
Number of live chains.
srn_spinlock_t chain_locks[MAX_NUMBER_OF_BLOCKS]
One lock per chain, keyed by block id.
uint64_t block_bitmap[4]
This is a 256bit bitmap we treat it as a whole.
srn_memory_provider_t * provider
An abstraction over a memory provider like the malloc/free pair.
srn_spinlock_t immortal_lock
The immortal chain has no block id, so it gets its own chain lock.
#define PANIC_IF_NULL(ptr)
static void srn_spinlock_lock(srn_spinlock_t *lock)
#define PANIC_IF(cond, msg)
static void srn_spinlock_unlock(srn_spinlock_t *lock)
static void srn_spinlock_init(srn_spinlock_t *lock)