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::iter;
8use std::{cell::UnsafeCell, rc::Rc};
9
10#[cfg(feature = "clone-behavior")]
11use clone_behavior::{MirroredClone, Speed};
12
13use crate::{
14 other_utils::{ResetNothing, ResetResource, ResourcePoolEmpty},
15 pooled_resource::{PooledResource, SealedPool},
16};
17
18
19#[derive(Debug)]
21pub struct BoundedPool<Resource, Reset> {
22 pool: Rc<[UnsafeCell<Option<Resource>>]>,
28 reset_resource: Reset,
29}
30
31impl<Resource, Reset> BoundedPool<Resource, Reset> {
32 #[inline]
38 #[must_use]
39 pub fn new<F>(pool_size: usize, mut init_resource: F, reset_resource: Reset) -> Self
40 where
41 F: FnMut() -> Resource,
42 Reset: ResetResource<Resource> + Clone,
43 {
44 let mut pool = Vec::new();
45 pool.reserve_exact(pool_size);
46 pool.extend(
47 iter::repeat_with(|| UnsafeCell::new(Some(init_resource()))).take(pool_size),
48 );
49 Self {
50 pool: Rc::from(pool),
51 reset_resource,
52 }
53 }
54}
55
56impl<Resource: Default, Reset> BoundedPool<Resource, Reset> {
57 #[inline]
63 #[must_use]
64 pub fn new_default(pool_size: usize, reset_resource: Reset) -> Self
65 where
66 Reset: ResetResource<Resource> + Clone,
67 {
68 Self::new(pool_size, Resource::default, reset_resource)
69 }
70}
71
72impl<Resource> BoundedPool<Resource, ResetNothing> {
73 #[inline]
79 #[must_use]
80 pub fn new_without_reset<F>(pool_size: usize, init_resource: F) -> Self
81 where
82 F: FnMut() -> Resource,
83 {
84 Self::new(pool_size, init_resource, ResetNothing)
85 }
86}
87
88impl<Resource: Default> BoundedPool<Resource, ResetNothing> {
89 #[inline]
95 #[must_use]
96 pub fn new_default_without_reset(pool_size: usize) -> Self {
97 Self::new(pool_size, Resource::default, ResetNothing)
98 }
99}
100
101impl<Resource, Reset: ResetResource<Resource> + Clone> BoundedPool<Resource, Reset> {
102 pub fn try_get(&self) -> Result<PooledResource<Self, Resource>, ResourcePoolEmpty> {
104 self.pool.iter()
105 .enumerate()
106 .find_map(|(slot_idx, slot)| {
107 let slot: *mut Option<Resource> = slot.get();
108 let slot: &mut Option<Resource> = unsafe { &mut *slot };
114
115 slot.take().map(|resource| {
116 let pool = self.clone();
117 unsafe { PooledResource::new((pool, slot_idx), resource) }
121 })
122 })
123 .ok_or(ResourcePoolEmpty)
124 }
125
126 #[must_use]
132 pub fn get(&self) -> PooledResource<Self, Resource> {
133 #[expect(
134 clippy::expect_used,
135 reason = "this call would never succeed if it fails once. Also, this is documented.",
136 )]
137 self.try_get().expect(
138 "A single-threaded BoundedPool ran out of `Resource`s and had `get()` called on \
139 it, which can never succeed",
140 )
141 }
142
143 #[inline]
145 #[must_use]
146 pub fn pool_size(&self) -> usize {
147 self.pool.len()
148 }
149
150 #[must_use]
152 pub fn available_resources(&self) -> usize {
153 self.pool.iter()
154 .map(|slot| {
155 let slot: *const Option<Resource> = slot.get().cast_const();
156 let slot: &Option<Resource> = unsafe { & *slot };
162
163 if slot.is_some() {
164 1_usize
166 } else {
167 0_usize
169 }
170 })
171 .sum()
172 }
173}
174
175impl<Resource, Reset> SealedPool<Resource> for BoundedPool<Resource, Reset>
176where
177 Reset: ResetResource<Resource> + Clone,
178{
179 type Returner = (Self, usize);
180
181 unsafe fn return_resource(returner: &Self::Returner, mut resource: Resource) {
188 let this = &returner.0;
189 let slot_idx = returner.1;
190
191 this.reset_resource.reset(&mut resource);
193
194 #[expect(
195 clippy::indexing_slicing,
196 reason = "the pool slice's length is never changed after construction, and `slot_idx` \
197 was a valid index into the slice when the `PooledResource` was made",
198 )]
199 let slot_contents: *mut Option<Resource> = this.pool[slot_idx].get();
200
201 let slot_contents: &mut Option<Resource> = unsafe { &mut *slot_contents };
207
208 *slot_contents = Some(resource);
213 }
214}
215
216impl<Resource, ResetResource: Clone> Clone for BoundedPool<Resource, ResetResource> {
217 #[inline]
218 fn clone(&self) -> Self {
219 Self {
220 pool: Rc::clone(&self.pool),
221 reset_resource: self.reset_resource.clone(),
222 }
223 }
224
225 #[inline]
226 fn clone_from(&mut self, source: &Self) {
227 self.pool.clone_from(&source.pool);
228 self.reset_resource.clone_from(&source.reset_resource);
229 }
230}
231
232#[cfg(feature = "clone-behavior")]
233impl<Resource, ResetResource, S> MirroredClone<S> for BoundedPool<Resource, ResetResource>
234where
235 ResetResource: MirroredClone<S>,
236 S: Speed,
237{
238 #[inline]
239 fn mirrored_clone(&self) -> Self {
240 Self {
241 pool: Rc::clone(&self.pool),
242 reset_resource: self.reset_resource.mirrored_clone(),
243 }
244 }
245}
246
247
248#[cfg(all(test, not(tests_with_leaks)))]
249mod tests {
250 use std::array;
251 use super::*;
252
253
254 #[test]
255 fn zero_capacity() {
256 let pool: BoundedPool<(), ResetNothing> = BoundedPool::new_default_without_reset(0);
257 assert_eq!(pool.pool_size(), 0);
258 assert_eq!(pool.available_resources(), 0);
259 assert!(pool.try_get().is_err());
260 }
261
262 #[test]
263 #[should_panic]
264 fn zero_capacity_fail() {
265 let pool: BoundedPool<(), ResetNothing> = BoundedPool::new_default_without_reset(0);
266 let unreachable = pool.get();
267 let _: &() = &*unreachable;
268 }
269
270 #[test]
271 fn one_capacity() {
272 let pool: BoundedPool<(), ResetNothing> = BoundedPool::new_default_without_reset(1);
273 let unit = pool.get();
274 assert_eq!(pool.pool_size(), 1);
275 assert_eq!(pool.available_resources(), 0);
276 assert!(pool.try_get().is_err());
277 drop(unit);
278 assert_eq!(pool.available_resources(), 1);
279 }
280
281 #[test]
282 #[should_panic]
283 fn one_capacity_fail() {
284 let pool: BoundedPool<(), ResetNothing> = BoundedPool::new_default_without_reset(1);
285 let _unit = pool.get();
286 assert_eq!(pool.pool_size(), 1);
287 assert_eq!(pool.available_resources(), 0);
288 let _unreachable = pool.get();
289 }
290
291 #[test]
292 fn init_and_reset() {
293 const CAPACITY: usize = 10;
294
295 let pool = BoundedPool::new(CAPACITY, || 1_usize, |int: &mut usize| *int = 1);
296 let integers: [_; CAPACITY] = array::from_fn(|_| pool.get());
297 for (idx, mut integer) in integers.into_iter().enumerate() {
298 assert_eq!(*integer, 1);
299 *integer = idx;
300 assert_eq!(*integer, idx);
301 }
302
303 let integers: [_; CAPACITY] = array::from_fn(|_| pool.get());
305 for integer in integers {
306 assert_eq!(*integer, 1);
307 }
308 }
309
310 #[test]
311 fn no_reset() {
312 const CAPACITY: usize = 10;
313
314 let pool = BoundedPool::new(CAPACITY, || 1_usize, ResetNothing);
315 let integers: [_; CAPACITY] = array::from_fn(|_| pool.get());
316 for (idx, mut integer) in integers.into_iter().enumerate() {
317 assert_eq!(*integer, 1);
318 *integer = idx;
319 assert_eq!(*integer, idx);
320 }
321
322 let integers: [_; CAPACITY] = array::from_fn(|_| pool.get());
324 for (idx, integer) in integers.into_iter().enumerate() {
325 assert_eq!(*integer, idx);
326 }
327 }
328
329 #[test]
331 fn init_and_reset_disagreeing() {
332 let pool = BoundedPool::new(2, || 1, |int: &mut i32| *int = 2);
333 let first_int = pool.get();
334 assert_eq!(*first_int, 1);
335 drop(first_int);
336 let mut reset_first_int = pool.get();
337 assert_eq!(*reset_first_int, 2);
338 let second_int = pool.get();
339 assert_eq!(*second_int, 1);
340 *reset_first_int = 3;
341 assert_eq!(*reset_first_int, 3);
342 drop(reset_first_int);
343 let re_reset_first_int = pool.get();
344 assert_eq!(*re_reset_first_int, 2);
345 }
346}