Skip to main content

rylv_pool/
core.rs

1//! Provider-independent pooling primitives.
2//!
3//! [`PoolProvider`] deliberately exposes only the lifecycle operations needed
4//! by [`PoolGuard`]. Providers choose their own storage, identity, routing, and
5//! synchronization strategy.
6
7use std::{
8    convert::Infallible,
9    mem::ManuallyDrop,
10    ops::{Deref, DerefMut},
11};
12
13use stable_deref_trait::StableDeref;
14
15use super::{PoolError, error::catch_operation};
16
17/// A reusable value managed by a pool.
18///
19/// Values must be movable between threads; thread-confined values are rejected.
20///
21/// ```compile_fail,E0277
22/// use std::rc::Rc;
23/// use rylv_pool::PoolItem;
24///
25/// struct LocalOnly(Rc<()>);
26/// impl PoolItem for LocalOnly {
27///     fn reset(&mut self) {}
28/// }
29/// ```
30pub trait PoolItem: Send + 'static {
31    /// Restore a value to its reusable state before it returns to a provider.
32    fn reset(&mut self);
33}
34
35/// Supplies complete take, return, and warming operations for a pool.
36///
37/// The provider owns all policy decisions. It may use a queue, thread-local
38/// storage, sharding, remote-return queues, or another implementation without
39/// exposing those concepts through this trait.
40///
41/// # Reentrancy
42///
43/// `take` and `warm` must invoke `create` without holding an
44/// exclusive borrow or lock that would make reentrant acquisition unsound.
45/// `return_entry` must return a rejected entry to its caller rather than
46/// dropping it while such internal access remains active. Providers must keep
47/// storage invariants valid on unwind. `take` and `warm` should use
48/// [`PoolError::catch`] to capture unwinding panics.
49pub trait PoolProvider<T: PoolItem>: Send + 'static {
50    /// Provider-owned entry with exclusive access to an address-stable value.
51    /// Providers choose its allocation strategy and private routing context.
52    ///
53    /// An entry providing access without `StableDeref` is insufficient:
54    ///
55    /// ```compile_fail,E0277
56    /// use rylv_pool::{PoolError, PoolItem, PoolProvider};
57    /// # struct Item;
58    /// # impl PoolItem for Item { fn reset(&mut self) {} }
59    /// # struct InlineEntry(Item);
60    /// # impl std::ops::Deref for InlineEntry {
61    /// #     type Target = Item;
62    /// #     fn deref(&self) -> &Item { &self.0 }
63    /// # }
64    /// # impl std::ops::DerefMut for InlineEntry {
65    /// #     fn deref_mut(&mut self) -> &mut Item { &mut self.0 }
66    /// # }
67    /// struct Provider;
68    /// impl PoolProvider<Item> for Provider {
69    ///     type Entry = InlineEntry;
70    /// #   fn take<F, E>(&self, create: F) -> Result<Self::Entry, PoolError<E>>
71    /// #   where F: FnOnce() -> Result<Item, E> {
72    /// #       create().map(InlineEntry).map_err(PoolError::Factory)
73    /// #   }
74    /// #   fn return_entry(&self, entry: Self::Entry) -> Result<(), Self::Entry> {
75    /// #       Err(entry)
76    /// #   }
77    /// #   fn warm<F, E>(&self, _: usize, _: F) -> Result<usize, PoolError<E>>
78    /// #   where F: FnMut() -> Result<Item, E> { Ok(0) }
79    /// }
80    /// ```
81    type Entry: DerefMut<Target = T> + StableDeref + Send + 'static;
82
83    /// Take a reusable entry, invoking `create` only on a pool miss.
84    ///
85    /// # Errors
86    ///
87    /// Returns the factory error or a captured panic when acquisition fails.
88    fn take<F, E>(&self, create: F) -> Result<Self::Entry, PoolError<E>>
89    where
90        F: FnOnce() -> Result<T, E>;
91
92    /// Return an entry to its pool.
93    ///
94    /// On rejection, ownership must be returned so destruction happens after
95    /// provider access has ended.
96    ///
97    /// # Errors
98    ///
99    /// Returns the unchanged entry when the provider cannot retain or route
100    /// it. The guard then destroys it after provider access has ended.
101    fn return_entry(&self, entry: Self::Entry) -> Result<(), Self::Entry>;
102
103    /// Ensure that up to `count` configured values are available for reuse.
104    ///
105    /// If creation fails after some entries were inserted, those entries stay
106    /// available and the creation error is returned.
107    ///
108    /// # Errors
109    ///
110    /// Returns the factory error or a captured panic while warming the pool.
111    fn warm<F, E>(&self, count: usize, create: F) -> Result<usize, PoolError<E>>
112    where
113        F: FnMut() -> Result<T, E>;
114}
115
116// Keep this helper internal even if its containing module becomes public.
117#[allow(clippy::redundant_pub_crate)]
118pub(super) struct Storage<E, const N: usize> {
119    slots: [Option<E>; N],
120    len: usize,
121}
122
123impl<E, const N: usize> Storage<E, N> {
124    pub(super) const fn new() -> Self {
125        Self {
126            slots: [const { None }; N],
127            len: 0,
128        }
129    }
130
131    #[inline(always)]
132    pub(super) const fn pop(&mut self) -> Option<E> {
133        if self.len == 0 {
134            return None;
135        }
136        self.len -= 1;
137        self.slots[self.len].take()
138    }
139
140    #[inline(always)]
141    pub(super) fn try_push(&mut self, entry: E) -> Result<(), E> {
142        if self.len < N {
143            self.slots[self.len] = Some(entry);
144            self.len += 1;
145            Ok(())
146        } else {
147            Err(entry)
148        }
149    }
150
151    #[inline(always)]
152    pub(super) const fn len(&self) -> usize {
153        self.len
154    }
155
156    /// Retain the newest entries that fit, leaving older overflow in the
157    /// iterator. Only the appended slots are reversed to preserve pop order.
158    pub(super) fn extend_newest_first(&mut self, entries: &mut impl Iterator<Item = E>) {
159        let start = self.len;
160        while self.len < N {
161            let Some(entry) = entries.next() else {
162                break;
163            };
164            self.slots[self.len] = Some(entry);
165            self.len += 1;
166        }
167        self.slots[start..self.len].reverse();
168    }
169}
170
171/// Exclusive, address-stable guard returned by a [`PoolProvider`].
172///
173/// # Address stability
174///
175/// The provider entry implements [`StableDeref`], keeping the value at the same
176/// address for the complete lifetime of the guard, even when the guard moves.
177///
178/// References into the value cannot outlive the guard:
179///
180/// ```compile_fail,E0505
181/// use rylv_pool::{FixedThreadLocalPool, FixedThreadLocalPoolGuard, PoolItem};
182/// # #[derive(Default)]
183/// # struct Item(Vec<u8>);
184/// # impl PoolItem for Item { fn reset(&mut self) { self.0.clear(); } }
185/// # thread_local! {
186/// #     static POOL: FixedThreadLocalPool<Item, 1> = FixedThreadLocalPool::new();
187/// # }
188/// let mut guard = FixedThreadLocalPoolGuard::acquire(&POOL).unwrap();
189/// let borrowed = &mut guard.0;
190/// drop(guard);
191/// borrowed.push(1);
192/// ```
193///
194/// A guard cannot provide two simultaneously used mutable references:
195///
196/// ```compile_fail,E0499
197/// use rylv_pool::{FixedThreadLocalPool, FixedThreadLocalPoolGuard, PoolItem};
198/// # #[derive(Default)]
199/// # struct Item(Vec<u8>);
200/// # impl PoolItem for Item { fn reset(&mut self) { self.0.clear(); } }
201/// # thread_local! {
202/// #     static POOL: FixedThreadLocalPool<Item, 1> = FixedThreadLocalPool::new();
203/// # }
204/// let mut guard = FixedThreadLocalPoolGuard::acquire(&POOL).unwrap();
205/// let first = &mut guard.0;
206/// let second = &mut guard.0;
207/// first.push(1);
208/// second.push(2);
209/// ```
210///
211/// A guard cannot be cloned to create a second owner:
212///
213/// ```compile_fail,E0599
214/// use rylv_pool::{FixedThreadLocalPool, FixedThreadLocalPoolGuard, PoolItem};
215/// # #[derive(Default)]
216/// # struct Item;
217/// # impl PoolItem for Item { fn reset(&mut self) {} }
218/// # thread_local! {
219/// #     static POOL: FixedThreadLocalPool<Item, 1> = FixedThreadLocalPool::new();
220/// # }
221/// let guard = FixedThreadLocalPoolGuard::acquire(&POOL).unwrap();
222/// let duplicate = guard.clone();
223/// ```
224pub struct PoolGuard<T: PoolItem, P: PoolProvider<T>> {
225    node: ManuallyDrop<P::Entry>,
226    provider: P,
227}
228
229impl<T: PoolItem, P: PoolProvider<T>> PoolGuard<T, P> {
230    /// Acquire a value through the supplied provider.
231    ///
232    /// # Errors
233    ///
234    /// Returns a captured panic if acquisition or the default factory unwinds.
235    #[inline(always)]
236    pub fn acquire(provider: P) -> Result<Self, PoolError<Infallible>>
237    where
238        T: Default,
239    {
240        Self::acquire_with(provider, T::default)
241    }
242
243    /// Acquire a value, using `create` only when the provider misses.
244    ///
245    /// # Errors
246    ///
247    /// Returns a captured panic if acquisition or the factory unwinds.
248    #[inline(always)]
249    pub fn acquire_with<F>(provider: P, create: F) -> Result<Self, PoolError<Infallible>>
250    where
251        F: FnOnce() -> T,
252    {
253        Self::try_acquire_with(provider, || Ok::<T, Infallible>(create()))
254    }
255
256    /// Acquire a value using a fallible factory only when the provider misses.
257    ///
258    /// # Errors
259    ///
260    /// Returns the factory error or a captured panic without creating a guard.
261    #[inline(always)]
262    pub fn try_acquire_with<F, E>(provider: P, create: F) -> Result<Self, PoolError<E>>
263    where
264        F: FnOnce() -> Result<T, E>,
265    {
266        Ok(Self {
267            node: ManuallyDrop::new(
268                catch_operation(|| provider.take(create)).map_err(PoolError::Panic)??,
269            ),
270            provider,
271        })
272    }
273}
274
275impl<T: PoolItem, P: PoolProvider<T>> Deref for PoolGuard<T, P> {
276    type Target = T;
277
278    #[inline(always)]
279    fn deref(&self) -> &Self::Target {
280        self.node.deref()
281    }
282}
283
284impl<T: PoolItem, P: PoolProvider<T>> DerefMut for PoolGuard<T, P> {
285    #[inline(always)]
286    fn deref_mut(&mut self) -> &mut Self::Target {
287        self.node.deref_mut()
288    }
289}
290
291// SAFETY: P::Entry implements StableDeref and the guard never replaces it.
292unsafe impl<T: PoolItem, P: PoolProvider<T>> StableDeref for PoolGuard<T, P> {}
293
294impl<T: PoolItem, P: PoolProvider<T>> Drop for PoolGuard<T, P> {
295    #[inline(always)]
296    fn drop(&mut self) {
297        // SAFETY: `node` is initialized by every constructor, `PoolGuard`
298        // cannot be reused after `Drop` starts, and this is its only take.
299        let mut node = unsafe { ManuallyDrop::take(&mut self.node) };
300        node.reset();
301
302        if let Err(node) = self.provider.return_entry(node) {
303            discard_entry(node);
304        }
305    }
306}
307
308/// Destroy rejected entries after provider access has ended.
309#[cold]
310#[inline(never)]
311fn discard_entry<E>(entry: E) {
312    drop(entry);
313}
314
315#[cfg(test)]
316mod tests;