Serene Runtime 1.0.0
C runtime for the Serene programming language
Loading...
Searching...
No Matches
interface.h File Reference

Notes: More...

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "serene/utils.h"
Include dependency graph for interface.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  srn_memory_provider_t
 This interface is here to abstract over the allocator. More...
 
struct  srn_block_t
 
struct  srn_mm_t
 Main memory manager structure that will own all the allocated blocks and data. More...
 

Macros

#define FALLBACK_PAGE_SIZE   4096U
 
#define MAX_NUMBER_OF_BLOCKS   256U
 TODO(lxsameer): Since we want to move fast, at this stage a static array of blocks is enough for us, we can tweak the size as we see fit.
 
#define DEFAULT_BLOCK_ALIGNMENT   16U
 We strictly use 16 bytes alignment for blocks.
 
#define DESIRED_BLOCK_SIZE   131072
 This is the value that we want for our blocks but we want it to be a multiple of the page size on the platform as well.
 
#define SRN_BLOCK_NO_ID   SIZE_MAX
 
#define srn_mm_allocate_in_block(mm, id, T)
 
#define srn_mm_immortal_allocate(mm, T)
 

Typedefs

typedef struct srn_memory_provider_t srn_memory_provider_t
 This interface is here to abstract over the allocator.
 
typedef struct srn_block_t srn_block_t
 
typedef struct srn_mm_t srn_mm_t
 Main memory manager structure that will own all the allocated blocks and data.
 

Functions

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_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.
 
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
 
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.
 
srn_mm_tsrn_mm_init (void)
 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_unlock_memory_manager (srn_mm_t *mm)
 Unocks the memory manager.
 
void srn_lock_memory_manager (srn_mm_t *mm)
 Locks the memory manager.
 
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.
 

Detailed Description

Notes:

  • Never give out any pointer to intermediate blocks in a block chain to user.
  • Always lock the memory manager when operating only on srn_mm_t.
  • Always lock the block when operating on it
  • Never lock the memory manager to operate on a block in it.
  • To the user a chain of blocks are just one block, so deallocation happens on the chain level not per block
  • It's users responsibility to copy the data between different chains.

Definition in file interface.h.

Macro Definition Documentation

◆ DEFAULT_BLOCK_ALIGNMENT

#define DEFAULT_BLOCK_ALIGNMENT   16U

We strictly use 16 bytes alignment for blocks.

Definition at line 47 of file interface.h.

◆ DESIRED_BLOCK_SIZE

#define DESIRED_BLOCK_SIZE   131072

This is the value that we want for our blocks but we want it to be a multiple of the page size on the platform as well.

So there is no guarantee that we get exactly this number. 128Kib

Definition at line 53 of file interface.h.

◆ FALLBACK_PAGE_SIZE

#define FALLBACK_PAGE_SIZE   4096U

Definition at line 36 of file interface.h.

◆ MAX_NUMBER_OF_BLOCKS

#define MAX_NUMBER_OF_BLOCKS   256U

TODO(lxsameer): Since we want to move fast, at this stage a static array of blocks is enough for us, we can tweak the size as we see fit.

But we need to change this for the final stage. We should be able to dynamically expand the array of blocks. Notes: Due to my laziness, if you ever change this value, you need to change the popcount functions for the block bitmap as well.

Definition at line 44 of file interface.h.

◆ SRN_BLOCK_NO_ID

#define SRN_BLOCK_NO_ID   SIZE_MAX

Definition at line 63 of file interface.h.

◆ srn_mm_allocate_in_block

#define srn_mm_allocate_in_block ( mm,
id,
T )
Value:
(T *)srn_mm_allocate_in_block_aligned(mm, id, sizeof(T), alignof(T))
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.
Definition default.c:347

Definition at line 166 of file interface.h.

166#define srn_mm_allocate_in_block(mm, id, T) \
167 (T *)srn_mm_allocate_in_block_aligned(mm, id, sizeof(T), alignof(T))

◆ srn_mm_immortal_allocate

#define srn_mm_immortal_allocate ( mm,
T )
Value:
(T *)srn_mm_immortal_allocate_aligned(mm, sizeof(T), alignof(T))
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.
Definition default.c:356

Definition at line 169 of file interface.h.

169#define srn_mm_immortal_allocate(mm, T) \
170 (T *)srn_mm_immortal_allocate_aligned(mm, sizeof(T), alignof(T))

Typedef Documentation

◆ srn_block_t

typedef struct srn_block_t srn_block_t

◆ srn_memory_provider_t

typedef struct srn_memory_provider_t srn_memory_provider_t

This interface is here to abstract over the allocator.

For instance, malloc/free can be a page provider. This will let us switch to other implementation later on. Eventually we might end up coming up with our own version of malloc/free.

◆ srn_mm_t

typedef struct srn_mm_t srn_mm_t

Main memory manager structure that will own all the allocated blocks and data.

In every instance of the compiler there should be only one instance of this. It should be created via srn_mm_init and destroyed via srn_shutdown_memory_manager.

Function Documentation

◆ 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
static void srn_spinlock_lock(srn_spinlock_t *lock)
Definition utils.h:286
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 void * alloc_block_internal(srn_mm_t *mm)
Allocate a block worth of memory using the memory provider.
Definition default.c:178
static void init_block(srn_mm_t *mm, srn_block_t *block)
Definition default.c:166
void srn_lock_memory_manager(srn_mm_t *mm)
Locks the memory manager.
Definition default.c:362
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
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
#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
srn_block_t * blocks[MAX_NUMBER_OF_BLOCKS]
Definition interface.h:129
size_t block_count
Definition interface.h:128
#define PANIC_IF(cond, msg)
Definition utils.h:57
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}
#define MM_LOG(FMT,...)
Definition default.c:39
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 * 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
#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:

◆ 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
#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
Main memory manager structure that will own all the allocated blocks and data.
Definition interface.h:112
size_t block_size
Definition interface.h:127
uint64_t block_bitmap[4]
This is a 256bit bitmap we treat it as a whole.
Definition interface.h:126
srn_memory_provider_t * provider
An abstraction over a memory provider like the malloc/free pair.
Definition interface.h:122
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:

◆ 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}
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
void(* release)(void *p)
Definition interface.h:71
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); }
static void srn_spinlock_unlock(srn_spinlock_t *lock)
Definition utils.h:277
Here is the call graph for this function:
Here is the caller graph for this function: