autoreturn_pool/
pool.rs

1use crate::pool_object::PoolObject;
2use crate::Config;
3use parking_lot::{Condvar, Mutex};
4
5/// A pool of objects.
6/// After an object is taken from the pool, it is returned to the pool when it is dropped.
7/// Pool items must be passed on creation by values:
8/// # Examples
9/// basic usage:
10/// ```
11/// let pool = autoreturn_pool::Pool::new([1, 2]);
12/// let item = pool.take();
13/// ```
14/// with custom config:
15/// ```
16/// let config = autoreturn_pool::Config {
17///    wait_duration: std::time::Duration::from_millis(5),
18/// };
19/// let pool = autoreturn_pool::Pool::with_config(config, [1, 2]);
20/// let item = pool.take();
21/// ```
22pub struct Pool<T: Send> {
23    config: Config,
24    storage: Mutex<Vec<T>>,
25    condvar: Condvar,
26}
27
28impl<T: Send + 'static> Pool<T> {
29    pub fn new(items: impl IntoIterator<Item = T>) -> Self {
30        Self::with_config(Config::default(), items)
31    }
32
33    pub fn with_config(config: Config, items: impl IntoIterator<Item = T>) -> Self {
34        let objects = items.into_iter().collect();
35        Self {
36            config,
37            storage: Mutex::new(objects),
38            condvar: Condvar::new(),
39        }
40    }
41
42    /// Take an object from the pool.
43    /// If the pool is empty, the method will wait for the object to be returned to the pool.
44    /// If the wait duration is exceeded, the method will return `None`.
45    pub fn take(&self) -> Option<PoolObject<T>> {
46        let mut lock = self.storage.lock();
47        while lock.is_empty() {
48            let wait_res = self.condvar.wait_for(&mut lock, self.config.wait_duration);
49            if wait_res.timed_out() {
50                return None;
51            }
52        }
53        let inner = lock.pop().unwrap();
54        Some(PoolObject::new(inner, self))
55    }
56
57    /// Allows to add new object to the pool.
58    pub fn add(&self, item: T) {
59        self.storage.lock().push(item);
60    }
61
62    /// Get the number of available objects in the pool.
63    pub fn size(&self) -> usize {
64        self.storage.lock().len()
65    }
66
67    /// Put an object back into the pool and notify one waiting thread.
68    pub(crate) fn put(&self, item: T) {
69        self.storage.lock().push(item);
70        self.condvar.notify_one();
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use crate::pool::Pool;
77    use crate::Config;
78    use std::ops::Deref;
79
80    #[test]
81    fn test_create() {
82        let pool = Pool::new([1, 2, 3]);
83        assert_eq!(pool.size(), 3);
84    }
85
86    #[test]
87    fn test_take() {
88        let pool = Pool::new([1, 2, 3]);
89        let obj1 = pool.take();
90        assert_eq!(pool.size(), 2);
91        assert_eq!(*obj1.as_ref().unwrap().deref(), 3);
92    }
93
94    #[test]
95    fn test_add() {
96        let pool = Pool::new([1]);
97        pool.add(2);
98        assert_eq!(pool.size(), 2);
99    }
100
101    #[test]
102    fn test_wait() {
103        let wait_time = std::time::Duration::from_millis(20);
104        let config = Config {
105            wait_duration: wait_time,
106        };
107        let pool = Pool::with_config(config, [1]);
108        let _obj1 = pool.take();
109        assert_eq!(pool.size(), 0);
110        let start_time = std::time::Instant::now();
111        let obj2 = pool.take();
112        assert!(start_time.elapsed() >= wait_time);
113        assert!(obj2.is_none());
114    }
115
116    #[test]
117    fn test_workflow() -> anyhow::Result<()> {
118        let config = Config {
119            wait_duration: std::time::Duration::from_millis(5),
120        };
121        let pool = Pool::with_config(config, [1, 2, 3]);
122        assert_eq!(pool.size(), 3);
123
124        let obj1 = pool.take();
125        assert_eq!(pool.size(), 2);
126        assert_eq!(*obj1.as_ref().unwrap().deref(), 3);
127
128        let obj2 = pool.take();
129        assert_eq!(*obj2.as_ref().unwrap().deref(), 2);
130        let obj3 = pool.take();
131        assert_eq!(pool.size(), 0);
132        assert_eq!(*obj3.as_ref().unwrap().deref(), 1);
133
134        let obj4 = pool.take();
135        assert!(obj4.is_none());
136
137        Ok(())
138    }
139}