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}