spark_storage/snapshot_swap/
mmap_arena.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2
3// ─────────────────────────── real peer-mmap arena ───────────────────────────
4
5use anyhow::{Result, bail};
6
7use super::SlotArena;
8
9/// `SlotArena` over the peer's RDMA-registered `mmap` region (a raw base ptr).
10/// The peer memcpys between an arena slot and the disk swap on spill/fault; the
11/// client one-sided-RDMAs into/out of the same slots. The base VA is stable and
12/// registered ONCE per rail — this NEVER re-registers (no MR churn).
13///
14/// SAFETY: `base` must point at a live mapping of at least `num_slots *
15/// slot_bytes` bytes, page-aligned (mmap guarantees this), outliving the arena.
16/// (Peer-specific — deliberately NOT lifted into atlas-tier: the lifted crate
17/// carries no unsafe raw-pointer arena types.)
18pub struct MmapSlotArena {
19    base: *mut u8,
20    slot_bytes: usize,
21    num_slots: usize,
22}
23unsafe impl Send for MmapSlotArena {}
24
25impl MmapSlotArena {
26    /// # Safety
27    /// `base` must be a valid, writable mapping of `>= num_slots*slot_bytes`
28    /// bytes that outlives this arena.
29    pub unsafe fn new(base: *mut u8, slot_bytes: usize, num_slots: usize) -> Self {
30        Self {
31            base,
32            slot_bytes,
33            num_slots,
34        }
35    }
36    fn slot_ptr(&self, slot: usize) -> *mut u8 {
37        // slot < num_slots enforced by callers (residency free-list).
38        unsafe { self.base.add(slot * self.slot_bytes) }
39    }
40}
41
42impl SlotArena for MmapSlotArena {
43    fn slot_bytes(&self) -> usize {
44        self.slot_bytes
45    }
46    fn num_slots(&self) -> usize {
47        self.num_slots
48    }
49    fn read_slot(&self, slot: usize, out: &mut [u8]) -> Result<()> {
50        if slot >= self.num_slots || out.len() != self.slot_bytes {
51            bail!("read_slot({slot}) out of range / size mismatch");
52        }
53        unsafe {
54            std::ptr::copy_nonoverlapping(self.slot_ptr(slot), out.as_mut_ptr(), self.slot_bytes)
55        };
56        Ok(())
57    }
58    fn write_slot(&mut self, slot: usize, bytes: &[u8]) -> Result<()> {
59        if slot >= self.num_slots || bytes.len() != self.slot_bytes {
60            bail!("write_slot({slot}) out of range / size mismatch");
61        }
62        unsafe {
63            std::ptr::copy_nonoverlapping(bytes.as_ptr(), self.slot_ptr(slot), self.slot_bytes)
64        };
65        Ok(())
66    }
67}
68
69#[cfg(test)]
70#[path = "mmap_arena_tests.rs"]
71mod tests;