dynamic_pool/
pool.rs

1use crossbeam_queue::ArrayQueue;
2use std::fmt::{Debug, Formatter};
3use std::ops::{Deref, DerefMut};
4use std::sync::{Arc, Weak};
5
6use crate::DynamicReset;
7
8/// a lock-free, thread-safe, dynamically-sized object pool.
9///
10/// this pool begins with an initial capacity and will continue creating new objects on request when none are available.
11/// pooled objects are returned to the pool on destruction (with an extra provision to optionally "reset" the state of
12/// an object for re-use).
13///
14/// if, during an attempted return, a pool already has `maximum_capacity` objects in the pool, the pool will throw away
15/// that object.
16#[derive(Debug)]
17pub struct DynamicPool<T: DynamicReset> {
18    data: Arc<PoolData<T>>,
19}
20
21impl<T: DynamicReset> DynamicPool<T> {
22    /// creates a new `DynamicPool<T>`. this pool will create `initial_capacity` objects, and retain up to
23    /// `maximum_capacity` objects.
24    ///
25    /// # panics.
26    ///
27    /// panics if `initial_capacity > maximum_capacity`.
28    pub fn new<F: Fn() -> T + Sync + Send + 'static>(
29        initial_capacity: usize,
30        maximum_capacity: usize,
31        create: F,
32    ) -> DynamicPool<T> {
33        assert![initial_capacity <= maximum_capacity];
34
35        let items = ArrayQueue::new(maximum_capacity);
36
37        for x in (0..initial_capacity).map(|_| create()) {
38            items
39                .push(x)
40                .expect("invariant: items.len() always less than initial_capacity.");
41        }
42
43        let data = PoolData {
44            items,
45            create: Box::new(create),
46        };
47        let data = Arc::new(data);
48
49        DynamicPool { data }
50    }
51
52    /// takes an item from the pool, creating one if none are available.
53    pub fn take(&self) -> DynamicPoolItem<T> {
54        let object = self
55            .data
56            .items
57            .pop()
58            .unwrap_or_else(|_| (self.data.create)());
59
60        DynamicPoolItem {
61            data: Arc::downgrade(&self.data),
62            object: Some(object),
63        }
64    }
65
66    /// attempts to take an item from the pool, returning `none` if none is available. will never allocate.
67    pub fn try_take(&self) -> Option<DynamicPoolItem<T>> {
68        let object = self.data.items.pop().ok()?;
69        let data = Arc::downgrade(&self.data);
70
71        Some(DynamicPoolItem {
72            data,
73            object: Some(object),
74        })
75    }
76
77    /// returns the number of free objects in the pool.
78    #[inline]
79    pub fn available(&self) -> usize {
80        self.data.items.len()
81    }
82
83    /// returns the number of objects currently in use. does not include objects that have been detached.
84    #[inline]
85    pub fn used(&self) -> usize {
86        Arc::weak_count(&self.data)
87    }
88
89    #[inline]
90    pub fn capacity(&self) -> usize {
91        self.data.items.capacity()
92    }
93}
94
95impl<T: DynamicReset> Clone for DynamicPool<T> {
96    fn clone(&self) -> Self {
97        Self {
98            data: self.data.clone(),
99        }
100    }
101}
102
103// data shared by a `DynamicPool`.
104struct PoolData<T> {
105    items: ArrayQueue<T>,
106    create: Box<dyn Fn() -> T + Sync + Send + 'static>,
107}
108
109impl<T: DynamicReset + Debug> Debug for PoolData<T> {
110    fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> {
111        formatter
112            .debug_struct("PoolData")
113            .field("items", &self.items)
114            .field("create", &"Box<dyn Fn() -> T>")
115            .finish()
116    }
117}
118
119/// an object, checked out from a dynamic pool object.
120#[derive(Debug)]
121pub struct DynamicPoolItem<T: DynamicReset> {
122    data: Weak<PoolData<T>>,
123    object: Option<T>,
124}
125
126impl<T: DynamicReset> DynamicPoolItem<T> {
127    /// detaches this instance from the pool, returns T.
128    pub fn detach(mut self) -> T {
129        self.object
130            .take()
131            .expect("invariant: object is always `some`.")
132    }
133}
134
135impl<T: DynamicReset> AsRef<T> for DynamicPoolItem<T> {
136    fn as_ref(&self) -> &T {
137        self.object
138            .as_ref()
139            .expect("invariant: object is always `some`.")
140    }
141}
142
143impl<T: DynamicReset> Deref for DynamicPoolItem<T> {
144    type Target = T;
145
146    fn deref(&self) -> &T {
147        self.object
148            .as_ref()
149            .expect("invariant: object is always `some`.")
150    }
151}
152
153impl<T: DynamicReset> DerefMut for DynamicPoolItem<T> {
154    fn deref_mut(&mut self) -> &mut T {
155        self.object
156            .as_mut()
157            .expect("invariant: object is always `some`.")
158    }
159}
160
161impl<T: DynamicReset> Drop for DynamicPoolItem<T> {
162    fn drop(&mut self) {
163        if let Some(mut object) = self.object.take() {
164            object.reset();
165            if let Some(pool) = self.data.upgrade() {
166                pool.items.push(object).ok();
167            }
168        }
169    }
170}