You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asyncband.apache.org by ti...@apache.org on 2026/10/03 15:25:48 UTC

(asyncband) 01/01: refactor(pool): borrow the pool during recycling

This is an automated email from the ASF dual-hosted git repository.

tisonkun pushed a commit to branch codex/deslopify-pool-recycling
in repository https://gitbox.apache.org/repos/asf/asyncband.git

commit 1d1d04e3d41a06d8c6b557d2715df06e2b7c264c
Author: tison <wa...@gmail.com>
AuthorDate: Sat Oct 3 23:25:39 2026 +0800

    refactor(pool): borrow the pool during recycling
    
    UnreadyObject only lives inside Pool::get, which already borrows an
    Arc<Pool>. Hold that borrow directly, remove the copied cancellation
    policy, and stop upgrading a Weak that cannot have expired.
    
    Create the returned Object's Weak in ready(), preserving independent
    checkout lifetimes. Cover both pool variants when a recycled object
    outlives its pool and is then dropped or detached.
---
 asyncband/src/pool/bounded.rs                | 33 ++++++++++++----------------
 asyncband/src/pool/unbounded.rs              | 33 ++++++++++++----------------
 tests-integration/tests/pool_test/recycle.rs | 22 +++++++++++++++++++
 3 files changed, 50 insertions(+), 38 deletions(-)

diff --git a/asyncband/src/pool/bounded.rs b/asyncband/src/pool/bounded.rs
index c630991..9b3e2fd 100644
--- a/asyncband/src/pool/bounded.rs
+++ b/asyncband/src/pool/bounded.rs
@@ -293,8 +293,7 @@ impl<M: ManageObject> Pool<M> {
                 Some(object) => {
                     let mut unready_object = UnreadyObject {
                         state: Some(object),
-                        pool: Arc::downgrade(self),
-                        recycle_cancelled_strategy: self.config.recycle_cancelled_strategy,
+                        pool: self,
                     };
 
                     let state = unready_object.state();
@@ -556,34 +555,32 @@ impl<M: ManageObject> Object<M> {
 /// If the check fails, `detach()` should be called to permanently remove the object
 /// from the pool. If dropped without calling either method (due to being cancelled),
 /// the behavior depends on the pool's [`RecycleCancelledStrategy`] configuration.
-struct UnreadyObject<M: ManageObject> {
+struct UnreadyObject<'a, M: ManageObject> {
     state: Option<ObjectState<M::Object>>,
-    pool: Weak<Pool<M>>,
-    recycle_cancelled_strategy: RecycleCancelledStrategy,
+    // The enclosing `get` call keeps the pool alive through cancellation cleanup.
+    pool: &'a Arc<Pool<M>>,
 }
 
-impl<M: ManageObject> Drop for UnreadyObject<M> {
+impl<M: ManageObject> Drop for UnreadyObject<'_, M> {
     fn drop(&mut self) {
         if let Some(mut state) = self.state.take() {
-            if let Some(pool) = self.pool.upgrade() {
-                match self.recycle_cancelled_strategy {
-                    RecycleCancelledStrategy::Detach => {
-                        pool.detach_object(&mut state.o);
-                    }
-                    RecycleCancelledStrategy::ReturnToPool => {
-                        pool.restore_idle(state);
-                    }
+            match self.pool.config.recycle_cancelled_strategy {
+                RecycleCancelledStrategy::Detach => {
+                    self.pool.detach_object(&mut state.o);
+                }
+                RecycleCancelledStrategy::ReturnToPool => {
+                    self.pool.restore_idle(state);
                 }
             }
         }
     }
 }
 
-impl<M: ManageObject> UnreadyObject<M> {
+impl<M: ManageObject> UnreadyObject<'_, M> {
     fn ready(mut self, permit: OwnedSemaphorePermit) -> Object<M> {
         // INVARIANT: `state` is `Some` until this object becomes ready, detaches, or is dropped.
         let state = Some(self.state.take().unwrap());
-        let pool = self.pool.clone();
+        let pool = Arc::downgrade(self.pool);
         Object {
             state,
             permit,
@@ -593,9 +590,7 @@ impl<M: ManageObject> UnreadyObject<M> {
 
     fn detach(&mut self) {
         if let Some(mut state) = self.state.take() {
-            if let Some(pool) = self.pool.upgrade() {
-                pool.detach_object(&mut state.o);
-            }
+            self.pool.detach_object(&mut state.o);
         }
     }
 
diff --git a/asyncband/src/pool/unbounded.rs b/asyncband/src/pool/unbounded.rs
index ac0baff..44deef3 100644
--- a/asyncband/src/pool/unbounded.rs
+++ b/asyncband/src/pool/unbounded.rs
@@ -342,8 +342,7 @@ impl<T, M: ManageObject<Object = T>> Pool<T, M> {
                 Some(object) => {
                     let mut unready_object = UnreadyObject {
                         state: Some(object),
-                        pool: Arc::downgrade(self),
-                        recycle_cancelled_strategy: self.config.recycle_cancelled_strategy,
+                        pool: self,
                     };
 
                     let state = unready_object.state();
@@ -571,42 +570,38 @@ impl<T, M: ManageObject<Object = T>> Object<T, M> {
 /// If the check fails, `detach()` should be called to permanently remove the object
 /// from the pool. If dropped without calling either method (due to being cancelled),
 /// the behavior depends on the pool's [`RecycleCancelledStrategy`] configuration.
-struct UnreadyObject<T, M: ManageObject<Object = T>> {
+struct UnreadyObject<'a, T, M: ManageObject<Object = T>> {
     state: Option<ObjectState<T>>,
-    pool: Weak<Pool<T, M>>,
-    recycle_cancelled_strategy: RecycleCancelledStrategy,
+    // The enclosing `get` call keeps the pool alive through cancellation cleanup.
+    pool: &'a Arc<Pool<T, M>>,
 }
 
-impl<T, M: ManageObject<Object = T>> Drop for UnreadyObject<T, M> {
+impl<T, M: ManageObject<Object = T>> Drop for UnreadyObject<'_, T, M> {
     fn drop(&mut self) {
         if let Some(mut state) = self.state.take() {
-            if let Some(pool) = self.pool.upgrade() {
-                match self.recycle_cancelled_strategy {
-                    RecycleCancelledStrategy::Detach => {
-                        pool.detach_object(&mut state.o);
-                    }
-                    RecycleCancelledStrategy::ReturnToPool => {
-                        pool.restore_idle(state);
-                    }
+            match self.pool.config.recycle_cancelled_strategy {
+                RecycleCancelledStrategy::Detach => {
+                    self.pool.detach_object(&mut state.o);
+                }
+                RecycleCancelledStrategy::ReturnToPool => {
+                    self.pool.restore_idle(state);
                 }
             }
         }
     }
 }
 
-impl<T, M: ManageObject<Object = T>> UnreadyObject<T, M> {
+impl<T, M: ManageObject<Object = T>> UnreadyObject<'_, T, M> {
     fn ready(mut self) -> Object<T, M> {
         // INVARIANT: `state` is `Some` until this object becomes ready, detaches, or is dropped.
         let state = Some(self.state.take().unwrap());
-        let pool = self.pool.clone();
+        let pool = Arc::downgrade(self.pool);
         Object { state, pool }
     }
 
     fn detach(&mut self) {
         if let Some(mut state) = self.state.take() {
-            if let Some(pool) = self.pool.upgrade() {
-                pool.detach_object(&mut state.o);
-            }
+            self.pool.detach_object(&mut state.o);
         }
     }
 
diff --git a/tests-integration/tests/pool_test/recycle.rs b/tests-integration/tests/pool_test/recycle.rs
index 03ff53e..d877cc6 100644
--- a/tests-integration/tests/pool_test/recycle.rs
+++ b/tests-integration/tests/pool_test/recycle.rs
@@ -113,6 +113,28 @@ macro_rules! recycle_contract {
             }
         }
 
+        #[test]
+        fn recycled_object_can_outlive_the_pool() {
+            for detach in [false, true] {
+                let manager = Manager::default();
+                let pool = pool(1, manager.clone(), None);
+                drop(ready(pool.get()).unwrap());
+                let object = ready(pool.get()).unwrap();
+                assert_eq!(object.status().recycle_count(), 1);
+
+                let weak_pool = Arc::downgrade(&pool);
+                drop(pool);
+                assert!(weak_pool.upgrade().is_none());
+                assert_eq!(*object, 0);
+                if detach {
+                    assert_eq!(object.detach(), 0);
+                } else {
+                    drop(object);
+                }
+                assert!(manager.detached().is_empty());
+            }
+        }
+
         #[test]
         fn cancelling_one_validation_leaves_other_idle_objects_available() {
             let manager = Manager::default();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@asyncband.apache.org
For additional commands, e-mail: commits-help@asyncband.apache.org