30#elif defined(__unix__) || defined(__APPLE__)
32#elif defined(__wasm__)
33#define WASM_PAGE_SIZE 65536U
37#define MM_LOG(FMT, ...) DBG("MM", FMT __VA_OPT__(, ) __VA_ARGS__)
39#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;
108 if (block_id < mm->block_count) {
109 return mm->
blocks[block_id];
118 PANIC_IF(size % alignment != 0,
"'size' should be a multiple of alignment");
121 return _aligned_malloc(size, alignment);
124 return aligned_alloc(alignment, size);
148 return realloc(ptr, new_size);
158 return (x + half) / m * m;
168 block->
next =
nullptr;
195 while (block !=
nullptr) {
215 TODO(
"We need to allocate a larger block");
217 MM_LOG(
"Allocating %ld with %ld alignment", size, alignment);
219 MM_LOG(
"Root block: %p", (
void *)root_block);
221 MM_LOG(
"Walking the block chain");
226 MM_LOG(
"Next block: %p", (
void *)target_block);
227 MM_LOG(
"Base: %p | Offset: 0x%lx | Space: %ld | Total size: %ld", (
void *)&target_block->base,
228 target_block->offset, block_remaining(target_block), target_block->size);
230 size_t base_offset = target_block->offset;
231 size_t aligned_offset = (base_offset + (alignment - 1)) & ~(alignment - 1);
234 MM_LOG(
"Calculated aligned offset 0x%lx", aligned_offset);
236 if (aligned_offset + size > capacity) {
237 MM_LOG(
"We can't allocate in this block");
238 if (target_block->next !=
nullptr) {
241 target_block = target_block->next;
245 MM_LOG(
"We have to allocate a new block");
249 target_block->next = b;
252 MM_LOG(
"New block: %p", (
void *)target_block);
255 MM_LOG(
"We have found enough space");
257 void *ptr = target_block->base + aligned_offset;
258 target_block->offset = aligned_offset + size;
259 MM_LOG(
"Allocated 0x%lx bytes from %p to 0x%lx", target_block->offset, ptr,
260 (uintptr_t)ptr + (uintptr_t)target_block->offset);
274 return (
size_t)si.dwPageSize;
275#elif defined(__unix__) || defined(__APPLE__)
276 long sz = sysconf(_SC_PAGESIZE);
278#elif defined(__wasm__)
279 return WASM_PAGE_SIZE;
306 "Wrong block size. Modify DESIRED_BLOCK_SIZE to a larger number");
317 mm->stats.allocated_pages = 0;
318 mm->stats.total_allocations = 0;
319 mm->stats.total_os_allocations = 0;
320 mm->stats.total_blocks = 0;
330 if (mm->
blocks[i] !=
nullptr) {
337 while (block !=
nullptr) {
349 MM_LOG(
"Allocating %ld bytes with 16 bytes alignment in block: %zu", size, block_id);
370 PANIC_IF(index == -1,
"Couldn't allocate a block id. This is a bug!");
375 "Miscalculated the block id. It is not free. This is a bug!");
382 mm->stats.total_blocks++;
383 mm->stats.total_os_allocations++;
398 printf(
"Block: %zu@%lx" PRIXPTR
"\n",
id, (uintptr_t)block);
399 printf(
"======================================================\n");
401 if (block->
next ==
nullptr) {
404 printf(
"%lx" PRIXPTR
"\n", (uintptr_t)block->
next);
408void srn_mm_print_blocks_summary(
srn_mm_t *mm) {
410 srn_mm_print_block_summary(mm, d);
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_allocate 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.
void * srn_mm_allocate(srn_mm_t *mm, size_t size)
Generic allocations that do not participate in the block based pools.
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)
srn_mm_t * srn_mm_init()
Initialize the memory manager, this function will panic on error.
void srn_mm_shutdown(srn_mm_t *mm)
Shut down the memory manager and release the resources.
static size_t block_capacity(const srn_block_t *block)
Return the size of the payload section of the block.
size_t srn_mm_get_block_size(void)
Calculates and return the block size based on our desired size and the OS page size.
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...
static size_t closest_multiple(size_t x, size_t m)
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 DESIRED_BLOCK_SIZE
This is the value that we want for our blocks but we want it to be a multiple of the page size on the...
#define FALLBACK_PAGE_SIZE
#define MAX_NUMBER_OF_BLOCKS
TODO(lxsameer): Since we want to move fast, at this stage a static array of blocks is enough for us,...
#define DEFAULT_BLOCK_ALIGNMENT
We strictly use 16 bytes alignment for blocks.
size_t offset
Offset from the base.
srn_spinlock_t lock
This lock will protect only the block level operations and NOT the memory manager.
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...
This interface is here to abstract over the allocator.
void *(* allocate)(size_t size, size_t alignment)
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.
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.
#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)