Skip to main content

reifydb_core/value/column/pool/
guard.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4//! Automatic pool management through RAII guards
5//!
6//! This module provides the `PooledGuard` type that automatically returns
7//! containers to their respective pools when dropped, implementing the RAII
8//! pattern for memory pool management.
9
10use std::{
11	fmt,
12	fmt::{Debug, Formatter},
13	ops::{Deref, DerefMut},
14	rc::{Rc, Weak},
15};
16
17use reifydb_type::value::{
18	container::{
19		blob::BlobContainer, bool::BoolContainer, number::NumberContainer, row::RowNumberContainer,
20		temporal::TemporalContainer, utf8::Utf8Container, uuid::UuidContainer,
21	},
22	date::Date,
23	datetime::DateTime,
24	duration::Duration,
25	time::Time,
26	uuid::{Uuid4, Uuid7},
27};
28
29use super::{Pools, PoolsInner, allocator::PoolAllocator};
30
31/// Trait for containers that can be released back to a pool
32pub trait Releasable: Clone + Debug {
33	fn release_to_pool(self, pools: &Pools);
34}
35
36// Implement Releasable for all container types
37impl Releasable for BoolContainer {
38	fn release_to_pool(self, pools: &Pools) {
39		pools.bool_pool().release(self);
40	}
41}
42
43impl Releasable for Utf8Container {
44	fn release_to_pool(self, pools: &Pools) {
45		pools.string_pool().release(self);
46	}
47}
48
49impl Releasable for BlobContainer {
50	fn release_to_pool(self, pools: &Pools) {
51		pools.blob_pool().release(self);
52	}
53}
54
55impl Releasable for RowNumberContainer {
56	fn release_to_pool(self, pools: &Pools) {
57		pools.row_number_pool().release(self);
58	}
59}
60
61impl Releasable for NumberContainer<i32> {
62	fn release_to_pool(self, pools: &Pools) {
63		pools.i32_pool().release(self);
64	}
65}
66
67impl Releasable for NumberContainer<i64> {
68	fn release_to_pool(self, pools: &Pools) {
69		pools.i64_pool().release(self);
70	}
71}
72
73impl Releasable for NumberContainer<f32> {
74	fn release_to_pool(self, pools: &Pools) {
75		pools.f32_pool().release(self);
76	}
77}
78
79impl Releasable for NumberContainer<f64> {
80	fn release_to_pool(self, pools: &Pools) {
81		pools.f64_pool().release(self);
82	}
83}
84
85impl Releasable for NumberContainer<i8> {
86	fn release_to_pool(self, pools: &Pools) {
87		pools.i8_pool().release(self);
88	}
89}
90
91impl Releasable for NumberContainer<i16> {
92	fn release_to_pool(self, pools: &Pools) {
93		pools.i16_pool().release(self);
94	}
95}
96
97impl Releasable for NumberContainer<i128> {
98	fn release_to_pool(self, pools: &Pools) {
99		pools.i128_pool().release(self);
100	}
101}
102
103impl Releasable for NumberContainer<u8> {
104	fn release_to_pool(self, pools: &Pools) {
105		pools.u8_pool().release(self);
106	}
107}
108
109impl Releasable for NumberContainer<u16> {
110	fn release_to_pool(self, pools: &Pools) {
111		pools.u16_pool().release(self);
112	}
113}
114
115impl Releasable for NumberContainer<u32> {
116	fn release_to_pool(self, pools: &Pools) {
117		pools.u32_pool().release(self);
118	}
119}
120
121impl Releasable for NumberContainer<u64> {
122	fn release_to_pool(self, pools: &Pools) {
123		pools.u64_pool().release(self);
124	}
125}
126
127impl Releasable for NumberContainer<u128> {
128	fn release_to_pool(self, pools: &Pools) {
129		pools.u128_pool().release(self);
130	}
131}
132
133impl Releasable for TemporalContainer<Date> {
134	fn release_to_pool(self, pools: &Pools) {
135		pools.date_pool().release(self);
136	}
137}
138
139impl Releasable for TemporalContainer<DateTime> {
140	fn release_to_pool(self, pools: &Pools) {
141		pools.datetime_pool().release(self);
142	}
143}
144
145impl Releasable for TemporalContainer<Time> {
146	fn release_to_pool(self, pools: &Pools) {
147		pools.time_pool().release(self);
148	}
149}
150
151impl Releasable for TemporalContainer<Duration> {
152	fn release_to_pool(self, pools: &Pools) {
153		pools.duration_pool().release(self);
154	}
155}
156
157impl Releasable for UuidContainer<Uuid4> {
158	fn release_to_pool(self, pools: &Pools) {
159		pools.uuid4_pool().release(self);
160	}
161}
162
163impl Releasable for UuidContainer<Uuid7> {
164	fn release_to_pool(self, pools: &Pools) {
165		pools.uuid7_pool().release(self);
166	}
167}
168
169/// A guard that automatically returns a container to its pool when dropped
170///
171/// This implements the RAII pattern for container pooling - when the guard goes
172/// out of scope, it automatically releases the contained value back to the pool
173/// for reuse.
174pub struct PooledGuard<T: Releasable> {
175	container: Option<T>,
176	pools: Weak<PoolsInner>,
177}
178
179impl<T: Releasable> Debug for PooledGuard<T> {
180	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
181		Debug::fmt(&self.container, f)
182	}
183}
184
185impl<T: Releasable> PooledGuard<T> {
186	/// Create a new pooled guard with the given container and pool
187	/// reference
188	fn new(container: T, pools: Pools) -> Self {
189		Self {
190			container: Some(container),
191			pools: Rc::downgrade(&pools.0),
192		}
193	}
194
195	/// Clone the container and release the guard back to the pool
196	///
197	/// This returns a clone of the container while automatically releasing
198	/// the original back to the pool for reuse.
199	pub fn to_owned(mut self) -> T {
200		let container = self.container.take().expect("Container already taken");
201		let cloned_container = container.clone();
202
203		// Release the original container back to the pool
204		if let Some(pools_inner) = self.pools.upgrade() {
205			container.release_to_pool(&Pools(pools_inner));
206		}
207
208		cloned_container
209	}
210
211	/// Check if the container is still held by this guard
212	pub fn is_empty(&self) -> bool {
213		self.container.is_none()
214	}
215}
216
217// Container-specific constructors
218impl PooledGuard<BoolContainer> {
219	/// Create a new pooled BoolContainer with the specified capacity
220	pub fn new_bool(pools: Pools, capacity: usize) -> Self {
221		let container = pools.bool_pool().acquire(capacity);
222		Self::new(container, pools)
223	}
224}
225
226impl PooledGuard<Utf8Container> {
227	/// Create a new pooled StringContainer with the specified capacity
228	pub fn new_string(pools: Pools, capacity: usize) -> Self {
229		let container = pools.string_pool().acquire(capacity);
230		Self::new(container, pools)
231	}
232}
233
234impl PooledGuard<BlobContainer> {
235	/// Create a new pooled BlobContainer with the specified capacity
236	pub fn new_blob(pools: Pools, capacity: usize) -> Self {
237		let container = pools.blob_pool().acquire(capacity);
238		Self::new(container, pools)
239	}
240}
241
242impl PooledGuard<RowNumberContainer> {
243	/// Create a new pooled RowNumberContainer with the specified capacity
244	pub fn new_row_number(pools: Pools, capacity: usize) -> Self {
245		let container = pools.row_number_pool().acquire(capacity);
246		Self::new(container, pools)
247	}
248}
249
250// Numeric container constructors
251impl PooledGuard<NumberContainer<i32>> {
252	/// Create a new pooled NumberContainer<i32> with the specified capacity
253	pub fn new_i32(pools: Pools, capacity: usize) -> Self {
254		let container = pools.i32_pool().acquire(capacity);
255		Self::new(container, pools)
256	}
257}
258
259impl PooledGuard<NumberContainer<i64>> {
260	/// Create a new pooled NumberContainer<i64> with the specified capacity
261	pub fn new_i64(pools: Pools, capacity: usize) -> Self {
262		let container = pools.i64_pool().acquire(capacity);
263		Self::new(container, pools)
264	}
265}
266
267impl PooledGuard<NumberContainer<f32>> {
268	/// Create a new pooled NumberContainer<f32> with the specified capacity
269	pub fn new_f32(pools: Pools, capacity: usize) -> Self {
270		let container = pools.f32_pool().acquire(capacity);
271		Self::new(container, pools)
272	}
273}
274
275impl PooledGuard<NumberContainer<f64>> {
276	/// Create a new pooled NumberContainer<f64> with the specified capacity
277	pub fn new_f64(pools: Pools, capacity: usize) -> Self {
278		let container = pools.f64_pool().acquire(capacity);
279		Self::new(container, pools)
280	}
281}
282
283impl PooledGuard<NumberContainer<i8>> {
284	/// Create a new pooled NumberContainer<i8> with the specified capacity
285	pub fn new_i8(pools: Pools, capacity: usize) -> Self {
286		let container = pools.i8_pool().acquire(capacity);
287		Self::new(container, pools)
288	}
289}
290
291impl PooledGuard<NumberContainer<i16>> {
292	/// Create a new pooled NumberContainer<i16> with the specified capacity
293	pub fn new_i16(pools: Pools, capacity: usize) -> Self {
294		let container = pools.i16_pool().acquire(capacity);
295		Self::new(container, pools)
296	}
297}
298
299impl PooledGuard<NumberContainer<i128>> {
300	/// Create a new pooled NumberContainer<i128> with the specified
301	/// capacity
302	pub fn new_i128(pools: Pools, capacity: usize) -> Self {
303		let container = pools.i128_pool().acquire(capacity);
304		Self::new(container, pools)
305	}
306}
307
308impl PooledGuard<NumberContainer<u8>> {
309	/// Create a new pooled NumberContainer<u8> with the specified capacity
310	pub fn new_u8(pools: Pools, capacity: usize) -> Self {
311		let container = pools.u8_pool().acquire(capacity);
312		Self::new(container, pools)
313	}
314}
315
316impl PooledGuard<NumberContainer<u16>> {
317	/// Create a new pooled NumberContainer<u16> with the specified capacity
318	pub fn new_u16(pools: Pools, capacity: usize) -> Self {
319		let container = pools.u16_pool().acquire(capacity);
320		Self::new(container, pools)
321	}
322}
323
324impl PooledGuard<NumberContainer<u32>> {
325	/// Create a new pooled NumberContainer<u32> with the specified capacity
326	pub fn new_u32(pools: Pools, capacity: usize) -> Self {
327		let container = pools.u32_pool().acquire(capacity);
328		Self::new(container, pools)
329	}
330}
331
332impl PooledGuard<NumberContainer<u64>> {
333	/// Create a new pooled NumberContainer<u64> with the specified capacity
334	pub fn new_u64(pools: Pools, capacity: usize) -> Self {
335		let container = pools.u64_pool().acquire(capacity);
336		Self::new(container, pools)
337	}
338}
339
340impl PooledGuard<NumberContainer<u128>> {
341	/// Create a new pooled NumberContainer<u128> with the specified
342	/// capacity
343	pub fn new_u128(pools: Pools, capacity: usize) -> Self {
344		let container = pools.u128_pool().acquire(capacity);
345		Self::new(container, pools)
346	}
347}
348
349// Temporal container constructors
350impl PooledGuard<TemporalContainer<Date>> {
351	/// Create a new pooled TemporalContainer<Date> with the specified
352	/// capacity
353	pub fn new_date(pools: Pools, capacity: usize) -> Self {
354		let container = pools.date_pool().acquire(capacity);
355		Self::new(container, pools)
356	}
357}
358
359impl PooledGuard<TemporalContainer<DateTime>> {
360	/// Create a new pooled TemporalContainer<DateTime> with the specified
361	/// capacity
362	pub fn new_datetime(pools: Pools, capacity: usize) -> Self {
363		let container = pools.datetime_pool().acquire(capacity);
364		Self::new(container, pools)
365	}
366}
367
368impl PooledGuard<TemporalContainer<Time>> {
369	/// Create a new pooled TemporalContainer<Time> with the specified
370	/// capacity
371	pub fn new_time(pools: Pools, capacity: usize) -> Self {
372		let container = pools.time_pool().acquire(capacity);
373		Self::new(container, pools)
374	}
375}
376
377impl PooledGuard<TemporalContainer<Duration>> {
378	/// Create a new pooled TemporalContainer<Duration> with the specified
379	/// capacity
380	pub fn new_duration(pools: Pools, capacity: usize) -> Self {
381		let container = pools.duration_pool().acquire(capacity);
382		Self::new(container, pools)
383	}
384}
385
386// UUID container constructors
387impl PooledGuard<UuidContainer<Uuid4>> {
388	/// Create a new pooled UuidContainer<Uuid4> with the specified capacity
389	pub fn new_uuid4(pools: Pools, capacity: usize) -> Self {
390		let container = pools.uuid4_pool().acquire(capacity);
391		Self::new(container, pools)
392	}
393}
394
395impl PooledGuard<UuidContainer<Uuid7>> {
396	/// Create a new pooled UuidContainer<Uuid7> with the specified capacity
397	pub fn new_uuid7(pools: Pools, capacity: usize) -> Self {
398		let container = pools.uuid7_pool().acquire(capacity);
399		Self::new(container, pools)
400	}
401}
402
403// Implement Drop for automatic pool release
404impl<T: Releasable> Drop for PooledGuard<T> {
405	fn drop(&mut self) {
406		if let (Some(container), Some(pools_inner)) = (self.container.take(), self.pools.upgrade()) {
407			container.release_to_pool(&Pools(pools_inner));
408		}
409	}
410}
411
412// Implement Deref for transparent access to the container
413impl<T: Releasable> Deref for PooledGuard<T> {
414	type Target = T;
415
416	fn deref(&self) -> &Self::Target {
417		self.container.as_ref().expect("Container has been taken")
418	}
419}
420
421// Implement DerefMut for mutable access to the container
422impl<T: Releasable> DerefMut for PooledGuard<T> {
423	fn deref_mut(&mut self) -> &mut Self::Target {
424		self.container.as_mut().expect("Container has been taken")
425	}
426}
427
428#[cfg(test)]
429pub mod tests {
430	use super::*;
431
432	#[test]
433	fn test_bool_container_guard() {
434		let pools = Pools::default();
435
436		// Initial pool should be empty
437		let initial_stats = pools.bool_pool().stats();
438		assert_eq!(initial_stats.available, 0);
439
440		{
441			let guard = PooledGuard::new_bool(pools.clone(), 10);
442			assert!(guard.capacity() >= 10);
443			assert_eq!(guard.len(), 0);
444
445			// Pool should show one acquired
446			let stats = pools.bool_pool().stats();
447			assert_eq!(stats.total_acquired, 1);
448			assert_eq!(stats.available, 0);
449		} // Guard dropped here
450
451		// After drop, container should be returned to pool
452		let final_stats = pools.bool_pool().stats();
453		assert_eq!(final_stats.total_acquired, 1);
454		assert_eq!(final_stats.total_released, 1);
455		assert_eq!(final_stats.available, 1);
456	}
457
458	#[test]
459	fn test_number_container_guard() {
460		let pools = Pools::default();
461
462		{
463			let mut guard = PooledGuard::new_i32(pools.clone(), 20);
464			assert!(guard.capacity() >= 20);
465
466			// Test mutable access
467			guard.push(42);
468			guard.push(100);
469			assert_eq!(guard.len(), 2);
470			assert_eq!(guard.get(0), Some(&42));
471			assert_eq!(guard.get(1), Some(&100));
472		}
473
474		// Container should be returned to pool
475		let stats = pools.i32_pool().stats();
476		assert_eq!(stats.available, 1);
477	}
478
479	#[test]
480	fn test_string_container_guard() {
481		let pools = Pools::default();
482
483		{
484			let mut guard = PooledGuard::new_string(pools.clone(), 5);
485			guard.push("hello".to_string());
486			guard.push("world".to_string());
487			assert_eq!(guard.len(), 2);
488		}
489
490		let stats = pools.string_pool().stats();
491		assert_eq!(stats.available, 1);
492	}
493
494	#[test]
495	fn test_guard_to_owned() {
496		let pools = Pools::default();
497
498		let guard = PooledGuard::new_bool(pools.clone(), 10);
499		let container = guard.to_owned(); // Clone container and release guard to pool
500
501		assert!(container.capacity() >= 10);
502
503		// Pool should have received the original container back
504		let stats = pools.bool_pool().stats();
505		assert_eq!(stats.available, 1);
506		assert_eq!(stats.total_released, 1);
507
508		// We now have a cloned container that's independent of the pool
509		assert!(container.capacity() >= 10);
510	}
511
512	#[test]
513	fn test_multiple_guards_same_pool() {
514		let pools = Pools::default();
515
516		{
517			let _guard1 = PooledGuard::new_f32(pools.clone(), 100);
518			let _guard2 = PooledGuard::new_f32(pools.clone(), 200);
519			let _guard3 = PooledGuard::new_f32(pools.clone(), 50);
520
521			let stats = pools.f32_pool().stats();
522			assert_eq!(stats.total_acquired, 3);
523			assert_eq!(stats.available, 0);
524		}
525
526		// All should be returned
527		let final_stats = pools.f32_pool().stats();
528		assert_eq!(final_stats.total_released, 3);
529		assert_eq!(final_stats.available, 3);
530	}
531
532	#[test]
533	fn test_guard_reuse() {
534		let pools = Pools::default();
535
536		// First usage
537		{
538			let mut guard = PooledGuard::new_i64(pools.clone(), 50);
539			guard.push(123);
540			assert_eq!(guard.len(), 1);
541		}
542
543		// Second usage should reuse the same container
544		{
545			let guard = PooledGuard::new_i64(pools.clone(), 50);
546			// Container should be cleared from pool
547			assert_eq!(guard.len(), 0);
548			assert!(guard.capacity() >= 50);
549		}
550
551		let stats = pools.i64_pool().stats();
552		assert_eq!(stats.total_acquired, 2);
553		assert_eq!(stats.total_released, 2);
554		assert_eq!(stats.available, 1);
555	}
556
557	#[test]
558	fn test_temporal_containers() {
559		let pools = Pools::default();
560
561		{
562			let _date_guard = PooledGuard::new_date(pools.clone(), 10);
563			let _datetime_guard = PooledGuard::new_datetime(pools.clone(), 20);
564			let _time_guard = PooledGuard::new_time(pools.clone(), 30);
565			let _duration_guard = PooledGuard::new_duration(pools.clone(), 40);
566		}
567
568		let all_stats = pools.all_stats();
569		assert_eq!(all_stats["date"].available, 1);
570		assert_eq!(all_stats["datetime"].available, 1);
571		assert_eq!(all_stats["time"].available, 1);
572		assert_eq!(all_stats["duration"].available, 1);
573	}
574
575	#[test]
576	fn test_uuid_containers() {
577		let pools = Pools::default();
578
579		{
580			let _uuid4_guard = PooledGuard::new_uuid4(pools.clone(), 15);
581			let _uuid7_guard = PooledGuard::new_uuid7(pools.clone(), 25);
582		}
583
584		let all_stats = pools.all_stats();
585		assert_eq!(all_stats["uuid4"].available, 1);
586		assert_eq!(all_stats["uuid7"].available, 1);
587	}
588}