Serene Runtime 1.0.0-dev
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/rt/configuration.h"
#include "serene/rt/trace.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 MM_TRACEPOINT(...)
#define FALLBACK_PAGE_SIZE   4096U
#define MAX_NUMBER_OF_BLOCKS   256U
 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 SRN_BLOCK_NO_ID   SIZE_MAX
#define srn_mm_allocate_in_block(mm, id, T)
#define srn_mm_immortal_allocate(mm, T)
#define MALLOC(ctx, T)
#define FREE(ctx, ptr)

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.
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_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_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_t * srn_mm_init (const srn_configuration_t *config)
 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_malloc (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_malloc 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.
  • Chain locks live in the manager, keyed by block id. An allocation holds the chain's lock for the whole walk; a release holds the manager lock and then the chain lock before freeing, so an allocation racing a release either completes first or resolves the id to nothing.
  • 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 58 of file interface.h.

◆ FALLBACK_PAGE_SIZE

#define FALLBACK_PAGE_SIZE   4096U

Definition at line 45 of file interface.h.

◆ FREE

#define FREE ( ctx,
ptr )
Value:
srn_mm_free((ctx)->engine->mm, ptr)
void srn_mm_free(srn_mm_t *mm, void *ptr)
Release a pointer previously returned by srn_mm_malloc or srn_mm_reallocate.
Definition default.c:168

Definition at line 247 of file interface.h.

◆ MALLOC

#define MALLOC ( ctx,
T )
Value:
(T *)srn_mm_malloc((ctx)->engine->mm, sizeof(T))
void * srn_mm_malloc(srn_mm_t *mm, size_t size)
Generic allocations that do not participate in the block based pools.
Definition default.c:155

Definition at line 235 of file interface.h.

◆ MAX_NUMBER_OF_BLOCKS

#define MAX_NUMBER_OF_BLOCKS   256U

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 55 of file interface.h.

◆ MM_TRACEPOINT

#define MM_TRACEPOINT ( ...)
Value:
SRN_TRACEPOINT_WITH_GROUP(mm __VA_OPT__(, ) __VA_ARGS__)
#define SRN_TRACEPOINT_WITH_GROUP(group, name,...)
Definition trace.h:80

Definition at line 43 of file interface.h.

◆ SRN_BLOCK_NO_ID

#define SRN_BLOCK_NO_ID   SIZE_MAX

Definition at line 68 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:411

Definition at line 185 of file interface.h.

185#define srn_mm_allocate_in_block(mm, id, T) \
186 (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:431

Definition at line 188 of file interface.h.

188#define srn_mm_immortal_allocate(mm, T) \
189 (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 441 of file default.c.

441{ 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:285
Here is the call graph for this function:
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 443 of file default.c.

443 {
445 // Ids are reused after release, so exhaustion means every id is live
446 // right now, not that this many allocations ever happened.
447 int index = find_a_free_block_id(mm);
448 PANIC_IF(index == -1, "Out of memory: all block ids are in use");
450
451 mm->block_count++;
452 PANIC_IF(
453 !is_block_id_free(mm, (uint16_t)index),
454 "Miscalculated the block id. It is not free. This is a bug!"
455 );
456 allocated_block_id(mm, (uint16_t)index);
457 mm->blocks[(srn_block_id_t)index] = block;
458
460
461#if SERENE_DEBUG
462 mm->stats.total_blocks++;
463 mm->stats.total_os_allocations++;
464#endif
465 return (srn_block_id_t)index;
466}
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:201
void srn_lock_memory_manager(srn_mm_t *mm)
Locks the memory manager.
Definition default.c:441
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:439
static bool is_block_id_free(const srn_mm_t *mm, uint16_t bit)
Definition default.c:45
srn_block_t * blocks[MAX_NUMBER_OF_BLOCKS]
Definition interface.h:131
size_t block_count
Number of live chains.
Definition interface.h:130
#define PANIC_IF(cond, msg)
Definition utils.h:59
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 411 of file default.c.

413 {
414 MM_LOG("Allocating %zu bytes with %zu bytes alignment in block: %zu", size, alignment, block_id);
415 PANIC_IF(block_id >= MAX_NUMBER_OF_BLOCKS, "Block id out of range");
416
417 // The id resolves to a block only under the chain lock, so a release
418 // cannot free the chain out from under this walk.
419 srn_spinlock_t *chain_lock = &mm->chain_locks[block_id];
420 srn_spinlock_lock(chain_lock);
421
422 srn_block_t *block = get_block(mm, block_id);
423 PANIC_IF_NULL(block);
424
425 void *ptr = alloc_in_block(mm, block, size, alignment);
426 srn_spinlock_unlock(chain_lock);
427 MM_TRACEPOINT(mm_alloc, (uint64_t)block_id, (uint64_t)size, ptr);
428 return ptr;
429}
#define MM_LOG(FMT,...)
Definition default.c:38
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:109
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:251
#define MM_TRACEPOINT(...)
Definition interface.h:43
#define MAX_NUMBER_OF_BLOCKS
array of blocks is enough for us, we can tweak the size as we see fit.
Definition interface.h:55
srn_spinlock_t chain_locks[MAX_NUMBER_OF_BLOCKS]
One lock per chain, keyed by block id.
Definition interface.h:136
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
static void srn_spinlock_unlock(srn_spinlock_t *lock)
Definition utils.h:276
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_malloc or srn_mm_reallocate.

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

Definition at line 168 of file default.c.

168 {
169 UNUSED(mm);
170 MM_TRACEPOINT(mm_free, ptr);
171 free(ptr);
172}
#define UNUSED(x)
Definition utils.h:45
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 333 of file default.c.

333 {
334 return get_block(mm, block_id);
335}
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 318 of file default.c.

318 {
319#if defined(_WIN32)
320 SYSTEM_INFO si;
321 GetSystemInfo(&si);
322 return (size_t)si.dwPageSize;
323#elif defined(__unix__) || defined(__APPLE__)
324 long sz = sysconf(_SC_PAGESIZE);
325 return (sz > 0) ? (size_t)sz : FALLBACK_PAGE_SIZE;
326#elif defined(__wasm__)
327 return WASM_PAGE_SIZE;
328#else
329 return FALLBACK_PAGE_SIZE;
330#endif
331}
#define FALLBACK_PAGE_SIZE
Definition interface.h:45
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 431 of file default.c.

431 {
433 void *ptr = alloc_in_block(mm, mm->immortal_block, size, alignment);
435 MM_TRACEPOINT(mm_immortal_alloc, (uint64_t)size, ptr);
436 return ptr;
437}
srn_block_t * immortal_block
Immortal block is a chain of blocks which will never die.
Definition interface.h:144
srn_spinlock_t immortal_lock
The immortal chain has no block id, so it gets its own chain lock.
Definition interface.h:139
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_init()

srn_mm_t * srn_mm_init ( const srn_configuration_t * config)

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

config provides the knobs the manager reads at init (mm.block_size_magnitude, the block size is 1 << magnitude); a null config means "use the defaults". The config is read only during the call, so the caller may pass a stack value and reuse it for srn_engine_make.

Definition at line 337 of file default.c.

337 {
338 if (config != nullptr) {
339 srn_config_validate(config);
340 }
341
342 srn_mm_t *mm = malloc(sizeof(srn_mm_t));
343 PANIC_IF_NULL(mm);
344
346 memset(mm->block_bitmap, 0, sizeof(mm->block_bitmap));
347
348 // The block size is the one knob the manager reads at init. It comes as a
349 // magnitude, so the size is a power of two and a whole number of pages by
350 // construction, with nothing to round.
351 const size_t magnitude =
353
354 mm->block_count = 0;
355 mm->block_size = (size_t)1 << magnitude;
356 memset((void *)mm->blocks, 0, sizeof(mm->blocks));
357
358 PANIC_IF(
359 mm->block_size <= sizeof(srn_block_t),
360 "Wrong block size. Configure mm.block_size_magnitude to a larger number"
361 );
362 // srn_config_validate only sees a caller provided config, so the default
363 // path is checked here too. The page size is only known at run time.
364 PANIC_IF(
365 mm->block_size % srn_mm_get_os_page_size() != 0, "Block size must be a whole number of OS pages"
366 );
367
369 for (size_t i = 0; i < MAX_NUMBER_OF_BLOCKS; i++) {
371 }
373
374 srn_block_t *immortal =
376 PANIC_IF_NULL(immortal);
377 init_block(mm, immortal);
378 mm->immortal_block = immortal;
379
380#if SERENE_DEBUG
381 mm->stats.allocated_pages = 0;
382 mm->stats.total_allocations = 0;
383 mm->stats.total_os_allocations = 0;
384 mm->stats.total_blocks = 0;
385#endif
386 return mm;
387}
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.
static void init_block(srn_mm_t *mm, srn_block_t *block)
Definition default.c:189
static srn_memory_provider_t stdlib_provider
Definition default.c:144
size_t srn_mm_get_os_page_size(void)
Retutrns the OS page size.
Definition default.c:318
#define DEFAULT_BLOCK_ALIGNMENT
We strictly use 16 bytes alignment for blocks.
Definition interface.h:58
srn_mm_config_t mm
void *(* allocate)(size_t size, size_t alignment)
Definition interface.h:75
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.
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:280
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_mm_malloc()

void * srn_mm_malloc ( 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 155 of file default.c.

155 {
156 UNUSED(mm);
157 void *ptr = malloc(size);
158 MM_TRACEPOINT(mm_malloc, (uint64_t)size, ptr);
159 return ptr;
160}
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 162 of file default.c.

162 {
163 UNUSED(mm);
164 void *out = realloc(ptr, new_size);
165 return out;
166}
Here is the caller graph for this function:

◆ 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 468 of file default.c.

468 {
469 PANIC_IF_NULL(mm);
470 destroy_chain(mm, id);
471}
static void destroy_chain(srn_mm_t *mm, srn_block_id_t root_id)
Definition default.c:211
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 389 of file default.c.

389 {
390 PANIC_IF_NULL(mm);
391 assert(mm->provider != nullptr);
392
393 for (size_t i = 0; i < MAX_NUMBER_OF_BLOCKS; i++) {
394 if (mm->blocks[i] != nullptr) {
395 destroy_chain(mm, i);
396 }
397 }
398
399 srn_block_t *block = mm->immortal_block;
400
401 while (block != nullptr) {
402 srn_block_t *tmp = block;
403 block = tmp->next;
404
405 mm->provider->release(tmp);
406 }
407
408 mm->provider->release(mm);
409}
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:82
void(* release)(void *p)
Definition interface.h:76
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 439 of file default.c.

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