anchored_pool/
unbounded.rs

1#![expect(
2    unsafe_code,
3    reason = "Use UnsafeCell instead of the needless overhead of RefCell;
4              let unsafe code in Pools rely on PooledResource Drop impl",
5)]
6
7use std::{cell::UnsafeCell, rc::Rc};
8
9#[cfg(feature = "clone-behavior")]
10use clone_behavior::{MirroredClone, Speed};
11
12use crate::{
13    pooled_resource::{PooledResource, SealedPool},
14    other_utils::{ResetNothing, ResetResource},
15};
16
17
18/// A resource pool with a growable number of `Resource`s.
19#[derive(Debug)]
20pub struct UnboundedPool<Resource, Reset> {
21    /// Safety: the `UnsafeCell` is only accessed from `Self::get`, `Self::len`,
22    /// `Self::available_resources` and `Self::return_resource`. None of them
23    /// allow a reference to something inside the `UnsafeCell` to escape outside the function body.
24    /// The only potential for them to call each other is in the callback passed to `Self::get`,
25    /// and the `reset_resource` callback. `Self::get` ensures that the contents of the
26    /// `UnsafeCell` are not borrowed while the callback is run, and likewise for
27    /// `Self::return_resource` with the `reset_resource` callback. Other than that, they do not
28    /// call each other.
29    pool: Rc<UnsafeCell<(
30        // Pool contents
31        Vec<Resource>,
32        // The number of resources currently in-use
33        usize,
34    )>>,
35    reset_resource: Reset,
36}
37
38impl<Resource, Reset> UnboundedPool<Resource, Reset> {
39    /// Create a new `UnboundedPool`, which initially has zero `Resource`s.
40    ///
41    /// Whenever a `Resource` is returned to the pool, `reset_resource` is run on it first.
42    #[inline]
43    #[must_use]
44    pub fn new(reset_resource: Reset) -> Self
45    where
46        Reset: ResetResource<Resource> + Clone,
47    {
48        Self {
49            pool: Rc::new(UnsafeCell::new((Vec::new(), 0))),
50            reset_resource,
51        }
52    }
53}
54
55impl<Resource> UnboundedPool<Resource, ResetNothing> {
56    /// Create a new `UnboundedPool`, which initially has zero `Resource`s.
57    ///
58    /// When a `Resource` is returned to the pool, it is not reset in any way.
59    #[inline]
60    #[must_use]
61    pub fn new_without_reset() -> Self {
62        Self::new(ResetNothing)
63    }
64}
65
66impl<Resource, Reset> UnboundedPool<Resource, Reset>
67where
68    Resource: Default,
69    Reset:    ResetResource<Resource> + Clone,
70{
71    /// Get a `Resource` from the pool, returning a default `Resource` if none were already
72    /// available in the pool.
73    #[inline]
74    #[must_use]
75    pub fn get_default(&self) -> PooledResource<Self, Resource> {
76        self.get(Resource::default)
77    }
78}
79
80impl<Resource, Reset: ResetResource<Resource> + Clone> UnboundedPool<Resource, Reset> {
81    /// Get a `Resource` from the pool.
82    #[must_use]
83    pub fn get<F>(&self, init_resource: F) -> PooledResource<Self, Resource>
84    where
85        F: FnOnce() -> Resource,
86    {
87        let raw_cell_contents: *mut (Vec<Resource>, usize) = self.pool.get();
88
89        // SAFETY:
90        // The only potential way for any borrow to the cell contents to overlap with
91        // this access is via user-provided callbacks. We drop the resulting reference
92        // before running the callback, so the access is unique.
93        let mut cell_contents: &mut (Vec<Resource>, usize) = unsafe { &mut *raw_cell_contents };
94
95        let resource = if let Some(resource) = cell_contents.0.pop() {
96            resource
97        } else {
98            #[expect(
99                dropping_references,
100                reason = "ensure there is no active borrow while the callback is run",
101            )]
102            drop(cell_contents);
103            let resource = init_resource();
104            // SAFETY:
105            // The only potential way for any borrow to the cell contents to overlap with
106            // this access was via the `init_resource` callback. The access is unique for the
107            // remainder of this function.
108            cell_contents = unsafe { &mut *raw_cell_contents };
109            resource
110        };
111
112        let pool = self.clone();
113        cell_contents.1 += 1;
114
115        // SAFETY:
116        // It's safe for the `PooledResource` to call `return_resource` however it
117        // likes, actually, and thus safe in the restricted guaranteed scenario.
118        unsafe { PooledResource::new(pool, resource) }
119    }
120
121    /// Get the total number of `Resource`s in this pool, whether available or in-use.
122    #[inline]
123    #[must_use]
124    pub fn pool_size(&self) -> usize {
125        let raw_cell_contents: *const (Vec<Resource>, usize) = self.pool.get().cast_const();
126
127        // SAFETY:
128        // We can ensure that this access is unique, implying that this is sound.
129        // See the note on `UnboundedPool.0`. This is one of only five functions that access
130        // the `UnsafeCell` contents, and none allow a reference to escape, and they do not call
131        // each other while the `UnsafeCell` contents are borrowed.
132        let cell_contents: &(Vec<Resource>, usize) = unsafe { &*raw_cell_contents };
133
134        cell_contents.0.len() + cell_contents.1
135    }
136
137    /// Get the number of `Resource`s in the pool which are not currently being used.
138    #[must_use]
139    pub fn available_resources(&self) -> usize {
140        let raw_cell_contents: *const (Vec<Resource>, usize) = self.pool.get().cast_const();
141
142        // SAFETY:
143        // We can ensure that this access is unique, implying that this is sound.
144        // See the note on `UnboundedPool.0`. This is one of only five functions that access
145        // the `UnsafeCell` contents, and none allow a reference to escape, and they do not call
146        // each other while the `UnsafeCell` contents are borrowed.
147        let cell_contents: &(Vec<Resource>, usize) = unsafe { &*raw_cell_contents };
148
149        cell_contents.0.len()
150    }
151}
152
153impl<Resource, Reset> SealedPool<Resource> for UnboundedPool<Resource, Reset>
154where
155    Reset: ResetResource<Resource> + Clone,
156{
157    type Returner = Self;
158
159    /// Used by [`PooledResource`] to return a `Resource` to a pool.
160    ///
161    /// # Safety
162    /// Must be called at most once in the `Drop` impl of a `PooledResource` constructed
163    /// via `PooledResource::new`, where `*returner` must be the `returner` value passed to
164    /// `PooledResource::new`.
165    unsafe fn return_resource(returner: &Self::Returner, mut resource: Resource) {
166        // We must run this before getting the `cell_contents` borrow.
167        returner.reset_resource.reset(&mut resource);
168
169        let raw_cell_contents: *mut (Vec<Resource>, usize) = returner.pool.get();
170
171        // SAFETY:
172        // We only need to ensure that this access is unique for this to be sound.
173        // See the note on `UnboundedPool.0`. This is one of only five functions that access
174        // the `UnsafeCell` contents, and none allow a reference to escape, and they do not call
175        // each other while the `UnsafeCell` contents are borrowed.
176        let cell_contents: &mut (Vec<Resource>, usize) = unsafe { &mut *raw_cell_contents };
177
178        cell_contents.1 -= 1;
179        cell_contents.0.push(resource);
180    }
181}
182
183impl<Resource, Reset> Default for UnboundedPool<Resource, Reset>
184where
185    Reset: ResetResource<Resource> + Clone + Default,
186{
187    #[inline]
188    fn default() -> Self {
189        Self::new(Reset::default())
190    }
191}
192
193impl<Resource, ResetResource: Clone> Clone for UnboundedPool<Resource, ResetResource> {
194    #[inline]
195    fn clone(&self) -> Self {
196        Self {
197            pool:           Rc::clone(&self.pool),
198            reset_resource: self.reset_resource.clone(),
199        }
200    }
201
202    #[inline]
203    fn clone_from(&mut self, source: &Self) {
204        self.pool.clone_from(&source.pool);
205        self.reset_resource.clone_from(&source.reset_resource);
206    }
207}
208
209#[cfg(feature = "clone-behavior")]
210impl<Resource, ResetResource, S> MirroredClone<S> for UnboundedPool<Resource, ResetResource>
211where
212    ResetResource: MirroredClone<S>,
213    S:             Speed,
214{
215    #[inline]
216    fn mirrored_clone(&self) -> Self {
217        Self {
218            pool:           Rc::clone(&self.pool),
219            reset_resource: self.reset_resource.mirrored_clone(),
220        }
221    }
222}
223
224
225#[cfg(all(test, not(tests_with_leaks)))]
226mod tests {
227    use std::array;
228    use super::*;
229
230
231    #[test]
232    fn zero_or_one_size() {
233        let pool: UnboundedPool<(), ResetNothing> = UnboundedPool::new_without_reset();
234        assert_eq!(pool.pool_size(), 0);
235        assert_eq!(pool.available_resources(), 0);
236
237        let unit = pool.get_default();
238        let _: &() = &unit;
239        assert_eq!(pool.pool_size(), 1);
240        assert_eq!(pool.available_resources(), 0);
241
242        drop(unit);
243        assert_eq!(pool.pool_size(), 1);
244        assert_eq!(pool.available_resources(), 1);
245    }
246
247    #[test]
248    fn init_and_reset() {
249        const SIZE: usize = 10;
250
251        let pool = UnboundedPool::new(|int: &mut usize| *int = 1);
252        let integers: [_; SIZE] = array::from_fn(|_| pool.get(|| 1_usize));
253
254        for (idx, mut integer) in integers.into_iter().enumerate() {
255            assert_eq!(*integer, 1);
256            *integer = idx;
257            assert_eq!(*integer, idx);
258        }
259
260        // They've been reset to 1, and the new constructor is not used.
261        let integers: [_; SIZE] = array::from_fn(|_| pool.get(|| 2_usize));
262        for integer in integers {
263            assert_eq!(*integer, 1);
264        }
265    }
266
267    #[test]
268    fn no_reset() {
269        const SIZE: usize = 10;
270
271        let pool = UnboundedPool::new(ResetNothing);
272        let integers: [_; SIZE] = array::from_fn(|_| pool.get(|| 1_usize));
273        for (idx, mut integer) in integers.into_iter().enumerate() {
274            assert_eq!(*integer, 1);
275            *integer = idx;
276            assert_eq!(*integer, idx);
277        }
278
279        // They haven't been reset, nor is the constructor used
280        // NOTE: users should not rely on the order.
281        let integers: [_; SIZE] = array::from_fn(|_| pool.get(|| 1_usize));
282        // This one is new.
283        assert_eq!(*pool.get(|| 11), 11);
284
285        for (idx, integer) in integers.into_iter().rev().enumerate() {
286            assert_eq!(*integer, idx);
287        }
288    }
289
290    /// This test has unspecified behavior that a user should not rely on.
291    #[test]
292    fn init_and_reset_disagreeing() {
293        let pool = UnboundedPool::new(|int: &mut i32| *int = 2);
294        let first_int = pool.get_default();
295        assert_eq!(*first_int, 0);
296        drop(first_int);
297        let mut reset_first_int = pool.get_default();
298        assert_eq!(*reset_first_int, 2);
299        let second_int = pool.get_default();
300        assert_eq!(*second_int, 0);
301        *reset_first_int = 3;
302        assert_eq!(*reset_first_int, 3);
303        drop(reset_first_int);
304        let re_reset_first_int = pool.get_default();
305        assert_eq!(*re_reset_first_int, 2);
306    }
307}