pub struct MemMap<T: Sized + Clone> { /* private fields */ }Expand description
Custom memory-mapped typed array storage with slot-level locking and epoch durability
MemMap<T> maps an on-disk File into process virtual address space as a contiguous array of
fixed-size slots of type T
It provides,
- Slot Synchronization: Per-slot reader-writer spinlocks (
SlotLocks) protecting each index against concurrent write-write and read-write hazards with backoff spin/yield/sleep stages - Epoch Durability: Monotonically advancing write epoch counter incremented on each
mutation, tracked via
AckTicketand synchronized in the background by a dedicated worker - Process Isolation: Advisory kernel-level locking (
flock/LockFileEx) ensuring exclusive file ownership by a single running instance
§Constraints
Tmust be sized and cloneable (T: Sized + Clone)Tmust be 8-byte aligned (core::mem::align_of::<T>() == 8)size_of::<T>()must be non-zero, a multiple of 8, and must not exceed the host page sizeTmust not implementDrop(!core::mem::needs_drop::<T>())- Type layout is immutable across file lifetime; schema changes require bumping
MemMapCfg::version
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("storage.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let memmap = MemMap::<u64>::new(cfg).unwrap();
assert_eq!(memmap.length(), 512 * std::mem::size_of::<u64>());
let (ticket, _) = memmap.write(0, |v| *v = 12345u64).unwrap();
assert_eq!(memmap.read(0, |v| *v).unwrap(), 12345u64);
assert!(memmap.delete().is_ok());Implementations§
Source§impl<T> MemMap<T>
impl<T> MemMap<T>
Sourcepub fn new(cfg: MemMapCfg) -> FrozenResult<Self>
pub fn new(cfg: MemMapCfg) -> FrozenResult<Self>
Creates and memory-maps a new File at cfg.versioned_path()
Allocates an underlying file with capacity for initial_t * size_of::<T>() bytes, acquires
an exclusive advisory lock on the descriptor, establishes the memory mapping, and spawns the
background worker for periodic durability flushing
§Constraints
- Returns [
err::EXS] if the versioned file already exists on the filesystem; UseMemMap::openorMemMap::open_or_createinstead - Returns [
err::PTH] or [err::DIR] if the base path or its parent directory is invalid - Enforces all structural type invariants for
T(align == 8,size % 8 == 0,size <= page_size, noDrop) - An exclusive advisory lock is acquired on creation; concurrent callers on the same file path fail
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("new_memmap.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg.clone()).unwrap();
assert_eq!(mmap.length(), std::mem::size_of::<u64>() * 512);
// Creating another file at the same path fails
assert!(MemMap::<u64>::new(cfg).is_err());Sourcepub fn open(cfg: MemMapCfg) -> FrozenResult<Self>
pub fn open(cfg: MemMapCfg) -> FrozenResult<Self>
Opens an existing memory-mapped File at cfg.versioned_path()
Opens the underlying versioned file, verifies its length matches the requested layout, acquires an exclusive advisory lock on the descriptor, maps its memory region, and spawns the background flush worker
§Constraints
- Returns [
err::NEX] if the backing file does not exist on disk - Returns [
err::PTH] if the path resolves to a directory - Fails with advisory lock error if another handle currently owns the exclusive lock
- Type
Tinvariants must strictly match the binary layout originally written to the file
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("open_memmap.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
{
let mmap = MemMap::<u64>::new(cfg.clone()).unwrap();
assert_eq!(mmap.length(), std::mem::size_of::<u64>() * 512);
}
let opened = MemMap::<u64>::open(cfg).unwrap();
assert_eq!(opened.length(), std::mem::size_of::<u64>() * 512);Sourcepub fn open_or_create(cfg: MemMapCfg) -> FrozenResult<Self>
pub fn open_or_create(cfg: MemMapCfg) -> FrozenResult<Self>
Opens an existing memory-mapped File or creates a new one if it does not yet exist
Idempotent entrypoint that transparently delegates to either open or creation semantics based on filesystem presence, maintaining consistent advisory locking and background flushing
§Constraints
- Returns [
err::PTH] if the resolved path points to an existing directory - Returns [
err::DIR] if parent directory does not exist - Fails if another handle currently owns the advisory lock on the target file
- All structural
Tinvariants (align,size,needs_drop) are strictly validated
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("open_or_create_memmap.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
{
let mmap = MemMap::<u64>::open_or_create(cfg.clone()).unwrap();
assert_eq!(mmap.length(), std::mem::size_of::<u64>() * 512);
}
let reopened = MemMap::<u64>::open_or_create(cfg).unwrap();
assert_eq!(reopened.length(), std::mem::size_of::<u64>() * 512);Sourcepub fn page_size() -> FrozenResult<PageSize>
pub fn page_size() -> FrozenResult<PageSize>
Returns the system memory page size queried from kernel settings
§Example
use frozen_core::memmap::{MemMap, PageSize};
let page_size = MemMap::<u64>::page_size().unwrap();
assert!(page_size.as_usize() >= 4096);
assert!(page_size.is_power_of_two());Sourcepub fn length(&self) -> usize
pub fn length(&self) -> usize
Returns the length of the memory mapping in bytes
Corresponds to initial_t * size_of::<T>()
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("length_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
assert_eq!(mmap.length(), 512 * std::mem::size_of::<u64>());Sourcepub fn grow(&self, pages: usize) -> FrozenResult<()>
pub fn grow(&self, pages: usize) -> FrozenResult<()>
Dynamically grows the memory-mapped file by pages additional virtual memory pages
§Semantics
- Halts all concurrent read and write operations across all slots for the duration of the growth
- Halts background durability flushing and manual sync operations
- If uncommitted dirty writes exist (
current_epoch > durable_epoch), performs a durability sync before modifying file boundaries - Explicitly unmaps the current virtual memory mapping
- Zero-extends the backing file on disk safely via
File::growand synchronizes file metadata in units of the configured page size - Establishes a new memory mapping over the enlarged file range
- Dynamically expands per-slot spinlocks to accommodate new slots
- Updates tracked mapping length and advances durability epoch
§Crash Durability & Safety
- Existing payload data is flushed to persistent storage before file extension, ensuring no data loss
- Disk block pre-allocation (
fallocate/f_preallocate) precedes logical size expansion; out-of-space failures leave existing data untouched - If file growth fails, the original memory mapping is automatically restored
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("grow_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: None,
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
assert_eq!(mmap.length(), 512 * std::mem::size_of::<u64>());
mmap.write(0, |v| *v = 111u64).unwrap();
mmap.grow(1).unwrap();
assert_eq!(mmap.length(), (512 * std::mem::size_of::<u64>()) + MemMap::<u64>::page_size().unwrap().as_usize());
assert_eq!(mmap.read(0, |v| *v).unwrap(), 111u64);
assert_eq!(mmap.read(512, |v| *v).unwrap(), 0u64);Sourcepub fn cfg(&self) -> &MemMapCfg
pub fn cfg(&self) -> &MemMapCfg
Returns a reference to the active MemMapCfg configuration
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("cfg_demo.bin"),
version: 2,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
assert_eq!(mmap.cfg().version, 2);
assert_eq!(mmap.cfg().initial_t, 512);Sourcepub fn current_epoch(&self) -> TEpoch
pub fn current_epoch(&self) -> TEpoch
Fetch the latest assigned durability epoch
The current epoch monotonically increments on each MemMap::write call
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("epoch_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
assert_eq!(mmap.current_epoch(), 0);
mmap.write(0, |v| *v = 999u64).unwrap();
assert_eq!(mmap.current_epoch(), 1);Sourcepub fn durable_epoch(&self) -> TEpoch
pub fn durable_epoch(&self) -> TEpoch
Fetch the latest confirmed durable epoch flushed to persistent storage
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("durable_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
let (ticket, _) = mmap.write(0, |v| *v = 100u64).unwrap();
mmap.sync().unwrap();
assert_eq!(mmap.durable_epoch(), ticket.epoch());Sourcepub fn sync(&self) -> FrozenResult<()>
pub fn sync(&self) -> FrozenResult<()>
Synchronize dirty pages and file metadata to stable storage
Performs a synchronous two-phase flush,
- Flushes modified virtual memory pages to host cache via [
PlatformMemMap::sync] - Advances the durable epoch to the current write epoch and awakens all pending listeners
Returns Ok(()) immediately if no writes have occurred since the last sync
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("sync_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: None,
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
let (ticket, _) = mmap.write(1, |v| *v = 0xCAFE_BABE).unwrap();
assert!(!ticket.is_durable());
mmap.sync().unwrap();
assert!(ticket.is_durable());Sourcepub fn read<R, F>(&self, index: usize, f: F) -> FrozenResult<R>
pub fn read<R, F>(&self, index: usize, f: F) -> FrozenResult<R>
Locks slot index for reading and invokes f with a reference to T
§Constraints
index * size_of::<T>()must not overflowusize, otherwise returns [err::INV]- Reading past the mapped memory region returns [
err::HCF]
§Zero-Copy & Safety
Callback f is invoked with &T directly referencing mapped memory under lock
The higher-ranked lifetime bound (F: FnOnce(&T) -> R) ensures references cannot
escape or outlive the locked callback scope
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("read_test.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
mmap.write(0, |v| *v = 42u64).unwrap();
assert_eq!(mmap.read(0, |v| *v).unwrap(), 42u64);Sourcepub fn read_exact<Target, R, F>(&self, start: usize, f: F) -> FrozenResult<R>
pub fn read_exact<Target, R, F>(&self, start: usize, f: F) -> FrozenResult<R>
Atomically locks N consecutive slots and invokes f with a reference to Target
Requires size_of::<Target>() == N * size_of::<T>() and compatible alignment
§Constraints
size_of::<Target>()must not be zero ([err::ZRO])size_of::<Target>()must be an exact multiple ofsize_of::<T>()([err::SZE])align_of::<Target>()must not exceedalign_of::<T>()([err::ALN])- Slot index and byte offset calculations must not overflow
usize([err::INV]) - Target slot range
start..start + Nmust not exceed mapping bounds ([err::HCF])
§Deadlock Freedom
Read spinlocks for slots start..start + N are acquired in strictly ascending order
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
#[derive(Clone, Copy)]
#[repr(C)]
struct Page {
header: u64,
payload: [u64; 3],
}
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("read_exact_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
mmap.write(0, |v| *v = 0xAA).unwrap();
mmap.write(1, |v| *v = 0xBB).unwrap();
mmap.write(2, |v| *v = 0xCC).unwrap();
mmap.write(3, |v| *v = 0xDD).unwrap();
let header = mmap.read_exact(0, |page: &Page| page.header).unwrap();
assert_eq!(header, 0xAA);Sourcepub fn write<R, F>(&self, index: usize, f: F) -> FrozenResult<(AckTicket, R)>
pub fn write<R, F>(&self, index: usize, f: F) -> FrozenResult<(AckTicket, R)>
Locks slot index for writing and invokes f with a mutable reference to T
Returns a tuple (AckTicket, R) containing the durability acknowledgement ticket
and the result returned by callback f
§Constraints
index * size_of::<T>()must not overflowusize, otherwise returns [err::INV]- Writing past the mapped memory region returns [
err::HCF]
§Deadlock & Safety
- Exclusive writer spinlock for slot
indexis held during closure execution - If the closure unwinds / panics, the spinlock is cleanly released via RAII and the durability epoch counter is not incremented
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("write_test.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
let (ticket, _) = mmap.write(2, |v| *v = 100u64).unwrap();
assert_eq!(mmap.read(2, |v| *v).unwrap(), 100u64);Sourcepub fn write_exact<Target, R, F>(
&self,
start: usize,
f: F,
) -> FrozenResult<(AckTicket, R)>where
F: FnOnce(&mut Target) -> R,
pub fn write_exact<Target, R, F>(
&self,
start: usize,
f: F,
) -> FrozenResult<(AckTicket, R)>where
F: FnOnce(&mut Target) -> R,
Atomically locks N consecutive slots and invokes f with mutable access to Target
Requires size_of::<Target>() == N * size_of::<T>() and compatible alignment
Returns a tuple (AckTicket, R) containing the durability acknowledgement ticket and the
result returned by callback f
§Constraints
size_of::<Target>()must not be zero ([err::ZRO])size_of::<Target>()must be an exact multiple ofsize_of::<T>()([err::SZE])align_of::<Target>()must not exceedalign_of::<T>()([err::ALN])- Slot index and byte offset calculations must not overflow
usize([err::INV]) - Target slot range
start..start + Nmust not exceed mapping bounds ([err::HCF])
§Deadlock Freedom
Exclusive write spinlocks for slots start..start + N are acquired in strictly ascending
order
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
#[repr(C)]
struct Page {
header: u64,
payload: [u64; 3],
}
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("write_exact_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
let (ticket, _) = mmap
.write_exact(0, |page: &mut Page| {
page.header = 0xAA;
page.payload[0] = 0xBB;
})
.unwrap();
assert_eq!(mmap.read(0, |v| *v).unwrap(), 0xAA);
assert_eq!(mmap.read(1, |v| *v).unwrap(), 0xBB);
assert_eq!(ticket.epoch(), 1);Sourcepub fn delete(self) -> FrozenResult<()>
pub fn delete(self) -> FrozenResult<()>
Deletes the MemMap and underlying File entry from the storage device
Consumes self by value to release resources in deterministic order,
- Signals the background flush worker to stop and waits for its thread join
- Unmaps the active memory mapping from virtual address space
- Closes and unlinks the backing versioned file from the filesystem
§Constraints
- Consumes
selfby value; after deletion, the instance can no longer be accessed - Returns [
err::INV] if the underlying file handle was already released
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("delete_demo.bin");
let versioned_path = dir.path().join("delete_demo_1.bin");
let cfg = MemMapCfg {
module_id: 1,
path,
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
assert!(versioned_path.exists());
mmap.delete().unwrap();
assert!(!versioned_path.exists());Source§impl<T> MemMap<T>
impl<T> MemMap<T>
Sourcepub fn compare_and_write(
&self,
expected: &T,
val: &T,
index: usize,
) -> FrozenResult<AckTicket>
pub fn compare_and_write( &self, expected: &T, val: &T, index: usize, ) -> FrozenResult<AckTicket>
Conditionally writes val into slot index if and only if the current value matches
expected
§Semantics
Acquires the exclusive slot lock for index and verifies that the current slot value equals
expected; if equal, writes val, advances the epoch, and returns an
AckTicket. If the current value differs from expected, the operation immediately
aborts without modifying the slot, returning [err::CAS] so the caller can re-read and
retry
§Constraints
index * size_of::<T>()must not overflowusize, otherwise returns [err::INV]- Writing past the mapped memory region returns [
err::HCF] - Returns [
err::CAS] ifexpected != current
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("cas_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
mmap.write(0, |v| *v = 100u64).unwrap();
// Successful CAS: 100 -> 200
let ticket = mmap.compare_and_write(&100u64, &200u64, 0).unwrap();
assert_eq!(mmap.read(0, |v| *v).unwrap(), 200u64);
// Failed CAS: expected 100, but slot is 200
assert!(mmap.compare_and_write(&100u64, &300u64, 0).is_err());
assert_eq!(mmap.read(0, |v| *v).unwrap(), 200u64);Sourcepub fn new_tx(&self) -> MemMapTransaction<'_, T>
pub fn new_tx(&self) -> MemMapTransaction<'_, T>
Creates a new MemMapTransaction context for grouping multiple conditional writes into a
single atomic epoch
§Overview
MemMapTransaction allows grouping multiple slot mutations into a single atomic operation:
- Atomicity: All writes succeed or fail together
- Optimistic Concurrency Control (CAS): Each write asserts that its target slot
currently matches
expected; if any slot differs at commit time, all changes abort and no slots are modified - Single Epoch: All committed writes share a single durability epoch and durable window
- Deadlock Freedom: Slot locks are acquired in strictly ascending index order
§Example
use frozen_core::memmap::{MemMap, MemMapCfg};
use std::time::Duration;
let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
module_id: 1,
path: dir.path().join("tx_demo.bin"),
version: 1,
initial_t: 512,
sync_interval: Some(Duration::from_millis(100)),
page_size: None,
};
let mmap = MemMap::<u64>::new(cfg).unwrap();
let mut tx = mmap.new_tx();
tx.write(&0u64, &10u64, 0).unwrap();
tx.write(&0u64, &20u64, 1).unwrap();
let ticket = tx.commit().unwrap();
assert_eq!(mmap.read(0, |v| *v).unwrap(), 10u64);
assert_eq!(mmap.read(1, |v| *v).unwrap(), 20u64);