1- use std:: {
2- cell:: RefCell , collections:: BTreeMap , rc:: Rc , sync:: atomic:: AtomicBool ,
3- } ;
1+ use std:: { cell:: RefCell , collections:: BTreeMap , rc:: Rc } ;
42
53// Vector pooling minimum capacity threshold
64// Recommended threshold: 64
@@ -11,7 +9,7 @@ use std::{
119// 4. Empirical value: 64 is a proven balance point in real projects
1210const MIN_POOL_CAPACITY : usize = 64 ;
1311
14- trait Poolable {
12+ pub trait Poolable {
1513 fn with_capacity ( capacity : usize ) -> Self ;
1614 fn capacity ( & self ) -> usize ;
1715 fn clear ( & mut self ) ;
@@ -34,50 +32,64 @@ impl<T> Poolable for Vec<T> {
3432/// A memory pool for reusing `T` allocations to reduce memory allocation overhead.
3533#[ derive( Default , Debug ) ]
3634pub struct ObjectPool < T > {
37- usize_vec_pool : RefCell < BTreeMap < usize , Vec < T > > > ,
35+ objects : Rc < RefCell < BTreeMap < usize , Vec < T > > > > ,
36+ }
37+
38+ impl < T > Clone for ObjectPool < T > {
39+ fn clone ( & self ) -> Self {
40+ Self {
41+ objects : self . objects . clone ( ) ,
42+ }
43+ }
3844}
3945
4046impl < T : Poolable > ObjectPool < T > {
4147 /// Retrieves a reusable `T` from the pool with at least the requested capacity.
4248 pub fn pull ( & self , requested_capacity : usize ) -> T {
4349 if requested_capacity < MIN_POOL_CAPACITY
44- || self . usize_vec_pool . borrow ( ) . is_empty ( )
50+ || self . objects . borrow ( ) . is_empty ( )
4551 {
4652 return T :: with_capacity ( requested_capacity) ;
4753 }
48- let mut usize_vec_pool = self . usize_vec_pool . borrow_mut ( ) ;
49- if let Some ( ( _, bucket) ) =
50- usize_vec_pool. range_mut ( requested_capacity..) . next ( )
51- {
52- if let Some ( mut v) = bucket. pop ( ) {
53- v. clear ( ) ;
54- return v;
54+ let mut objects = self . objects . borrow_mut ( ) ;
55+ if let Some ( ( _, bucket) ) = objects. range_mut ( requested_capacity..) . next ( ) {
56+ if let Some ( mut object) = bucket. pop ( ) {
57+ object. clear ( ) ;
58+ return object;
5559 }
5660 }
5761 T :: with_capacity ( requested_capacity)
5862 }
5963
6064 /// Returns a `T` to the pool for future reuse.
61- pub fn ret ( & self , object : T ) {
65+ pub fn return_to_pool ( & self , object : T ) {
6266 if object. capacity ( ) < MIN_POOL_CAPACITY {
6367 return ;
6468 }
65- let mut usize_vec_pool = self . usize_vec_pool . borrow_mut ( ) ;
69+ let mut objects = self . objects . borrow_mut ( ) ;
6670 let cap = object. capacity ( ) ;
67- let bucket = usize_vec_pool . entry ( cap) . or_default ( ) ;
71+ let bucket = objects . entry ( cap) . or_default ( ) ;
6872 bucket. push ( object) ;
6973 }
7074}
7175
76+ /// A smart pointer that holds a pooled object and automatically returns it to the pool when dropped.
77+ ///
78+ /// `Pooled<T>` implements RAII (Resource Acquisition Is Initialization) pattern to manage
79+ /// pooled objects lifecycle. When the `Pooled` instance is dropped, the contained object
80+ /// is automatically returned to its associated pool for future reuse.
7281#[ derive( Debug ) ]
7382pub struct Pooled < T : Poolable > {
7483 object : Option < T > ,
75- pool : Rc < ObjectPool < T > > ,
84+ pool : Option < ObjectPool < T > > ,
7685}
7786
7887impl < T : Poolable > Pooled < T > {
79- fn new ( pool : Rc < ObjectPool < T > > , requested_capacity : usize ) -> Self {
80- let object = pool. pull ( requested_capacity) ;
88+ pub fn new ( pool : Option < ObjectPool < T > > , requested_capacity : usize ) -> Self {
89+ let object = match & pool {
90+ Some ( pool) => pool. pull ( requested_capacity) ,
91+ None => T :: with_capacity ( requested_capacity) ,
92+ } ;
8193 Self {
8294 object : Some ( object) ,
8395 pool,
@@ -96,7 +108,9 @@ impl<T: Poolable> Pooled<T> {
96108impl < T : Poolable > Drop for Pooled < T > {
97109 fn drop ( & mut self ) {
98110 if let Some ( object) = self . object . take ( ) {
99- self . pool . ret ( object) ;
111+ if let Some ( pool) = & self . pool {
112+ pool. return_to_pool ( object) ;
113+ }
100114 }
101115 }
102116}
@@ -115,14 +129,33 @@ impl<T: Poolable> std::ops::DerefMut for Pooled<T> {
115129 }
116130}
117131
118- pub ( crate ) const USING_OBJECT_POOL : AtomicBool = AtomicBool :: new ( false ) ;
132+ thread_local ! {
133+ pub static USIZE_VEC_POOL : RefCell <Option <ObjectPool <Vec <usize >>>> = RefCell :: default ( ) ;
134+ }
119135
136+ /// Executes a function with object pooling enabled for the current thread.
137+ ///
138+ /// This function temporarily enables a thread-local object pool for `Vec<usize>` allocations,
139+ /// executes the provided closure, and then cleans up the pool to prevent memory leaks.
120140pub fn using_object_pool < F , R > ( f : F ) -> R
121141where
122142 F : FnOnce ( ) -> R ,
123143{
124- USING_OBJECT_POOL . store ( true , std:: sync:: atomic:: Ordering :: SeqCst ) ;
144+ // Initialize the thread-local pool if needed
145+ USIZE_VEC_POOL . with ( |pool| {
146+ let mut pool_ref = pool. borrow_mut ( ) ;
147+ if pool_ref. is_none ( ) {
148+ * pool_ref = Some ( ObjectPool :: default ( ) ) ;
149+ }
150+ } ) ;
151+
125152 let result = f ( ) ;
126- USING_OBJECT_POOL . store ( false , std:: sync:: atomic:: Ordering :: SeqCst ) ;
153+
154+ // Clean up the pool to prevent memory retention
155+ // This ensures no memory is held between different pooling sessions
156+ USIZE_VEC_POOL . with ( |pool| {
157+ pool. borrow_mut ( ) . take ( ) ;
158+ } ) ;
159+
127160 result
128161}
0 commit comments