shm_primitives/
slot.rs

1use crate::sync::{AtomicU32, Ordering};
2
3/// Slot states for generational slots.
4#[repr(u32)]
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum SlotState {
7    Free = 0,
8    Allocated = 1,
9    InFlight = 2,
10}
11
12impl SlotState {
13    #[inline]
14    pub fn from_u32(v: u32) -> Option<Self> {
15        match v {
16            0 => Some(SlotState::Free),
17            1 => Some(SlotState::Allocated),
18            2 => Some(SlotState::InFlight),
19            _ => None,
20        }
21    }
22}
23
24/// Metadata for a single slot.
25#[repr(C)]
26pub struct SlotMeta {
27    pub generation: AtomicU32,
28    pub state: AtomicU32,
29}
30
31#[cfg(not(loom))]
32const _: () = assert!(core::mem::size_of::<SlotMeta>() == 8);
33
34/// Metadata for a variable-size slot with ownership tracking.
35///
36/// Used by shared variable-size slot pools where we need to track
37/// which peer allocated each slot for crash recovery.
38///
39/// shm[impl shm.varslot.ownership]
40#[repr(C)]
41pub struct VarSlotMeta {
42    /// ABA counter, incremented on allocation.
43    pub generation: AtomicU32,
44    /// Slot state: Free=0, Allocated=1, InFlight=2.
45    pub state: AtomicU32,
46    /// Peer ID that allocated this slot (0 = host, 1-255 = guest).
47    /// Used for crash recovery: when a peer dies, its slots are reclaimed.
48    pub owner_peer: AtomicU32,
49    /// Free list link (next free slot index, or u32::MAX for end).
50    pub next_free: AtomicU32,
51}
52
53#[cfg(not(loom))]
54const _: () = assert!(core::mem::size_of::<VarSlotMeta>() == 16);
55
56impl VarSlotMeta {
57    /// Initialize a new variable slot metadata entry.
58    #[inline]
59    pub fn init(&mut self) {
60        self.generation = AtomicU32::new(0);
61        self.state = AtomicU32::new(SlotState::Free as u32);
62        self.owner_peer = AtomicU32::new(0);
63        self.next_free = AtomicU32::new(u32::MAX);
64    }
65
66    /// Read the current generation.
67    #[inline]
68    pub fn generation(&self) -> u32 {
69        self.generation.load(Ordering::Acquire)
70    }
71
72    /// Read the current state.
73    #[inline]
74    pub fn state(&self) -> SlotState {
75        SlotState::from_u32(self.state.load(Ordering::Acquire)).unwrap_or(SlotState::Free)
76    }
77
78    /// Read the owner peer ID.
79    #[inline]
80    pub fn owner(&self) -> u8 {
81        self.owner_peer.load(Ordering::Acquire) as u8
82    }
83
84    /// Read the next free slot index.
85    #[inline]
86    pub fn next_free(&self) -> u32 {
87        self.next_free.load(Ordering::Acquire)
88    }
89
90    /// Check if the slot matches the expected generation.
91    #[inline]
92    pub fn check_generation(&self, expected: u32) -> bool {
93        self.generation.load(Ordering::Acquire) == expected
94    }
95}
96
97impl SlotMeta {
98    /// Initialize a new slot metadata entry.
99    #[inline]
100    pub fn init(&mut self) {
101        self.generation = AtomicU32::new(0);
102        self.state = AtomicU32::new(SlotState::Free as u32);
103    }
104
105    /// Attempt to transition state.
106    ///
107    /// Returns `Ok(generation)` on success, `Err(actual_state)` on failure.
108    #[inline]
109    pub fn try_transition(&self, expected: SlotState, new: SlotState) -> Result<u32, SlotState> {
110        match self.state.compare_exchange(
111            expected as u32,
112            new as u32,
113            Ordering::AcqRel,
114            Ordering::Acquire,
115        ) {
116            Ok(_) => Ok(self.generation.load(Ordering::Acquire)),
117            Err(actual) => Err(SlotState::from_u32(actual).unwrap_or(SlotState::Free)),
118        }
119    }
120
121    /// Check if the slot matches the expected generation.
122    #[inline]
123    pub fn check_generation(&self, expected: u32) -> bool {
124        self.generation.load(Ordering::Acquire) == expected
125    }
126
127    /// Read the current generation.
128    #[inline]
129    pub fn generation(&self) -> u32 {
130        self.generation.load(Ordering::Acquire)
131    }
132
133    /// Read the current state.
134    #[inline]
135    pub fn state(&self) -> SlotState {
136        SlotState::from_u32(self.state.load(Ordering::Acquire)).unwrap_or(SlotState::Free)
137    }
138}