rust_logic_graph/memory/
pool.rs1use parking_lot::Mutex;
4use std::collections::HashMap;
5use std::sync::Arc;
6
7use crate::core::Context;
8
9#[derive(Debug, Clone)]
11pub struct PoolConfig {
12 pub max_pooled: usize,
14 pub initial_capacity: usize,
16 pub track_stats: bool,
18}
19
20impl Default for PoolConfig {
21 fn default() -> Self {
22 Self {
23 max_pooled: 100,
24 initial_capacity: 16,
25 track_stats: true,
26 }
27 }
28}
29
30#[derive(Debug, Default, Clone)]
32pub struct PoolStats {
33 pub total_acquired: u64,
35 pub reused: u64,
37 pub created: u64,
39 pub current_pool_size: usize,
41 pub peak_pool_size: usize,
43}
44
45pub struct ContextPool {
47 pool: Arc<Mutex<Vec<Context>>>,
48 config: PoolConfig,
49 stats: Arc<Mutex<PoolStats>>,
50}
51
52impl ContextPool {
53 pub fn new() -> Self {
55 Self::with_config(PoolConfig::default())
56 }
57
58 pub fn with_config(config: PoolConfig) -> Self {
60 Self {
61 pool: Arc::new(Mutex::new(Vec::with_capacity(config.max_pooled))),
62 config,
63 stats: Arc::new(Mutex::new(PoolStats::default())),
64 }
65 }
66
67 pub fn acquire(&self) -> Context {
69 let mut pool = self.pool.lock();
70
71 if let Some(mut ctx) = pool.pop() {
72 ctx.data.clear();
74
75 if self.config.track_stats {
76 let mut stats = self.stats.lock();
77 stats.total_acquired += 1;
78 stats.reused += 1;
79 stats.current_pool_size = pool.len();
80 }
81
82 ctx
83 } else {
84 if self.config.track_stats {
86 let mut stats = self.stats.lock();
87 stats.total_acquired += 1;
88 stats.created += 1;
89 }
90
91 Context {
92 data: HashMap::with_capacity(self.config.initial_capacity),
93 }
94 }
95 }
96
97 pub fn release(&self, ctx: Context) {
99 let mut pool = self.pool.lock();
100
101 if pool.len() < self.config.max_pooled {
102 pool.push(ctx);
103
104 if self.config.track_stats {
105 let mut stats = self.stats.lock();
106 stats.current_pool_size = pool.len();
107 if pool.len() > stats.peak_pool_size {
108 stats.peak_pool_size = pool.len();
109 }
110 }
111 }
112 }
114
115 pub fn stats(&self) -> PoolStats {
117 self.stats.lock().clone()
118 }
119
120 pub fn clear(&self) {
122 let mut pool = self.pool.lock();
123 pool.clear();
124
125 if self.config.track_stats {
126 let mut stats = self.stats.lock();
127 stats.current_pool_size = 0;
128 }
129 }
130
131 pub fn size(&self) -> usize {
133 self.pool.lock().len()
134 }
135
136 pub fn reuse_rate(&self) -> f64 {
138 let stats = self.stats.lock();
139 if stats.total_acquired == 0 {
140 0.0
141 } else {
142 (stats.reused as f64 / stats.total_acquired as f64) * 100.0
143 }
144 }
145}
146
147impl Default for ContextPool {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153pub struct PooledContext {
155 context: Option<Context>,
156 pool: Arc<Mutex<Vec<Context>>>,
157 max_pooled: usize,
158}
159
160impl PooledContext {
161 pub fn new(context: Context, pool: Arc<Mutex<Vec<Context>>>, max_pooled: usize) -> Self {
163 Self {
164 context: Some(context),
165 pool,
166 max_pooled,
167 }
168 }
169
170 pub fn get_mut(&mut self) -> &mut Context {
172 self.context.as_mut().unwrap()
173 }
174
175 pub fn get(&self) -> &Context {
177 self.context.as_ref().unwrap()
178 }
179}
180
181impl Drop for PooledContext {
182 fn drop(&mut self) {
183 if let Some(ctx) = self.context.take() {
184 let mut pool = self.pool.lock();
185 if pool.len() < self.max_pooled {
186 pool.push(ctx);
187 }
188 }
189 }
190}
191
192impl std::ops::Deref for PooledContext {
193 type Target = Context;
194
195 fn deref(&self) -> &Self::Target {
196 self.context.as_ref().unwrap()
197 }
198}
199
200impl std::ops::DerefMut for PooledContext {
201 fn deref_mut(&mut self) -> &mut Self::Target {
202 self.context.as_mut().unwrap()
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_pool_acquire_and_release() {
212 let pool = ContextPool::new();
213
214 let ctx1 = pool.acquire();
215 assert_eq!(pool.size(), 0);
216
217 pool.release(ctx1);
218 assert_eq!(pool.size(), 1);
219
220 let ctx2 = pool.acquire();
221 assert_eq!(pool.size(), 0);
222
223 pool.release(ctx2);
224 assert_eq!(pool.size(), 1);
225 }
226
227 #[test]
228 fn test_pool_max_size() {
229 let config = PoolConfig {
230 max_pooled: 2,
231 initial_capacity: 16,
232 track_stats: true,
233 };
234 let pool = ContextPool::with_config(config);
235
236 let ctx1 = Context::new();
238 let ctx2 = Context::new();
239 let ctx3 = Context::new();
240
241 pool.release(ctx1);
242 pool.release(ctx2);
243 pool.release(ctx3);
244
245 assert_eq!(pool.size(), 2);
247 }
248
249 #[test]
250 fn test_pool_stats() {
251 let pool = ContextPool::new();
252
253 let _ctx1 = pool.acquire();
254 let _ctx2 = pool.acquire();
255
256 let stats = pool.stats();
257 assert_eq!(stats.total_acquired, 2);
258 assert_eq!(stats.created, 2);
259 assert_eq!(stats.reused, 0);
260
261 pool.release(_ctx1);
262 pool.release(_ctx2);
263
264 let _ctx3 = pool.acquire();
265 let stats = pool.stats();
266 assert_eq!(stats.total_acquired, 3);
267 assert_eq!(stats.reused, 1);
268 }
269
270 #[test]
271 fn test_reuse_rate() {
272 let pool = ContextPool::new();
273
274 let ctx1 = pool.acquire();
276 let ctx2 = pool.acquire();
277
278 pool.release(ctx1);
280 pool.release(ctx2);
281
282 let _ctx3 = pool.acquire();
284 let _ctx4 = pool.acquire();
285
286 let rate = pool.reuse_rate();
288 assert_eq!(rate, 50.0);
289 }
290
291 #[test]
292 fn test_pooled_context_guard() {
293 let pool_vec = Arc::new(Mutex::new(Vec::new()));
294 let ctx = Context::new();
295
296 {
297 let _guard = PooledContext::new(ctx, pool_vec.clone(), 10);
298 assert_eq!(pool_vec.lock().len(), 0);
299 }
300
301 assert_eq!(pool_vec.lock().len(), 1);
303 }
304}