Skip to main content

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}