Serene Runtime 1.0.0
C runtime for the Serene programming language
Loading...
Searching...
No Matches
default.c File Reference
#include <stdlib.h>
#include "serene/rt/mm/interface.h"
#include "serene/utils.h"
#include <assert.h>
#include <inttypes.h>
#include <string.h>
Include dependency graph for default.c:

Go to the source code of this file.

Macros

#define MM_LOG(FMT, ...)
 

Functions

static bool is_block_id_free (const srn_mm_t *mm, uint16_t bit)
 
static void allocated_block_id (srn_mm_t *mm, uint16_t bit)
 
static void deallocated_block_id (srn_mm_t *mm, uint16_t bit)
 
static int find_a_free_block_id (const srn_mm_t *mm)
 
static srn_block_tget_block (const srn_mm_t *mm, srn_block_id_t block_id)
 An abstraction over ID->Block operation.
 
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 something or even our own malloc, we can plug them in like this.
 
static void stdlib_releaser (void *ptr)
 
void * srn_mm_allocate (srn_mm_t *mm, size_t size)
 Generic allocations that do not participate in the block based pools.
 
void * srn_mm_reallocate (srn_mm_t *mm, void *ptr, size_t new_size)
 
void srn_mm_free (srn_mm_t *mm, void *ptr)
 Release a pointer previously returned by srn_mm_allocate or srn_mm_reallocate.
 
static size_t closest_multiple (size_t x, size_t m)
 
static size_t block_capacity (const srn_block_t *block)
 Return the size of the payload section of the block.
 
static void init_block (srn_mm_t *mm, srn_block_t *block)
 
static void * alloc_block_internal (srn_mm_t *mm)
 Allocate a block worth of memory using the memory provider.
 
static void destroy_chain (srn_mm_t *mm, srn_block_id_t root_id)
 
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.
 
size_t srn_mm_get_os_page_size (void)
 Retutrns the OS page size.
 
size_t srn_mm_get_block_size (void)
 Calculates and return the block size based on our desired size and the OS page size.
 
srn_block_tsrn_mm_get_block (srn_mm_t *mm, srn_block_id_t block_id)
 Return the block object associated by the given block_id
 
srn_mm_tsrn_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.
 
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.
 
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_unlock_memory_manager (srn_mm_t *mm)
 Unocks the memory manager.
 
void srn_lock_memory_manager (srn_mm_t *mm)
 Locks the memory manager.
 
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_release_block (srn_mm_t *mm, srn_block_id_t id)
 Release the given block id and free the memory for later allocations.
 

Variables

static srn_memory_provider_t stdlib_provider
 

Macro Definition Documentation

◆ MM_LOG

#define MM_LOG ( FMT,
... )

Definition at line 39 of file default.c.

Function Documentation

◆ alloc_block_internal()

static void * alloc_block_internal ( srn_mm_t * mm)
inlinestatic

Allocate a block worth of memory using the memory provider.

This is an internal function and it will NOT allocate block_id nor track the block via the memory manager by default.

Definition at line 178 of file default.c.

178 {
179 PANIC_IF_NULL(mm);
180 srn_block_t *block =
182 PANIC_IF_NULL(block);
183 init_block(mm, block);
184 return block;
185}
static void init_block(srn_mm_t *mm, srn_block_t *block)
Definition default.c:166
#define DEFAULT_BLOCK_ALIGNMENT
We strictly use 16 bytes alignment for blocks.
Definition interface.h:47
void *(* allocate)(size_t size, size_t alignment)
Definition interface.h:70
size_t block_size
Definition interface.h:127
srn_memory_provider_t * provider
An abstraction over a memory provider like the malloc/free pair.
Definition interface.h:122
#define PANIC_IF_NULL(ptr)
Definition utils.h:64
Here is the call graph for this function:
Here is the caller graph for this function:

◆ alloc_in_block()

static void * alloc_in_block ( srn_mm_t * mm,
srn_block_t * root_block,
size_t size,
size_t alignment )
inlinestatic

This is the main allocation logic that allocates the space in the given block.

Other public functions have to use this fuction to operate.

Definition at line 207 of file default.c.

208 {
209
210 if (size > block_capacity(root_block)) {
211 // Since all the blocks in a block chain have the same size,
212 // if user is asking for an allocation larger than the block size,
213 // there is no way we can find it in this chain, so a larger block
214 // size is required
215 TODO("We need to allocate a larger block");
216 }
217 MM_LOG("Allocating %ld with %ld alignment", size, alignment);
218
219 MM_LOG("Root block: %p", (void *)root_block);
220 srn_block_t *target_block = root_block;
221 MM_LOG("Walking the block chain");
222 for (;;) {
223 srn_spinlock_t *lock = &target_block->lock;
224 srn_spinlock_lock(lock);
225
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);
229
230 size_t base_offset = target_block->offset;
231 size_t aligned_offset = (base_offset + (alignment - 1)) & ~(alignment - 1);
232 size_t capacity = block_capacity(target_block);
233
234 MM_LOG("Calculated aligned offset 0x%lx", aligned_offset);
235
236 if (aligned_offset + size > capacity) {
237 MM_LOG("We can't allocate in this block");
238 if (target_block->next != nullptr) {
240 // let's try the next block in the chain
241 target_block = target_block->next;
242 continue;
243 }
244 // We need to create a new block for this chain.
245 MM_LOG("We have to allocate a new block");
246
248 init_block(mm, b);
249 target_block->next = b;
250 target_block = b;
252 MM_LOG("New block: %p", (void *)target_block);
253 continue;
254 }
255 MM_LOG("We have found enough space");
256
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);
261
263 return ptr;
264 }
265}
static void * alloc_block_internal(srn_mm_t *mm)
Allocate a block worth of memory using the memory provider.
Definition default.c:178
#define MM_LOG(FMT,...)
Definition default.c:39
static size_t block_capacity(const srn_block_t *block)
Return the size of the payload section of the block.
Definition default.c:162
uint8_t base[]
Where the data area starts.
Definition interface.h:93
size_t offset
Offset from the base.
Definition interface.h:91
srn_spinlock_t lock
This lock will protect only the block level operations and NOT the memory manager.
Definition interface.h:79
size_t size
This is the TOTAL size of the block, header + payloud.
Definition interface.h:88
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...
Definition interface.h:83
static void srn_spinlock_lock(srn_spinlock_t *lock)
Definition utils.h:286
static void srn_spinlock_unlock(srn_spinlock_t *lock)
Definition utils.h:277
#define TODO(msg)
Definition utils.h:52
Here is the call graph for this function:
Here is the caller graph for this function:

◆ allocated_block_id()

static void allocated_block_id ( srn_mm_t * mm,
uint16_t bit )
inlinestatic

Definition at line 56 of file default.c.

56 {
57 PANIC_IF(bit >= 256, "Bit position is out of block_bitmap boundaries.");
58
59 // divide by 64, which 64-bit word
60 uint64_t index = bit >> 6U;
61 // remainder, which bit in that word
62 unsigned offset = bit & 63U;
63
64 mm->block_bitmap[index] |= (1ULL << offset);
65}
uint64_t block_bitmap[4]
This is a 256bit bitmap we treat it as a whole.
Definition interface.h:126
#define PANIC_IF(cond, msg)
Definition utils.h:57
Here is the caller graph for this function:

◆ block_capacity()

static size_t block_capacity ( const srn_block_t * block)
inlinestatic

Return the size of the payload section of the block.

Definition at line 162 of file default.c.

162 {
163 return block->size - offsetof(srn_block_t, base);
164}
Here is the caller graph for this function:

◆ closest_multiple()

static size_t closest_multiple ( size_t x,
size_t m )
inlinestatic

Definition at line 156 of file default.c.

156 {
157 size_t half = m / 2;
158 return (x + half) / m * m;
159}
Here is the caller graph for this function:

◆ deallocated_block_id()

static void deallocated_block_id ( srn_mm_t * mm,
uint16_t bit )
inlinestatic

Definition at line 67 of file default.c.

67 {
68 PANIC_IF(bit >= 256, "Bit position is out of block_bitmap boundaries.");
69
70 uint64_t index = bit >> 6U;
71 unsigned offset = bit & 63U;
72 uint64_t mask = ~(1ULL << offset);
73 mm->block_bitmap[index] &= mask;
74}
Here is the caller graph for this function:

◆ destroy_chain()

static void destroy_chain ( srn_mm_t * mm,
srn_block_id_t root_id )
inlinestatic

Definition at line 187 of file default.c.

187 {
188
189 if (is_block_id_free(mm, root_id)) {
190 return;
191 }
192 srn_block_t *block = get_block(mm, root_id);
193
195 while (block != nullptr) {
196 srn_block_t *tmp = block;
197 block = tmp->next;
198
199 mm->provider->release(tmp);
200 }
201 deallocated_block_id(mm, root_id);
203}
static srn_block_t * get_block(const srn_mm_t *mm, srn_block_id_t block_id)
An abstraction over ID->Block operation.
Definition default.c:107
static void deallocated_block_id(srn_mm_t *mm, uint16_t bit)
Definition default.c:67
void srn_lock_memory_manager(srn_mm_t *mm)
Locks the memory manager.
Definition default.c:362
void srn_unlock_memory_manager(srn_mm_t *mm)
Unocks the memory manager.
Definition default.c:360
static bool is_block_id_free(const srn_mm_t *mm, uint16_t bit)
Definition default.c:45
void(* release)(void *p)
Definition interface.h:71
Here is the call graph for this function:
Here is the caller graph for this function:

◆ find_a_free_block_id()

static int find_a_free_block_id ( const srn_mm_t * mm)
inlinestatic

Definition at line 76 of file default.c.

76 {
77#if defined(__GNUC__) || defined(__clang__)
78 for (int i = 0; i < 4; i++) {
79 uint64_t word = mm->block_bitmap[i];
80
81 if (~word) {
82 // invert, then find first 1 bit
83 int bit = __builtin_ctzll(~word);
84 return (i * 64) + bit;
85 }
86 }
87 return -1;
88#else
89 // Fallback
90 for (int i = 0; i < 4; i++) {
91 uint64_t word = mm->block_bitmap[i];
92 if (word != ~0ULL) {
93 uint64_t mask = ~word;
94 int bit = 0;
95 while ((mask & 1ULL) == 0) {
96 mask >>= 1;
97 bit++;
98 }
99 return (i * 64) + bit;
100 }
101 }
102 return -1;
103#endif
104}
Here is the caller graph for this function:

◆ get_block()

static srn_block_t * get_block ( const srn_mm_t * mm,
srn_block_id_t block_id )
inlinestatic

An abstraction over ID->Block operation.

Definition at line 107 of file default.c.

107 {
108 if (block_id < mm->block_count) {
109 return mm->blocks[block_id];
110 }
111 return nullptr;
112}
srn_block_t * blocks[MAX_NUMBER_OF_BLOCKS]
Definition interface.h:129
Here is the caller graph for this function:

◆ init_block()

static void init_block ( srn_mm_t * mm,
srn_block_t * block )
inlinestatic

Definition at line 166 of file default.c.

166 {
167 PANIC_IF_NULL(mm);
168 block->next = nullptr;
169 block->size = mm->block_size;
170 block->offset = 0;
171 srn_spinlock_init(&block->lock);
172 srn_spinlock_unlock(&block->lock);
173}
static void srn_spinlock_init(srn_spinlock_t *lock)
Definition utils.h:281
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_block_id_free()

static bool is_block_id_free ( const srn_mm_t * mm,
uint16_t bit )
inlinestatic

Definition at line 45 of file default.c.

45 {
46 PANIC_IF(bit >= 256, "Bit position is out of block_bitmap boundaries.");
47
48 // divide by 64, which 64-bit word
49 uint64_t index = bit >> 6U;
50 // remainder, which bit in that word
51 unsigned offset = bit & 63U;
52
53 return ((mm->block_bitmap[index] >> offset) & 1ULL) == 0;
54}
Here is the caller graph for this function:

◆ srn_lock_memory_manager()

void srn_lock_memory_manager ( srn_mm_t * mm)

Locks the memory manager.

We have to lock the memory manager when allocating blocks. TODO(lxsameer): Do we need to support thread local blocks?

Definition at line 362 of file default.c.

362{ srn_spinlock_lock(&mm->lock); }
srn_spinlock_t lock
This spinlock is here to protect the srn_mm_t when allocating/deallocating new blocks.
Definition interface.h:119
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_allocate()

void * srn_mm_allocate ( srn_mm_t * mm,
size_t size )
nodiscard

Generic allocations that do not participate in the block based pools.

Equivalent to malloc/realloc/free. Routed through the memory manager so the backend can later be swapped without touching callers. mm is reserved for future per manager routing and is currently unused inside the implementation.

Definition at line 141 of file default.c.

141 {
142 UNUSED(mm);
143 return malloc(size);
144}
#define UNUSED(x)
Definition utils.h:43
Here is the caller graph for this function:

◆ srn_mm_allocate_block()

srn_block_id_t srn_mm_allocate_block ( srn_mm_t * mm)
nodiscard

Allocate a new block in the memory manager and return its ID.

The client code can use the ID to allocate memory on the block and when it's done, just use the same ID to release the block

Definition at line 364 of file default.c.

364 {
365 PANIC_IF(mm->block_count >= MAX_NUMBER_OF_BLOCKS - 1, "Out of memory");
366
369 int index = find_a_free_block_id(mm);
370 PANIC_IF(index == -1, "Couldn't allocate a block id. This is a bug!");
371
372 init_block(mm, block);
373 mm->block_count++;
374 PANIC_IF(!is_block_id_free(mm, (uint16_t)index),
375 "Miscalculated the block id. It is not free. This is a bug!");
376 allocated_block_id(mm, (uint16_t)index);
377 mm->blocks[(srn_block_id_t)index] = block;
378
380
381#if SERENE_DEBUG
382 mm->stats.total_blocks++;
383 mm->stats.total_os_allocations++;
384#endif
385 return (srn_block_id_t)index;
386}
size_t srn_block_id_t
The block id is effectively just an index in the blocks array in srn_mm_t.
Definition context.h:38
static int find_a_free_block_id(const srn_mm_t *mm)
Definition default.c:76
static void allocated_block_id(srn_mm_t *mm, uint16_t bit)
Definition default.c:56
#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,...
Definition interface.h:44
size_t block_count
Definition interface.h:128
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_allocate_in_block_aligned()

void * srn_mm_allocate_in_block_aligned ( srn_mm_t * mm,
srn_block_id_t block_id,
size_t size,
size_t alignment )
nodiscard

Allocate memory on a block with the given block_id.

Definition at line 347 of file default.c.

348 {
349 MM_LOG("Allocating %ld bytes with 16 bytes alignment in block: %zu", size, block_id);
350 srn_block_t *block = get_block(mm, block_id);
351 PANIC_IF_NULL(block);
352
353 return alloc_in_block(mm, block, size, alignment);
354}
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.
Definition default.c:207
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_free()

void srn_mm_free ( srn_mm_t * mm,
void * ptr )

Release a pointer previously returned by srn_mm_allocate or srn_mm_reallocate.

ptr may be nullptr, in which case the call is a no-op.

Definition at line 151 of file default.c.

151 {
152 UNUSED(mm);
153 free(ptr);
154}
Here is the caller graph for this function:

◆ srn_mm_get_block()

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

Definition at line 290 of file default.c.

290 {
291 return get_block(mm, block_id);
292}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_get_block_size()

size_t srn_mm_get_block_size ( void )

Calculates and return the block size based on our desired size and the OS page size.

Basically the ruturn value will be a number multiple of the OS page size and close or equal to our desired size

Definition at line 285 of file default.c.

285 {
286 const size_t ps = srn_mm_get_os_page_size();
288}
size_t srn_mm_get_os_page_size(void)
Retutrns the OS page size.
Definition default.c:270
static size_t closest_multiple(size_t x, size_t m)
Definition default.c:156
#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...
Definition interface.h:53
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_get_os_page_size()

size_t srn_mm_get_os_page_size ( void )

Retutrns the OS page size.

Definition at line 270 of file default.c.

270 {
271#if defined(_WIN32)
272 SYSTEM_INFO si;
273 GetSystemInfo(&si);
274 return (size_t)si.dwPageSize;
275#elif defined(__unix__) || defined(__APPLE__)
276 long sz = sysconf(_SC_PAGESIZE);
277 return (sz > 0) ? (size_t)sz : FALLBACK_PAGE_SIZE;
278#elif defined(__wasm__)
279 return WASM_PAGE_SIZE;
280#else
281 return FALLBACK_PAGE_SIZE;
282#endif
283}
#define FALLBACK_PAGE_SIZE
Definition interface.h:36
Here is the caller graph for this function:

◆ srn_mm_immortal_allocate_aligned()

void * srn_mm_immortal_allocate_aligned ( srn_mm_t * mm,
size_t size,
size_t alignment )
nodiscard

Allocate memory on the importal block which will never gets freed.

Definition at line 356 of file default.c.

356 {
357 return alloc_in_block(mm, mm->immortal_block, size, alignment);
358}
srn_block_t * immortal_block
Immortal block is a chain of blocks which will never die.
Definition interface.h:134
Here is the call graph for this function:

◆ srn_mm_init()

srn_mm_t * srn_mm_init ( void )

Initialize the memory manager, this function will panic on error.

Definition at line 294 of file default.c.

294 {
295 srn_mm_t *mm = malloc(sizeof(srn_mm_t));
296 PANIC_IF_NULL(mm);
297
299 memset(mm->block_bitmap, 0, sizeof(mm->block_bitmap));
300
301 mm->block_count = 0;
303 memset((void *)mm->blocks, 0, sizeof(mm->blocks));
304
305 PANIC_IF(mm->block_size <= sizeof(srn_block_t),
306 "Wrong block size. Modify DESIRED_BLOCK_SIZE to a larger number");
307
309
310 srn_block_t *immortal =
312 PANIC_IF_NULL(immortal);
313 init_block(mm, immortal);
314 mm->immortal_block = immortal;
315
316#if SERENE_DEBUG
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;
321#endif
322 return mm;
323}
static srn_memory_provider_t stdlib_provider
Definition default.c:130
size_t srn_mm_get_block_size(void)
Calculates and return the block size based on our desired size and the OS page size.
Definition default.c:285
Main memory manager structure that will own all the allocated blocks and data.
Definition interface.h:112
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_reallocate()

void * srn_mm_reallocate ( srn_mm_t * mm,
void * ptr,
size_t new_size )
nodiscard

Definition at line 146 of file default.c.

146 {
147 UNUSED(mm);
148 return realloc(ptr, new_size);
149}

◆ srn_mm_release_block()

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.

Definition at line 388 of file default.c.

388 {
389 PANIC_IF_NULL(mm);
390 destroy_chain(mm, id);
391}
static void destroy_chain(srn_mm_t *mm, srn_block_id_t root_id)
Definition default.c:187
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_shutdown()

void srn_mm_shutdown ( srn_mm_t * mm)

Shut down the memory manager and release the resources.

Will panic on error. Technically it should be the final piece of clean up that we call. Note: Shutdown is not thread safe at has to execute on the main thread.

Definition at line 325 of file default.c.

325 {
326 PANIC_IF_NULL(mm);
327 assert(mm->provider != nullptr);
328
329 for (size_t i = 0; i < mm->block_count; i++) {
330 if (mm->blocks[i] != nullptr) {
331 destroy_chain(mm, i);
332 }
333 }
334
335 srn_block_t *block = mm->immortal_block;
336
337 while (block != nullptr) {
338 srn_block_t *tmp = block;
339 block = tmp->next;
340
341 mm->provider->release(tmp);
342 }
343
344 mm->provider->release(mm);
345}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_unlock_memory_manager()

void srn_unlock_memory_manager ( srn_mm_t * mm)

Unocks the memory manager.

Definition at line 360 of file default.c.

360{ srn_spinlock_unlock(&mm->lock); }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ stdlib_allocator()

static void * stdlib_allocator ( size_t size,
size_t alignment )
static

Just a proxy functions to the standard stdlib malloc/free later on if we decided to use mimalloc or something or even our own malloc, we can plug them in like this.

Definition at line 117 of file default.c.

117 {
118 PANIC_IF(size % alignment != 0, "'size' should be a multiple of alignment");
119
120#if defined(_WIN32)
121 return _aligned_malloc(size, alignment);
122#else
123 // C11 standard. It stills bothers me why microsoft is not doing it.
124 return aligned_alloc(alignment, size);
125#endif
126}

◆ stdlib_releaser()

static void stdlib_releaser ( void * ptr)
static

Definition at line 128 of file default.c.

128{ free(ptr); }

Variable Documentation

◆ stdlib_provider

srn_memory_provider_t stdlib_provider
static
Initial value:
= {
.allocate = &stdlib_allocator,
.release = &stdlib_releaser,
}
static void stdlib_releaser(void *ptr)
Definition default.c:128
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...
Definition default.c:117

Definition at line 130 of file default.c.

130 {
131 .allocate = &stdlib_allocator,
132 .release = &stdlib_releaser,
133};