Skip to main content

MemMapTransaction

Struct MemMapTransaction 

Source
pub struct MemMapTransaction<'a, T: Sized + Clone + PartialEq> { /* private fields */ }
Expand description

Context for grouping multiple conditional writes into a single atomic operation

Multi-slot mutations are staged in MemMapTransaction with optimistic concurrency preconditions. At commit time, all target slot locks are acquired in strictly ascending order, each slot is verified against its expected value, and if all match, all updates are applied under a single epoch

If any slot fails verification, the transaction aborts with zero mutations, returning [err::CAS] with the index of the first conflicting slot so the caller can retry

§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_struct_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, &42u64, 0).unwrap();
tx.write(&0u64, &84u64, 2).unwrap();

let ticket = tx.commit().unwrap();
assert_eq!(mmap.read(0, |v| *v).unwrap(), 42u64);
assert_eq!(mmap.read(2, |v| *v).unwrap(), 84u64);

Implementations§

Source§

impl<T> MemMapTransaction<'_, T>
where T: Sized + Clone + PartialEq,

Source

pub fn write( &mut self, expected: &T, new: &T, index: usize, ) -> FrozenResult<&mut Self>

Stages a conditional write of new at index requiring expected as the current slot value

§Requirements
  • Writes must be staged in strictly increasing index order (index > previous_index)
  • Duplicate indices in the same transaction return an error
  • index must not exceed mapping bounds
§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_write_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, &111u64, 0).unwrap();
tx.write(&0u64, &222u64, 1).unwrap();

// Staging an out-of-order or duplicate index fails
assert!(tx.write(&0u64, &333u64, 1).is_err());
assert!(tx.write(&0u64, &333u64, 0).is_err());
Source

pub fn commit(self) -> FrozenResult<AckTicket>

Commits all staged writes atomically under a single durability epoch

Acquires exclusive locks for all affected slots in order; if all slots match their expected values, updates are applied, the durable epoch advances once, and an AckTicket is returned

If any slot differs from expected, all locks are released without modifying any data, and [err::CAS] is returned with context identifying the conflicting slot index

§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_commit_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, &100u64, 0).unwrap();
tx.write(&0u64, &200u64, 1).unwrap();

let ticket = tx.commit().unwrap();
assert_eq!(mmap.read(0, |v| *v).unwrap(), 100u64);
assert_eq!(mmap.read(1, |v| *v).unwrap(), 200u64);
Source

pub fn len(&self) -> usize

Returns the number of staged operations in this transaction

§Example
use frozen_core::memmap::{MemMap, MemMapCfg};

let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
    module_id: 1,
    path: dir.path().join("tx_len_demo.bin"),
    version: 1,
    initial_t: 512,
    sync_interval: None,
    page_size: None,
};

let mmap = MemMap::<u64>::new(cfg).unwrap();
let mut tx = mmap.new_tx();
assert_eq!(tx.len(), 0);
tx.write(&0u64, &10u64, 0).unwrap();
assert_eq!(tx.len(), 1);
Source

pub fn is_empty(&self) -> bool

Returns true if no operations have been staged in this transaction

§Example
use frozen_core::memmap::{MemMap, MemMapCfg};

let dir = tempfile::tempdir().unwrap();
let cfg = MemMapCfg {
    module_id: 1,
    path: dir.path().join("tx_empty_demo.bin"),
    version: 1,
    initial_t: 512,
    sync_interval: None,
    page_size: None,
};

let mmap = MemMap::<u64>::new(cfg).unwrap();
let tx = mmap.new_tx();
assert!(tx.is_empty());

Trait Implementations§

Source§

impl<'a, T: Debug + Sized + Clone + PartialEq> Debug for MemMapTransaction<'a, T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a, T> !RefUnwindSafe for MemMapTransaction<'a, T>

§

impl<'a, T> !UnwindSafe for MemMapTransaction<'a, T>

§

impl<'a, T> Freeze for MemMapTransaction<'a, T>
where &'a MemMap<T>: Freeze, Vec<(usize, T, T)>: Freeze,

§

impl<'a, T> Send for MemMapTransaction<'a, T>
where &'a MemMap<T>: Send, Vec<(usize, T, T)>: Send,

§

impl<'a, T> Sync for MemMapTransaction<'a, T>
where &'a MemMap<T>: Sync, Vec<(usize, T, T)>: Sync,

§

impl<'a, T> Unpin for MemMapTransaction<'a, T>
where &'a MemMap<T>: Unpin, Vec<(usize, T, T)>: Unpin,

§

impl<'a, T> UnsafeUnpin for MemMapTransaction<'a, T>
where &'a MemMap<T>: UnsafeUnpin, Vec<(usize, T, T)>: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.