|
Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
|
This page is the implementation map for the memory manager: the files, the block mechanism, and the three lifetimes built on it. For the design and its rationale — why one bump primitive, why whole-chain release, why a swappable page provider, why two lock scopes — see "The Memory Manager" chapter of the language report. The subsystems whose engine-lifetime state lives in the immortal chain are documented in Fiber subsystem implementation overview and Reactor implementation overview.
| File | Role |
|---|---|
| serene/rt/mm/interface.h | Public surface: srn_mm_t, srn_block_t, the block / immortal / manual allocation APIs, the srn_memory_provider_t interface, and the two locking accessors. |
| rt/mm/default.c | The default implementation: alloc_in_block (the one bump-and-chain primitive), the block table and occupancy bitmap, the immortal chain, and the stdlib page provider. |
| serene/rt/context.h, rt/context.c | The context ownership handle: srn_context_make/release, srn_allocate, and the ALLOC/ALLOCN macros over one block chain. |
Every allocation path ends at alloc_in_block — round the target block's offset up to the requested alignment, place the object if it fits, else advance to next or link a fresh slab. What differs between paths is which chain is used and who may free it.
A srn_block_t is a large slab (DESIRED_BLOCK_SIZE, 128 KiB, rounded up to whole pages) with a header of lock/next/size/offset in front of a 16-byte-aligned base[]. Allocation is a pointer bump; a full slab links a fresh one through next, and a run of linked slabs is one region. The manager tracks blocks in a fixed 256-entry table; a block id is the index into it, and a 256-bit bitmap records which entries are in use. A single allocation larger than one slab's payload is not served today.
A context owns exactly one block chain and stands for a scope's worth of memory. It is allocated inside its own first block, so the handle lives in the memory it owns. srn_context_release frees the whole chain in one call, which is how a scope's values are made together and freed together.
Two lock scopes:
Allocation takes only the chain lock. Release takes the manager lock and then the chain lock, so an allocation racing a release either finishes before the chain is freed or resolves the released id to nothing and panics. That is the only place the two scopes nest, and it always nests in the same order.
This split fits the M:N scheduler (Fiber subsystem implementation overview): independent contexts own independent chains and never contend, and only fibers sharing one context serialize on that chain's lock. Both locks are atomic-flag spinlocks, right for the tiny critical section they cover.