limen_core/memory/manager.rs
1//! Typed token-backed message storage.
2//!
3//! This module defines [`MemoryManager`], the core storage interface for
4//! managers that own complete [`Message<P>`] values and expose them through
5//! lightweight [`MessageToken`] handles.
6//!
7//! A memory manager is responsible for:
8//!
9//! - allocating storage for new messages,
10//! - resolving tokens back to stored messages,
11//! - providing shared and exclusive borrows of stored messages,
12//! - releasing storage when a token is no longer needed,
13//! - reporting capacity and memory-class information.
14//!
15//! The trait is intentionally typed over a single payload type `P`. A concrete
16//! manager instance stores only `Message<P>` values.
17//!
18//! The API remains small and direct:
19//!
20//! - [`MemoryManager::store`]
21//! - [`MemoryManager::read`]
22//! - [`MemoryManager::read_mut`]
23//! - [`MemoryManager::free`]
24//! - [`MemoryManager::available`]
25//! - [`MemoryManager::capacity`]
26//! - [`MemoryManager::memory_class`]
27//!
28//! Shared header access is provided separately through the
29//! [`HeaderStore`] supertrait.
30//!
31//! # Guard-based borrows
32//!
33//! The borrow-returning methods use associated guard types instead of plain
34//! references so implementations can support both:
35//!
36//! - zero-overhead plain references in single-threaded managers, and
37//! - slot-level synchronization in concurrent managers.
38//!
39//! For example:
40//!
41//! - a static or heap-backed manager may use `&Message<P>` and
42//! `&mut Message<P>` directly;
43//! - a concurrent manager may use wrapper types around slot-level read/write
44//! lock guards that dereference to `Message<P>`.
45//!
46//! This keeps the public API stable while allowing implementations to choose
47//! the correct synchronization strategy internally.
48
49use core::ops::{Deref, DerefMut};
50
51use crate::errors::MemoryError;
52use crate::memory::header_store::HeaderStore;
53use crate::memory::MemoryClass;
54use crate::message::payload::Payload;
55use crate::message::Message;
56use crate::types::MessageToken;
57
58/// Typed storage interface for token-addressed messages.
59///
60/// A `MemoryManager<P>` owns stored [`Message<P>`] values and provides access
61/// to them through stable [`MessageToken`] handles.
62///
63/// The manager's responsibilities are:
64///
65/// - storing new messages and returning tokens,
66/// - resolving tokens to stored messages,
67/// - providing immutable and mutable borrows of stored messages,
68/// - freeing storage when a token is no longer live,
69/// - reporting capacity and memory-class information.
70///
71/// The trait is parameterized by `P`, and a single manager instance stores only
72/// one concrete payload type.
73///
74/// # Why `P` is a generic parameter
75///
76/// `P` is a generic parameter rather than an associated type so that a future
77/// implementation can provide `MemoryManager<P>` for multiple payload types via
78/// monomorphization without changing the external interface.
79///
80/// # Borrow model
81///
82/// `read` and `read_mut` return guards rather than naked references.
83///
84/// This is necessary because:
85///
86/// - single-threaded managers should be able to return plain references with
87/// no additional runtime overhead;
88/// - concurrent managers need the returned borrow to remain tied to a
89/// slot-level synchronization guard.
90///
91/// The associated guard types make both implementation strategies possible with
92/// one API.
93///
94/// # Implementation notes
95///
96/// Typical implementations:
97///
98/// - static manager:
99/// - `ReadGuard<'a> = &'a Message<P>`
100/// - `WriteGuard<'a> = &'a mut Message<P>`
101/// - heap manager:
102/// - `ReadGuard<'a> = &'a Message<P>`
103/// - `WriteGuard<'a> = &'a mut Message<P>`
104/// - concurrent manager:
105/// - small wrapper types around slot-level read/write lock guards
106///
107/// # Errors
108///
109/// Implementations should use [`MemoryError`] variants to report invalid
110/// tokens, unallocated slots, active borrows, poisoned synchronization
111/// primitives, or exhausted capacity.
112///
113/// # Safety
114///
115/// The trait itself is fully safe. Any unsafe code needed by a specific
116/// implementation must remain internal to that implementation.
117pub trait MemoryManager<P: Payload>: HeaderStore {
118 /// Shared read guard returned by [`MemoryManager::read`].
119 ///
120 /// This guard dereferences to the stored [`Message<P>`].
121 ///
122 /// Typical implementations:
123 ///
124 /// - `&'a Message<P>` for single-threaded managers;
125 /// - a wrapper around a slot-level read lock guard for concurrent
126 /// managers.
127 type ReadGuard<'a>: Deref<Target = Message<P>>
128 where
129 Self: 'a;
130
131 /// Exclusive mutable guard returned by [`MemoryManager::read_mut`].
132 ///
133 /// This guard dereferences mutably to the stored [`Message<P>`].
134 ///
135 /// Typical implementations:
136 ///
137 /// - `&'a mut Message<P>` for single-threaded managers;
138 /// - a wrapper around a slot-level write lock guard for concurrent
139 /// managers.
140 type WriteGuard<'a>: DerefMut<Target = Message<P>>
141 where
142 Self: 'a;
143
144 /// Allocate storage for `value` and return its token.
145 ///
146 /// The returned [`MessageToken`] becomes the stable handle used to refer to
147 /// the stored message.
148 ///
149 /// # Errors
150 ///
151 /// Returns:
152 ///
153 /// - [`MemoryError::NoFreeSlots`] if the manager has no remaining
154 /// capacity;
155 /// - [`MemoryError::Poisoned`] if a concurrent implementation cannot
156 /// recover from a poisoned synchronization primitive during allocation.
157 fn store(&mut self, value: Message<P>) -> Result<MessageToken, MemoryError>;
158
159 /// Borrow a stored message immutably.
160 ///
161 /// This is the primary read path for manager-owned messages. The returned
162 /// guard provides access to the stored message without copying it.
163 ///
164 /// # Errors
165 ///
166 /// Returns:
167 ///
168 /// - [`MemoryError::BadToken`] if `token` is invalid;
169 /// - [`MemoryError::NotAllocated`] if the slot is currently empty;
170 /// - [`MemoryError::AlreadyBorrowed`] if the implementation chooses to
171 /// report an incompatible borrow state explicitly;
172 /// - [`MemoryError::Poisoned`] if a concurrent implementation encounters a
173 /// poisoned synchronization primitive.
174 fn read(&self, token: MessageToken) -> Result<Self::ReadGuard<'_>, MemoryError>;
175
176 /// Borrow a stored message mutably.
177 ///
178 /// This method provides exclusive mutable access to the stored message
179 /// identified by `token`.
180 ///
181 /// It is intended for in-place mutation of manager-owned messages.
182 ///
183 /// # Errors
184 ///
185 /// Returns:
186 ///
187 /// - [`MemoryError::BadToken`] if `token` is invalid;
188 /// - [`MemoryError::NotAllocated`] if the slot is currently empty;
189 /// - [`MemoryError::AlreadyBorrowed`] if the slot cannot be mutably
190 /// borrowed because it is already borrowed incompatibly;
191 /// - [`MemoryError::Poisoned`] if a concurrent implementation encounters a
192 /// poisoned synchronization primitive.
193 fn read_mut(&mut self, token: MessageToken) -> Result<Self::WriteGuard<'_>, MemoryError>;
194
195 /// Free the slot identified by `token`.
196 ///
197 /// After a successful call, the token must no longer be used to access the
198 /// manager.
199 ///
200 /// # Errors
201 ///
202 /// Returns:
203 ///
204 /// - [`MemoryError::BadToken`] if `token` is invalid;
205 /// - [`MemoryError::NotAllocated`] if the slot is already empty;
206 /// - [`MemoryError::BorrowActive`] if the slot still has active borrows;
207 /// - `MemoryError::QueueOwned` if the implementation tracks queue
208 /// ownership internally and the token is still considered live in one or
209 /// more queues;
210 /// - [`MemoryError::Poisoned`] if a concurrent implementation encounters a
211 /// poisoned synchronization primitive while releasing the slot.
212 fn free(&mut self, token: MessageToken) -> Result<(), MemoryError>;
213
214 /// Return the number of currently free slots.
215 ///
216 /// This is intended for diagnostics, telemetry, and admission decisions.
217 fn available(&self) -> usize;
218
219 /// Return the total slot capacity of the manager.
220 fn capacity(&self) -> usize;
221
222 /// Return the memory class represented by this manager.
223 ///
224 /// This value describes the storage domain used by the manager, such as
225 /// host memory or another backing class.
226 fn memory_class(&self) -> MemoryClass;
227}
228
229/// Scoped handle factory for memory managers used in concurrent execution.
230///
231/// Analogous to [`ScopedEdge`](crate::edge::ScopedEdge) for memory managers.
232/// The GAT `Handle<'a>` allows implementations to return either an owned
233/// clone (Arc-based) or a borrowed view (future lock-free managers).
234#[cfg(feature = "std")]
235pub trait ScopedManager<P: crate::message::payload::Payload>:
236 crate::memory::manager::MemoryManager<P>
237{
238 /// Per-worker handle type.
239 type Handle<'a>: crate::memory::manager::MemoryManager<P> + Send + 'a
240 where
241 Self: 'a;
242
243 /// Create a scoped handle for a worker thread.
244 fn scoped_handle<'a>(&'a self) -> Self::Handle<'a>
245 where
246 Self: 'a;
247}