Skip to main content

euv_engine/pool/
impl.rs

1use super::*;
2
3/// Implements construction, checkout, return, and introspection for
4/// [`ObjectPool`].
5///
6/// Checkout is LIFO on the free list (see [`ObjectPool`] for the rationale),
7/// and every successful `acquire` pairs with exactly one `release`, so the
8/// active count returns to zero once all outstanding values are back.
9impl<T> ObjectPool<T> {
10    /// Constructs a pool holding `initial` ready-to-use values.
11    ///
12    /// The seeded values land on the free list, so a pool built this way
13    /// satisfies its first `initial.len()` acquires without a factory.
14    ///
15    /// # Arguments
16    ///
17    /// - `Vec<T>` - The values to seed the free list with.
18    ///
19    /// # Returns
20    ///
21    /// - `ObjectPool<T>` - The new pool.
22    pub fn new(initial: Vec<T>) -> ObjectPool<T> {
23        ObjectPool {
24            active: 0,
25            free: initial,
26        }
27    }
28
29    /// Constructs an empty pool.
30    ///
31    /// The free list is reserved to [`POOL_DEFAULT_PREWARM`] so a default
32    /// pool starts with a pre-sized free list; a prewarm size of zero
33    /// reserves nothing and the first release grows the list.
34    ///
35    /// # Returns
36    ///
37    /// - `ObjectPool<T>` - The new pool.
38    pub fn empty() -> ObjectPool<T> {
39        ObjectPool {
40            active: 0,
41            free: Vec::with_capacity(POOL_DEFAULT_PREWARM),
42        }
43    }
44
45    /// Returns the number of values currently checked out of the pool.
46    ///
47    /// Named `get_active` rather than `active` so it does not collide with
48    /// the field of the same name.
49    ///
50    /// # Returns
51    ///
52    /// - `usize` - The active count.
53    pub fn get_active(&self) -> usize {
54        self.active
55    }
56
57    /// Sets the number of tracked active values.
58    ///
59    /// Only for pool owners that track checkouts out of band; `acquire` and
60    /// `release` maintain the count themselves.
61    ///
62    /// # Arguments
63    ///
64    /// - `usize` - The new active count.
65    pub fn set_active(&mut self, active: usize) {
66        self.active = active;
67    }
68
69    /// Returns the values the pool owns and can hand out.
70    ///
71    /// # Returns
72    ///
73    /// - `&Vec<T>` - The free list, most-recently-released first.
74    pub fn get_free(&self) -> &Vec<T> {
75        &self.free
76    }
77
78    /// Returns a mutable reference to the free list.
79    ///
80    /// # Returns
81    ///
82    /// - `&mut Vec<T>` - The free list, for bulk seeding before first use.
83    pub fn get_mut_free(&mut self) -> &mut Vec<T> {
84        &mut self.free
85    }
86
87    /// Returns the number of values ready to be handed out.
88    ///
89    /// # Returns
90    ///
91    /// - `usize` - The free list length.
92    pub fn available(&self) -> usize {
93        self.get_free().len()
94    }
95
96    /// Returns the total number of values the pool tracks, active plus free.
97    ///
98    /// This is the pool's high-water mark: it grows only when a factory
99    /// builds a value the pool could not serve, and never shrinks on a
100    /// release.
101    ///
102    /// # Returns
103    ///
104    /// - `usize` - The tracked value count.
105    pub fn len(&self) -> usize {
106        self.get_active() + self.available()
107    }
108
109    /// Returns whether the pool tracks no values at all.
110    ///
111    /// # Returns
112    ///
113    /// - `bool` - True when both the active count and the free list are empty.
114    pub fn is_empty(&self) -> bool {
115        self.len() == 0
116    }
117
118    /// Checks a pooled value out, or reports that the free list is empty.
119    ///
120    /// This is the factory-free form: it never builds a new value, so it
121    /// reports `None` on a miss. Use [`Self::acquire_with`] when the pool
122    /// should grow on demand.
123    ///
124    /// # Returns
125    ///
126    /// - `Option<T>` - A recycled value, or `None` if none was available.
127    pub fn acquire(&mut self) -> Option<T> {
128        let value: Option<T> = self.get_mut_free().pop();
129        if value.is_some() {
130            let active: usize = self.get_active() + 1;
131            self.set_active(active);
132        }
133        value
134    }
135
136    /// Checks a pooled value out, calling `make` only when the free list is
137    /// empty.
138    ///
139    /// A recycled value is returned verbatim; the factory runs at most once
140    /// per acquire and its result is handed to the caller, so the pool never
141    /// duplicates the value's allocation.
142    ///
143    /// # Arguments
144    ///
145    /// - `F` - Factory building a fresh value on a miss.
146    ///
147    /// # Returns
148    ///
149    /// - `T` - The recycled value, or a freshly built one.
150    pub fn acquire_with<F>(&mut self, mut make: F) -> T
151    where
152        F: FnMut() -> T,
153    {
154        // Phase 1: serve from the free list when a value is parked there.
155        if self.available() > 0
156            && let Some(value) = self.acquire()
157        {
158            return value;
159        }
160        // Phase 2: grow on demand; a fresh value is still an outstanding checkout.
161        let value: T = make();
162        let active: usize = self.get_active() + 1;
163        self.set_active(active);
164        value
165    }
166
167    /// Returns a value to the free list without destroying it.
168    ///
169    /// The value is moved onto the free list, so the allocation behind it
170    /// survives for the next acquire. The active count only decrements while
171    /// a checkout is outstanding, which keeps the count honest even when a
172    /// caller returns a value it never acquired.
173    ///
174    /// # Arguments
175    ///
176    /// - `T` - The value to make available again.
177    pub fn release(&mut self, value: T) {
178        let active: usize = self.get_active().saturating_sub(1);
179        self.set_active(active);
180        self.get_mut_free().push(value);
181    }
182
183    /// Builds `count` values up front and returns how many are available.
184    ///
185    /// Prewarming moves the factory cost off the first frames of a spawn
186    /// loop; the pool then serves that many acquires without calling `make`
187    /// again.
188    ///
189    /// # Arguments
190    ///
191    /// - `usize` - How many values to build.
192    /// - `F` - Factory building each new value.
193    ///
194    /// # Returns
195    ///
196    /// - `usize` - The number of values now available on the free list.
197    pub fn prewarm<F>(&mut self, count: usize, mut make: F) -> usize
198    where
199        F: FnMut() -> T,
200    {
201        for _ in 0..count {
202            let value: T = make();
203            self.get_mut_free().push(value);
204        }
205        self.available()
206    }
207
208    /// Discards every free value and resets the active count.
209    ///
210    /// # Returns
211    ///
212    /// - `usize` - How many values were discarded from the free list.
213    pub fn clear(&mut self) -> usize {
214        let discarded: usize = self.available();
215        self.get_mut_free().clear();
216        self.set_active(0);
217        discarded
218    }
219}
220
221/// Implements [`Default`] for [`ObjectPool`] as a new empty pool.
222impl<T> Default for ObjectPool<T> {
223    /// Constructs a default [`ObjectPool`] value.
224    ///
225    /// # Returns
226    ///
227    /// - `ObjectPool<T>` - A default-constructed instance with the documented initial state.
228    fn default() -> ObjectPool<T> {
229        ObjectPool::empty()
230    }
231}
232
233/// Implements [`Debug`] for [`ObjectPool`] showing counts rather than values.
234///
235/// `T` is deliberately not required to implement [`Debug`], so a pool of
236/// closure or handle types still prints usefully.
237impl<T> Debug for ObjectPool<T> {
238    /// Formats the [`ObjectPool`] via the supplied formatter.
239    ///
240    /// # Arguments
241    ///
242    /// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
243    ///
244    /// # Returns
245    ///
246    /// - `fmt::Result` - Result of the formatting operation.
247    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
248        formatter
249            .debug_struct(POOL_DEBUG_NAME)
250            .field(POOL_FIELD_ACTIVE, &self.active)
251            .field(POOL_FIELD_AVAILABLE, &self.free.len())
252            .finish()
253    }
254}