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/09/21 05:54:52 UTC
(asyncband) branch main updated: fix: support unsized condvar state and clarify failure contracts (#323)
This is an automated email from the ASF dual-hosted git repository.
tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/asyncband.git
The following commit(s) were added to refs/heads/main by this push:
new b4d833b fix: support unsized condvar state and clarify failure contracts (#323)
b4d833b is described below
commit b4d833b1a231434e3a059d840a206f82f936fee7
Author: tison <wa...@gmail.com>
AuthorDate: Mon Sep 21 13:54:48 2026 +0800
fix: support unsized condvar state and clarify failure contracts (#323)
---
CHANGELOG.md | 1 +
asyncband/src/condvar/mod.rs | 8 ++++----
asyncband/src/once/once/mod.rs | 7 +++++--
asyncband/src/pool/bounded.rs | 4 ++++
asyncband/src/pool/common.rs | 4 +++-
asyncband/src/pool/unbounded.rs | 4 ++++
tests-integration/tests/condvar_test.rs | 25 +++++++++++++++++++++++++
7 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3192960..e659fba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@ All notable changes to this project will be documented in this file.
### Improvements
+* Allow `Condvar` waits with unsized mutex contents, including slices and trait objects, for both borrowed and owned guards.
* Allow manual unbounded pools to use non-`Send` factory closures with `get_or_create`, including factories that borrow task-local state.
* Allow `watch::channel` to store non-`Clone` values for publication and change notification; only owning reads through `Receiver::get` and `Receiver::recv` require `Clone`.
* Finish releasing buffered bounded MPSC messages even if one message destructor panics.
diff --git a/asyncband/src/condvar/mod.rs b/asyncband/src/condvar/mod.rs
index 06a8550..f2b0652 100644
--- a/asyncband/src/condvar/mod.rs
+++ b/asyncband/src/condvar/mod.rs
@@ -193,7 +193,7 @@ impl Condvar {
/// [`notify_one`](Self::notify_one) but has not yet reacquired the mutex, the notification is
/// passed to another task that is waiting at that point, if one exists. It is never buffered
/// for a future waiter.
- pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
+ pub async fn wait<'a, T: ?Sized>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
let mutex = mutex::guard_lock(&guard);
let notify_one_baton = Wait {
condvar: self,
@@ -212,7 +212,7 @@ impl Condvar {
///
/// This has the same notification and cancellation semantics as [`wait`](Self::wait), but
/// accepts and returns an owned guard.
- pub async fn wait_owned<T>(&self, guard: OwnedMutexGuard<T>) -> OwnedMutexGuard<T> {
+ pub async fn wait_owned<T: ?Sized>(&self, guard: OwnedMutexGuard<T>) -> OwnedMutexGuard<T> {
let mutex = mutex::owned_guard_lock(&guard);
let notify_one_baton = Wait {
condvar: self,
@@ -263,7 +263,7 @@ impl Condvar {
///
/// Each wait iteration has the same cancellation semantics as [`wait`](Self::wait). Cancelling
/// drops the mutex guard; mutations already made by `condition` are not rolled back.
- pub async fn wait_while<'a, T, F>(
+ pub async fn wait_while<'a, T: ?Sized, F>(
&self,
mut guard: MutexGuard<'a, T>,
mut condition: F,
@@ -314,7 +314,7 @@ impl Condvar {
/// Each wait iteration has the same cancellation semantics as
/// [`wait_owned`](Self::wait_owned). Cancelling drops the owned mutex guard; mutations already
/// made by `condition` are not rolled back.
- pub async fn wait_while_owned<T, F>(
+ pub async fn wait_while_owned<T: ?Sized, F>(
&self,
mut guard: OwnedMutexGuard<T>,
mut condition: F,
diff --git a/asyncband/src/once/once/mod.rs b/asyncband/src/once/once/mod.rs
index 4870435..5389d85 100644
--- a/asyncband/src/once/once/mod.rs
+++ b/asyncband/src/once/once/mod.rs
@@ -32,8 +32,11 @@ use crate::semaphore::Semaphore;
///
/// This type also intentionally omits "poisoning" semantics. If an initialization future is
/// cancelled or panics, the attempt is abandoned and other tasks may retry the operation.
-/// Encode partial-initialization detection in the future itself (e.g. return a `Result`)
-/// when needed.
+/// Retrying does not undo side effects from the abandoned attempt.
+///
+/// [`call_once`](Self::call_once) accepts only initializers returning `()`. For fallible
+/// initialization, enable the `once-cell` feature and use `OnceCell::get_or_try_init`, which leaves
+/// the cell empty on error. Use `OnceCell<()>` when no initialized value needs to be stored.
///
/// See the [module level documentation](super) for additional context.
///
diff --git a/asyncband/src/pool/bounded.rs b/asyncband/src/pool/bounded.rs
index 4ac17c6..c630991 100644
--- a/asyncband/src/pool/bounded.rs
+++ b/asyncband/src/pool/bounded.rs
@@ -264,6 +264,10 @@ impl<M: ManageObject> Pool<M> {
/// If the pool has reached its maximum size and has no idle object, this method waits until an
/// object is returned to or detached from the pool.
///
+ /// Idle objects are checked with [`ManageObject::is_recyclable`]. A failed check detaches the
+ /// object and retries checkout; its error is discarded. Only errors from
+ /// [`ManageObject::create`] are returned to the caller.
+ ///
/// # Cancel safety
///
/// Cancelling while waiting for capacity or creating a new object restores the reserved pool
diff --git a/asyncband/src/pool/common.rs b/asyncband/src/pool/common.rs
index 3101961..894b777 100644
--- a/asyncband/src/pool/common.rs
+++ b/asyncband/src/pool/common.rs
@@ -89,7 +89,9 @@ pub trait ManageObject: Send + Sync {
/// Whether the object `o` is recyclable.
///
- /// Returns `Ok(())` if the object is recyclable; otherwise, returns an error.
+ /// Returns `Ok(())` if the object is recyclable. On error, the pool detaches the object and
+ /// retries checkout with another idle object or creates a replacement. The error is discarded
+ /// rather than returned by `Pool::get`; record any needed diagnostics in this implementation.
fn is_recyclable(
&self,
o: &mut Self::Object,
diff --git a/asyncband/src/pool/unbounded.rs b/asyncband/src/pool/unbounded.rs
index ebc5dd3..927530f 100644
--- a/asyncband/src/pool/unbounded.rs
+++ b/asyncband/src/pool/unbounded.rs
@@ -316,6 +316,10 @@ impl<T, M: ManageObject<Object = T>> Pool<T, M> {
///
/// If no idle object is available, this method calls [`ManageObject::create`].
///
+ /// Idle objects are checked with [`ManageObject::is_recyclable`]. A failed check detaches the
+ /// object and retries checkout; its error is discarded. Only errors from
+ /// [`ManageObject::create`] are returned to the caller.
+ ///
/// # Cancel safety
///
/// Cancelling while creating a new object leaves the pool unchanged. Cancelling while
diff --git a/tests-integration/tests/condvar_test.rs b/tests-integration/tests/condvar_test.rs
index 2f95bf2..fb7128f 100644
--- a/tests-integration/tests/condvar_test.rs
+++ b/tests-integration/tests/condvar_test.rs
@@ -256,3 +256,28 @@ fn wait_owned_reacquires_the_mutex() {
assert_eq!(*mutex.lock().await, 1);
});
}
+
+#[test]
+fn predicate_waits_support_unsized_state() {
+ let mutex: Arc<Mutex<[u8]>> = Arc::new(Mutex::new([0, 0]));
+ let condvar = Condvar::new();
+
+ let guard = mutex.try_lock().unwrap();
+ let mut borrowed = Box::pin(condvar.wait_while(guard, |bytes| bytes[0] == 0));
+ assert!(poll_once(borrowed.as_mut()).is_pending());
+ mutex.try_lock().unwrap()[0] = 1;
+ condvar.notify_one();
+ let guard = expect_ready(poll_once(borrowed.as_mut()));
+ assert_eq!(&*guard, &[1, 0]);
+ assert!(mutex.try_lock().is_none());
+ drop(guard);
+
+ let guard = mutex.clone().try_lock_owned().unwrap();
+ let mut owned = Box::pin(condvar.wait_while_owned(guard, |bytes| bytes[1] == 0));
+ assert!(poll_once(owned.as_mut()).is_pending());
+ mutex.try_lock().unwrap()[1] = 2;
+ condvar.notify_one();
+ let guard = expect_ready(poll_once(owned.as_mut()));
+ assert_eq!(&*guard, &[1, 2]);
+ assert!(mutex.try_lock().is_none());
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@asyncband.apache.org
For additional commands, e-mail: commits-help@asyncband.apache.org