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
210
211
212
213
214
215
216
217
218
219
220
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
// Portions ported from Fastpool 1.1.1 at commit e4c65f1ed38395abc58d68eda8bd09925c13028e:
// Copyright 2025 FastLabs Developers
// https://github.com/fast/fastpool/tree/e4c65f1ed38395abc58d68eda8bd09925c13028e
//! Runtime-agnostic object pools for async Rust.
//!
//! This module provides a manager-created [bounded pool](bounded::Pool) and an
//! [unbounded pool](unbounded::Pool) that can also accept objects supplied by callers.
//!
//! Both implementations provide resource reuse without taking ownership of runtime policy. They do
//! not start maintenance tasks or install timers. Callers decide how to schedule maintenance and
//! can wrap operations such as [`bounded::Pool::get`] in the deadline mechanism of their runtime.
//!
//! # Bounded pool
//!
//! A bounded pool uses a [`ManageObject`] implementation to create, validate, and detach objects.
//! Objects cannot be inserted manually.
//!
//! The pool is bounded by the `max_size` config option of [`PoolConfig`](bounded::PoolConfig). If
//! the pool reaches the maximum size, additional [`Pool::get`](bounded::Pool::get) calls wait until
//! an object is returned to or detached from the pool.
//!
//! Bounded pools are useful for pooling database connections.
//!
//! ## Examples
//!
//! The following example shows the core managed-pool workflow.
//!
//! ```
//! use asyncband::pool::ManageObject;
//! use asyncband::pool::ObjectStatus;
//! use asyncband::pool::bounded::Pool;
//! use asyncband::pool::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 accepts manually supplied objects and can be used 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
//!
//! The following example shows a manually populated unbounded pool.
//!
//! ```
//! use asyncband::pool::unbounded::Pool;
//! use asyncband::pool::unbounded::PoolConfig;
//!
//! let pool = Pool::<Vec<u8>>::never_manage(PoolConfig::default());
//!
//! assert!(pool.try_get().is_none());
//!
//! pool.extend_one(Vec::with_capacity(1024));
//! let o = pool.try_get().unwrap();
//! assert_eq!(o.capacity(), 1024);
//! ```
//!
//! # FAQ
//!
//! ## Why does the caller control timeouts?
//!
//! A timer inside the pool would couple it to a runtime or require one adapter per timer ecosystem.
//! Separate wait, create, and recycle timeouts also do not necessarily express the caller's actual
//! deadline for the complete checkout operation. Asyncband therefore returns an ordinary future so
//! the caller can apply one end-to-end deadline with its chosen timer:
//!
//! ```rust,no_run
//! use std::sync::Arc;
//! use std::time::Duration;
//!
//! use asyncband::pool::ManageObject;
//! use asyncband::pool::ObjectStatus;
//! use asyncband::pool::bounded::Object;
//! use asyncband::pool::bounded::Pool;
//! # use asyncband::pool::bounded::PoolConfig;
//!
//! # struct Connection;
//! # #[derive(Debug)]
//! # struct Error;
//! # struct ManageConnection;
//! # impl ManageObject for ManageConnection {
//! # type Object = Connection;
//! # type Error = Error;
//! #
//! # async fn create(&self) -> Result<Self::Object, Self::Error> {
//! # Ok(Connection)
//! # }
//! #
//! # async fn is_recyclable(
//! # &self,
//! # _object: &mut Self::Object,
//! # _status: &ObjectStatus,
//! # ) -> Result<(), Self::Error> {
//! # Ok(())
//! # }
//! # }
//! # enum AcquireError {
//! # Create(Error),
//! # Timeout,
//! # }
//!
//! #[derive(Clone)]
//! struct ConnectionPool {
//! pool: Arc<Pool<ManageConnection>>,
//! }
//!
//! impl ConnectionPool {
//! async fn acquire(&self) -> Result<Object<ManageConnection>, AcquireError> {
//! const ACQUIRE_TIMEOUT: Duration = Duration::from_secs(60);
//!
//! // Callers can use the timer implementation of their runtime.
//! match tokio::time::timeout(ACQUIRE_TIMEOUT, self.pool.get()).await {
//! Ok(result) => result.map_err(AcquireError::Create),
//! Err(_) => Err(AcquireError::Timeout),
//! }
//! }
//! }
//! # let _pool = ConnectionPool {
//! # pool: Pool::new(PoolConfig::new(16), ManageConnection),
//! # };
//! ```
//!
//! ## Why are general before/after hooks outside the pool?
//!
//! Before/after behavior is application policy. Small operations are clearer at the call site,
//! while larger behavior can live in the manager or an application wrapper without forcing a
//! general closure and error model into the pool.
//!
//! For example, create and recycle behavior can be expressed directly by [`ManageObject`]:
//!
//! ```
//! use asyncband::pool::ManageObject;
//! use asyncband::pool::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,
//! object: &mut Self::Object,
//! status: &ObjectStatus,
//! ) -> Result<(), Self::Error> {
//! // Validate or refresh `object`, using `status` when useful.
//! Ok(())
//! }
//! }
//! ```
pub use ManageObject;
pub use ObjectStatus;
pub use QueueStrategy;
pub use RecycleCancelledStrategy;
pub use RetainResult;