1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// Copyright 2025 FastLabs Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Fastpool provides fast and runtime-agnostic object pools for Async Rust.
//!
//! This crate provides two implementations: [bounded pool](bounded::Pool) and
//! [unbounded pool](unbounded::Pool).
//!
//! # Bounded pool
//!
//! A bounded pool creates and recycles objects with full management. You _cannot_ put an object to
//! the pool manually.
//!
//! The pool is bounded by the `max_size` config option of [`PoolConfig`](bounded::PoolConfig). If
//! the pool reaches the maximum size, it will block all the [`Pool::get`](bounded::Pool::get) calls
//! until an object is returned to the pool or an object is detached from the pool.
//!
//! Bounded pools are useful for pooling database connections.
//!
//! ## Examples
//!
//! Read the following simple demo or more complex examples in the examples directory.
//!
//! ```
//! use std::future::Future;
//!
//! use fastpool::ManageObject;
//! use fastpool::ObjectStatus;
//! use fastpool::bounded::Pool;
//! use fastpool::bounded::PoolConfig;
//!
//! struct Compute;
//! impl Compute {
//! async fn do_work(&self) -> i32 {
//! 42
//! }
//! }
//!
//! struct Manager;
//! impl ManageObject for Manager {
//! type Object = Compute;
//! type Error = ();
//!
//! async fn create(&self) -> Result<Self::Object, Self::Error> {
//! Ok(Compute)
//! }
//!
//! async fn is_recyclable(
//! &self,
//! o: &mut Self::Object,
//! status: &ObjectStatus,
//! ) -> Result<(), Self::Error> {
//! Ok(())
//! }
//! }
//!
//! # #[tokio::main]
//! # async fn main() {
//! let pool = Pool::new(PoolConfig::new(16), Manager);
//! let o = pool.get().await.unwrap();
//! assert_eq!(o.do_work().await, 42);
//! # }
//! ```
//!
//! # Unbounded pool
//!
//! An unbounded pool, on the other hand, allows you to put objects to the pool manually. You can
//! use it like Go's [`sync.Pool`](https://pkg.go.dev/sync#Pool).
//!
//! To configure a factory for creating objects when the pool is empty, like `sync.Pool`'s `New`,
//! you can create the unbounded pool via [`Pool::new`](unbounded::Pool::new) with an
//! implementation of [`ManageObject`].
//!
//! ## Examples
//!
//! Read the following simple demo or more complex examples in the examples directory.
//!
//! ```
//! use fastpool::unbounded::Pool;
//! use fastpool::unbounded::PoolConfig;
//!
//! # #[tokio::main]
//! # async fn main() {
//! let pool = Pool::<Vec<u8>>::never_manage(PoolConfig::default());
//!
//! let result = pool.get().await;
//! assert_eq!(result.unwrap_err().to_string(), "unbounded pool is empty");
//!
//! pool.extend_one(Vec::with_capacity(1024));
//! let o = pool.get().await.unwrap();
//! assert_eq!(o.capacity(), 1024);
//! # }
//! ```
//!
//! # FAQ
//!
//! ## Why does fastpool have no timeout config?
//!
//! Many async object pool implementations allow you to configure multiple timeout, like "wait
//! timeout", "create timeout", "recycle timeout", etc.
//!
//! This introduces two major problems:
//!
//! First, to support timeouts, the pool must use with a timer implementation like `tokio::time`.
//! This would prevent the pool from being runtime-agnostic. Theoretically, the pool can depend
//! on a timer trait, but there is no such a standard trait in the Rust ecosystem yet.
//!
//! Second, timeouts options not only add complexity for configuration, but also the value itself
//! cannot be configured properly at all. For example, end users often care about the total time
//! used to obtain an object. This is not solely "wait timeout", "create timeout", or
//! "recycle timeout", but a conditional composition of all internal operations.
//!
//! Thus, we propose a caller-side timeout solution:
//!
//! ```rust,ignore
//! use std::sync::Arc;
//! use std::time::Duration;
//!
//! use fastpool::bounded::Object;
//! use fastpool::bounded::Pool;
//!
//! #[derive(Debug, Clone)]
//! pub struct ConnectionPool {
//! pool: Arc<Pool<ManageConnection>>,
//! }
//!
//! impl ConnectionPool {
//! pub async fn acquire(&self) -> Result<Object<ManageConnection>, Error> {
//! const ACQUIRE_TIMEOUT: Duration = Duration::from_secs(60);
//!
//! // note that users can choose any timer implementation here
//! let result = tokio::time::timeout(ACQUIRE_TIMEOUT, self.pool.get()).await;
//!
//! // ... processing the result
//! }
//! }
//! ```
//!
//! Check out the postgres example in the examples directory for the complete code.
//!
//! ## Why does fastpool have no before/after hooks?
//!
//! Similar to the second point of the previous question, the before/after hooks (closures) are
//! very hard to configure properly. Specific to closures, many Rust code can be easily written
//! in place, but if you'd like to pass a code block as a closure, then you may encounter a lot of
//! lifetime and ownership issues. Besides, how to handle `Result` in the closure is also a
//! headache.
//!
//! Fastpool provides an ordinary interface of object pools, so that you should be able to add
//! any before/after logic in the implementation or using a wrapper.
//!
//! For example, all the "post-create", "pre-recycle", and "post-recycle" hooks can be implemented
//! as:
//!
//! ```
//! use std::future::Future;
//!
//! use fastpool::ManageObject;
//! use fastpool::ObjectStatus;
//!
//! struct Manager;
//! impl ManageObject for Manager {
//! type Object = i32;
//! type Error = std::convert::Infallible;
//!
//! async fn create(&self) -> Result<Self::Object, Self::Error> {
//! let o = 42;
//! // any post-create hooks
//! Ok(o)
//! }
//!
//! async fn is_recyclable(
//! &self,
//! _o: &mut Self::Object,
//! _status: &ObjectStatus,
//! ) -> Result<(), Self::Error> {
//! // any pre-recycle hooks
//! // determinate if is_recyclable
//! // any post-recycle hooks
//! Ok(())
//! }
//! }
//! ```
pub use ManageObject;
pub use ObjectStatus;
pub use QueueStrategy;
pub use RecycleCancelledStrategy;
pub use RetainResult;