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/04 07:52:53 UTC

(asyncband) branch main updated: fix: clarify synchronization licensing and documentation (#281)

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 58425c5  fix: clarify synchronization licensing and documentation (#281)
58425c5 is described below

commit 58425c54b6842dbc35c050699e1b44a79111db87
Author: tison <wa...@gmail.com>
AuthorDate: Fri Sep 4 15:52:50 2026 +0800

    fix: clarify synchronization licensing and documentation (#281)
---
 LICENSE                                          |  23 +-
 asyncband/src/broadcast/mpmc/unbounded/mod.rs    |   9 +-
 asyncband/src/completion/mod.rs                  |   5 +-
 asyncband/src/condvar/mod.rs                     |   2 +-
 asyncband/src/event/mod.rs                       |   9 +-
 asyncband/src/lib.rs                             |   7 +-
 asyncband/src/mpsc/bounded.rs                    |   6 +-
 asyncband/src/mutex/mod.rs                       | 399 +++++++++--------------
 asyncband/src/once/once/mod.rs                   |  10 +-
 asyncband/src/once/once_cell/mod.rs              |  22 +-
 asyncband/src/pool/bounded.rs                    |   2 +-
 asyncband/src/rwlock/mapped_read_guard.rs        |  48 +--
 asyncband/src/rwlock/mapped_write_guard.rs       |  66 +---
 asyncband/src/rwlock/mod.rs                      |  90 +++--
 asyncband/src/rwlock/owned_mapped_read_guard.rs  |  52 +--
 asyncband/src/rwlock/owned_mapped_write_guard.rs |  69 +---
 asyncband/src/rwlock/owned_read_guard.rs         |  67 ++--
 asyncband/src/rwlock/owned_write_guard.rs        |  76 +----
 asyncband/src/rwlock/read_guard.rs               |  50 +--
 asyncband/src/rwlock/write_guard.rs              |  60 +---
 asyncband/src/semaphore/mod.rs                   |  12 +-
 asyncband/src/watch/mod.rs                       |   4 +-
 licenserc.toml                                   |   1 -
 23 files changed, 398 insertions(+), 691 deletions(-)

diff --git a/LICENSE b/LICENSE
index 2229678..8a2b25c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -217,6 +217,20 @@ Portions of asyncband/src/mutex/mod.rs are derived from Tokio 1.41.0:
 
   https://github.com/tokio-rs/tokio/blob/01e04daaa162ce6122bb894fdda0b6803dd32093/tokio/src/sync/mutex.rs
 
+Asyncband's Mutex core predates the incorporation of Tokio's owned and mapped
+guard APIs. It is built on Asyncband's own fair semaphore, has no closed state
+or acquisition error, returns Option from nonblocking acquisition, and provides
+a single const constructor without runtime-specific tracing or blocking entry
+points. Asyncband substantially changed the incorporated guard implementation.
+Projected guards use NonNull pointers and explicit invariance, borrowed and
+owned projected guards can be mapped repeatedly, and guard destruction releases
+permits through Asyncband's semaphore lifecycle.
+
+The standard ASF source header on asyncband/src/mutex/mod.rs applies to the
+independent Mutex core and the substantial modifications contributed to Apache
+Asyncband. It does not replace the Tokio copyright notice or MIT License
+applicable to the incorporated portions.
+
 Portions of the following files are derived from Tokio 1.42.0. Each local path
 is followed by its upstream source path or paths:
 
@@ -245,8 +259,13 @@ The exact Tokio 1.42.0 revision is:
 
   https://github.com/tokio-rs/tokio/tree/bb9d57017e100985f86d8ca41ac105ee9140423e/tokio/src/sync
 
-Asyncband's RwLock accepts any NonZeroUsize as max_readers instead of Tokio's
-restricted range.
+Asyncband adapted RwLock to its own semaphore and guard model. Acquisition has
+no semaphore-close error, nonblocking methods return Option, and max_readers is
+any NonZeroUsize instead of Tokio's restricted range. Mapped read guards use
+separate borrowed and owned types rather than changing a type parameter on the
+original read guard. All mapped guards use filter_map naming, and mutable mapped
+guards carry explicit invariance. The local guard types release and downgrade
+permits through Asyncband's usize-based semaphore implementation.
 
 Portions of the following files originated from Tokio 1.47.0's OnceCell. Each
 local path is followed by its upstream source path:
diff --git a/asyncband/src/broadcast/mpmc/unbounded/mod.rs b/asyncband/src/broadcast/mpmc/unbounded/mod.rs
index 85712a5..3ac8e8f 100644
--- a/asyncband/src/broadcast/mpmc/unbounded/mod.rs
+++ b/asyncband/src/broadcast/mpmc/unbounded/mod.rs
@@ -28,18 +28,11 @@
 //! receiver can make that backlog exhaust available memory.
 //! [`UnboundedSender::retained_message_count`] reports its current length.
 //!
-//! Buffer capacity is reused across steady bursts. Allocation retained for an unusually large burst
-//! is released after a later, substantially smaller cycle drains completely.
-//!
 //! # Receivers
 //!
 //! [`UnboundedSender::subscribe`] and [`UnboundedReceiver::resubscribe`] add a receiver at the
 //! current publication boundary. They do not copy another receiver's unread backlog.
 //!
-//! Reclaiming the oldest value requires finding the earliest remaining receiver cursor. That work
-//! scales with the receiver slots allocated by the channel, including slots retained for reuse
-//! after receivers are dropped.
-//!
 //! # Example
 //!
 //! ```
@@ -387,7 +380,7 @@ impl<T> UnboundedSender<T> {
     ///
     /// # Panics
     ///
-    /// Panics when publishing would overflow the channel's internal version counter.
+    /// Panics if the channel has already published `u64::MAX` values.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/completion/mod.rs b/asyncband/src/completion/mod.rs
index 38a0e51..ea34a07 100644
--- a/asyncband/src/completion/mod.rs
+++ b/asyncband/src/completion/mod.rs
@@ -138,9 +138,8 @@ impl<T> Completer<T> {
     ///
     /// # Panics
     ///
-    /// Panics if a registered waker panics while being notified. The value is committed before
-    /// notification begins. Before resuming the panic, `complete` still attempts to wake every
-    /// remaining registered waker.
+    /// Panics if notifying a waiting observer panics. The value remains committed, and notification
+    /// is still attempted for every other pending observer before the panic resumes.
     pub fn complete(mut self, value: T) -> Result<(), T> {
         let Some(shared) = self.shared.upgrade() else {
             return Err(value);
diff --git a/asyncband/src/condvar/mod.rs b/asyncband/src/condvar/mod.rs
index c294363..8dcc643 100644
--- a/asyncband/src/condvar/mod.rs
+++ b/asyncband/src/condvar/mod.rs
@@ -191,7 +191,7 @@ impl Condvar {
     ///
     /// # Cancel safety
     ///
-    /// Cancelling this wait removes the task from the wait queue. If the task was selected by
+    /// Cancelling this wait removes it from the current waiters. If the task was selected by
     /// [`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.
diff --git a/asyncband/src/event/mod.rs b/asyncband/src/event/mod.rs
index aaab5e8..1afa051 100644
--- a/asyncband/src/event/mod.rs
+++ b/asyncband/src/event/mod.rs
@@ -144,8 +144,8 @@ impl ManualResetEvent {
     ///
     /// # Panics
     ///
-    /// Panics if a registered waker panics. The state transition remains committed, and every
-    /// remaining registered waker is still notified before the first panic resumes.
+    /// Panics if notifying a waiting task panics. The event remains set, and notification is still
+    /// attempted for every other current waiter before the panic resumes.
     ///
     /// # Examples
     ///
@@ -223,9 +223,8 @@ impl ManualResetEvent {
     /// # Cancel safety
     ///
     /// Dropping a pending wait unregisters only that call; it does not change the event or affect
-    /// other waiters. If cancellation races with `set`, either cancellation unregisters first or
-    /// `set` commits the wait first. A waker already detached by `set` may still run after the wait
-    /// is dropped.
+    /// other waiters. If cancellation races with [`set`](Self::set), the wait either unregisters
+    /// first or has already been released by that call.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/lib.rs b/asyncband/src/lib.rs
index 11d7cfd..f6ee53f 100644
--- a/asyncband/src/lib.rs
+++ b/asyncband/src/lib.rs
@@ -96,10 +96,9 @@
 //! instead of duplicating synchronous methods across every type. Sync-first implementations can
 //! exploit OS- or platform-specific facilities and remain the domain of dedicated libraries.
 //!
-//! The adapter's single-future executor parks the calling thread and resumes it through the
-//! future's waker. It is not a general-purpose async runtime, and futures that depend on a
-//! runtime-specific timer or I/O driver may not make progress. See the module documentation for the
-//! full execution constraints.
+//! The adapter polls one future on the calling thread. It is not a general-purpose async runtime,
+//! and futures that depend on a runtime-specific timer or I/O driver may not make progress. See the
+//! module documentation for the full execution constraints.
 //!
 //! # Thread safety
 //!
diff --git a/asyncband/src/mpsc/bounded.rs b/asyncband/src/mpsc/bounded.rs
index 3ca6098..6818a47 100644
--- a/asyncband/src/mpsc/bounded.rs
+++ b/asyncband/src/mpsc/bounded.rs
@@ -118,9 +118,9 @@ impl<T> BoundedSender<T> {
     ///
     /// # Cancel safety
     ///
-    /// Dropping a pending `send` removes that operation from the capacity wait queue and drops
-    /// `value`; a call that has returned `Pending` has not sent the message. Use [`Self::try_send`]
-    /// when the caller must retain ownership if capacity is unavailable.
+    /// Dropping a pending `send` loses its place waiting for capacity and drops `value`; a call
+    /// that has returned `Pending` has not sent the message. Use [`Self::try_send`] when the
+    /// caller must retain ownership if capacity is unavailable.
     pub async fn send(&self, value: T) -> Result<(), SendError<T>> {
         let value = match self.try_send(value) {
             Ok(()) => return Ok(()),
diff --git a/asyncband/src/mutex/mod.rs b/asyncband/src/mutex/mod.rs
index b0986ad..97772ee 100644
--- a/asyncband/src/mutex/mod.rs
+++ b/asyncband/src/mutex/mod.rs
@@ -1,19 +1,39 @@
-// This file contains code derived from Tokio 1.41.0.
+// 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 of the owned and mapped guard APIs originated from Tokio 1.41.0's Mutex implementation.
 // Copyright (c) Tokio Contributors
-// The derived code remains licensed under the MIT License.
-// The incorporated code has been modified for use in Apache Asyncband.
+// The Tokio-derived portions remain licensed under the MIT License.
+// Asyncband independently built the mutex on its own semaphore and substantially changed the
+// incorporated guard implementation: the try-lock error type and semaphore closure are absent,
+// projected guards use NonNull pointers with explicit invariance, and projected guards can be
+// mapped repeatedly in both borrowed and owned forms.
 // Upstream source:
 // https://github.com/tokio-rs/tokio/blob/01e04daaa162ce6122bb894fdda0b6803dd32093/tokio/src/sync/mutex.rs
 
-//! An async mutex for protecting shared data.
+//! Mutual exclusion that yields the current task while waiting.
 //!
-//! Unlike a standard mutex, this implementation is designed to work with async/await,
-//! ensuring tasks yield properly when the lock is contended. This makes it suitable
-//! for protecting shared resources in async code.
+//! A successful lock operation returns a guard that provides exclusive access to the protected
+//! value and releases the lock when dropped. The guard may be held across `.await` points. When
+//! that is unnecessary, a synchronous mutex is usually cheaper.
 //!
-//! This mutex will block tasks waiting for the lock to become available. The
-//! mutex can be created via [`new`] and the protected data can be accessed
-//! via the async [`lock`] method.
+//! Lock requests complete in the order they begin waiting. Cancelling a pending request loses its
+//! place, so a later attempt waits behind requests already in progress. A panic while holding a
+//! guard releases the lock without poisoning it.
 //!
 //! # Examples
 //!
@@ -24,29 +44,24 @@
 //!
 //! use asyncband::mutex::Mutex;
 //!
-//! let mutex = Arc::new(Mutex::new(0));
-//! let mut handles = vec![];
+//! let counter = Arc::new(Mutex::new(0));
+//! let mut tasks = vec![];
 //!
-//! for i in 0..3 {
-//!     let mutex = mutex.clone();
-//!     handles.push(tokio::spawn(async move {
-//!         let mut lock = mutex.lock().await;
-//!         *lock += i;
+//! for _ in 0..3 {
+//!     let counter = counter.clone();
+//!     tasks.push(tokio::spawn(async move {
+//!         *counter.lock().await += 1;
 //!     }));
 //! }
 //!
-//! for handle in handles {
-//!     handle.await.unwrap();
+//! for task in tasks {
+//!     task.await.unwrap();
 //! }
 //!
-//! let final_value = mutex.lock().await;
-//! assert_eq!(*final_value, 3); // 0 + 1 + 2
+//! assert_eq!(*counter.lock().await, 3);
 //!
-//! #  }
+//! # }
 //! ```
-//!
-//! [`new`]: Mutex::new
-//! [`lock`]: Mutex::lock
 
 use std::cell::UnsafeCell;
 use std::fmt;
@@ -59,7 +74,7 @@ use std::sync::Arc;
 
 use crate::internal::semaphore;
 
-/// An async mutex for protecting shared data.
+/// An asynchronous mutex that grants exclusive access in request order.
 ///
 /// See the [module level documentation](self) for more.
 pub struct Mutex<T: ?Sized> {
@@ -96,7 +111,7 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
 }
 
 impl<T> Mutex<T> {
-    /// Creates a new mutex in an unlocked state ready for use.
+    /// Wraps `t` in an unlocked mutex.
     ///
     /// # Examples
     ///
@@ -111,7 +126,7 @@ impl<T> Mutex<T> {
         Self { s, c }
     }
 
-    /// Consumes the mutex, returning the underlying data.
+    /// Unwraps the protected value.
     ///
     /// # Examples
     ///
@@ -128,17 +143,11 @@ impl<T> Mutex<T> {
 }
 
 impl<T: ?Sized> Mutex<T> {
-    /// Locks this mutex, causing the current task to yield until the lock has been acquired. When
-    /// the lock has been acquired, function returns a [`MutexGuard`].
-    ///
-    /// This method is async and will yield the current task if the mutex is currently held by
-    /// another task. When the mutex becomes available, the task will be woken up and given the
-    /// lock.
+    /// Waits for exclusive access and returns a borrowed guard.
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute locks in the order they were requested.
-    /// Cancelling a call to `lock` makes you lose your place in the queue.
+    /// Pending lock requests complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
@@ -158,8 +167,7 @@ impl<T: ?Sized> Mutex<T> {
         MutexGuard { lock: self }
     }
 
-    /// Attempts to acquire the lock, and returns `None` if the lock is currently held somewhere
-    /// else.
+    /// Acquires the mutex without waiting, or returns `None` when it is already held.
     ///
     /// # Examples
     ///
@@ -180,22 +188,14 @@ impl<T: ?Sized> Mutex<T> {
         }
     }
 
-    /// Locks this mutex, causing the current task to yield until the lock has been acquired. When
-    /// the lock has been acquired, this returns an [`OwnedMutexGuard`].
-    ///
-    /// This method is async and will yield the current task if the mutex is currently held by
-    /// another task. When the mutex becomes available, the task will be woken up and given the
-    /// lock.
+    /// Waits for exclusive access and returns a guard that owns this [`Arc`].
     ///
-    /// This method is identical to [`Mutex::lock`], except that the returned guard references the
-    /// `Mutex` with an [`Arc`] rather than by borrowing it. Therefore, the `Mutex` must be
-    /// wrapped in an `Arc` to call this method, and the guard will live for the `'static` lifetime,
-    /// as it keeps the `Mutex` alive by holding an `Arc`.
+    /// The owned guard keeps the mutex alive instead of borrowing it, which allows the guard to be
+    /// moved wherever a `'static` value is required.
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute locks in the order they were requested.
-    /// Cancelling a call to `lock_owned` makes you lose your place in the queue.
+    /// Pending lock requests complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
@@ -217,13 +217,9 @@ impl<T: ?Sized> Mutex<T> {
         OwnedMutexGuard { lock: self }
     }
 
-    /// Attempts to acquire the lock, and returns `None` if the lock is currently held somewhere
-    /// else.
+    /// Acquires the mutex without waiting and returns a guard that owns this [`Arc`].
     ///
-    /// This method is identical to [`Mutex::try_lock`], except that the returned guard references
-    /// the `Mutex` with an [`Arc`] rather than by borrowing it. Therefore, the `Mutex` must be
-    /// wrapped in an `Arc` to call this method, and the guard will live for the `'static` lifetime,
-    /// as it keeps the `Mutex` alive by holding an `Arc`.
+    /// Returns `None` when another guard currently holds the mutex.
     ///
     /// # Examples
     ///
@@ -246,10 +242,10 @@ impl<T: ?Sized> Mutex<T> {
         }
     }
 
-    /// Returns a mutable reference to the underlying data.
+    /// Borrows the protected value mutably without locking.
     ///
-    /// Since this call borrows the `Mutex` mutably, no actual locking needs to take place: the
-    /// mutable borrow statically guarantees no locks exist.
+    /// The exclusive borrow of the mutex already prevents any guard from existing at the same
+    /// time.
     ///
     /// # Examples
     ///
@@ -265,32 +261,11 @@ impl<T: ?Sized> Mutex<T> {
     }
 }
 
-/// RAII structure used to release the exclusive lock on a mutex when dropped.
-///
-/// This structure is created by the [`lock`] and [`try_lock`] methods on [`Mutex`].
-///
-/// [`lock`]: Mutex::lock
-/// [`try_lock`]: Mutex::try_lock
-///
-/// See the [module level documentation](self) for more.
-///
-/// # Variance
-///
-/// The guard is invariant over `T`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::mutex::MutexGuard;
+/// A borrowed proof of exclusive access to a [`Mutex`].
 ///
-/// fn shorten<'lock, 'short: 'lock>(
-///     guard: MutexGuard<'lock, &'static str>,
-///     value: &'short str,
-/// ) -> MutexGuard<'lock, &'short str> {
-///     let mut guard: MutexGuard<'lock, &'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the Mutex will immediately unlock"]
+/// [`Mutex::lock`] and [`Mutex::try_lock`] create this guard. It dereferences to the protected
+/// value and releases the lock when dropped.
+#[must_use = "dropping the guard releases the mutex immediately"]
 pub struct MutexGuard<'a, T: ?Sized> {
     lock: &'a Mutex<T>,
 }
@@ -334,12 +309,10 @@ impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
 }
 
 impl<'a, T: ?Sized> MutexGuard<'a, T> {
-    /// Makes a new [`MappedMutexGuard`] for a component of the locked data.
+    /// Projects this guard to a mutable component of the protected value.
     ///
-    /// This operation cannot fail as the `MutexGuard` passed in already locked the mutex.
-    ///
-    /// This is an associated function that needs to be used as `MutexGuard::map(...)`. A method
-    /// would interfere with methods of the same name on the contents of the locked data.
+    /// The returned guard keeps the same mutex locked. Call this as
+    /// `MutexGuard::map(...)` so a method named `map` on `T` remains accessible through deref.
     ///
     /// # Examples
     ///
@@ -391,14 +364,10 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
         }
     }
 
-    /// Attempts to make a new [`MappedMutexGuard`] for a component of the locked data. The
-    /// original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `MutexGuard` passed in already locked the mutex.
+    /// Attempts to project this guard to a mutable component of the protected value.
     ///
-    /// This is an associated function that needs to be used as `MutexGuard::filter_map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `MutexGuard::filter_map(...)` so a method with the same name on `T` remains accessible.
     ///
     /// # Examples
     ///
@@ -456,38 +425,12 @@ impl<'a, T: ?Sized> MutexGuard<'a, T> {
     }
 }
 
-/// An owned handle to a held `Mutex`.
-///
-/// This guard is only available from a [`Mutex`] that is wrapped in an [`Arc`]. It is identical to
-/// [`MutexGuard`], except that rather than borrowing the `Mutex`, it clones the `Arc`, incrementing
-/// the reference count. This means that unlike `MutexGuard`, it will have the `'static` lifetime.
-///
-/// As long as you have this guard, you have exclusive access to the underlying `T`. The guard
-/// internally keeps a reference-counted pointer to the original `Mutex`, so even if the lock goes
-/// away, the guard remains valid.
+/// A proof of exclusive access that owns an [`Arc`] containing its [`Mutex`].
 ///
-/// The lock is automatically released whenever the guard is dropped, at which point `lock` will
-/// succeed yet again.
-///
-/// See the [module level documentation](self) for more.
-///
-/// # Variance
-///
-/// The guard is invariant over `T`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::mutex::OwnedMutexGuard;
-///
-/// fn shorten<'short>(
-///     guard: OwnedMutexGuard<&'static str>,
-///     value: &'short str,
-/// ) -> OwnedMutexGuard<&'short str> {
-///     let mut guard: OwnedMutexGuard<&'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the Mutex will immediately unlock"]
+/// [`Mutex::lock_owned`] and [`Mutex::try_lock_owned`] create this guard. Owning the `Arc` lets the
+/// guard outlive the reference used to acquire it; dropping the guard releases the lock and its
+/// share of the `Arc`.
+#[must_use = "dropping the guard releases the mutex immediately"]
 pub struct OwnedMutexGuard<T: ?Sized> {
     lock: Arc<Mutex<T>>,
 }
@@ -531,12 +474,10 @@ impl<T: ?Sized> DerefMut for OwnedMutexGuard<T> {
 }
 
 impl<T: ?Sized> OwnedMutexGuard<T> {
-    /// Makes a new [`OwnedMappedMutexGuard`] for a component of the locked data.
-    ///
-    /// This operation cannot fail as the `OwnedMutexGuard` passed in already locked the mutex.
+    /// Projects this guard to a mutable component of the protected value.
     ///
-    /// This is an associated function that needs to be used as `OwnedMutexGuard::map(...)`. A
-    /// method would interfere with methods of the same name on the contents of the locked data.
+    /// The returned guard retains the same `Arc` and keeps the mutex locked. Call this as
+    /// `OwnedMutexGuard::map(...)` so a method named `map` on `T` remains accessible through deref.
     ///
     /// # Examples
     ///
@@ -584,14 +525,10 @@ impl<T: ?Sized> OwnedMutexGuard<T> {
         }
     }
 
-    /// Attempts to make a new [`OwnedMappedMutexGuard`] for a component of the locked data. The
-    /// original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `OwnedMutexGuard` passed in already locked the mutex.
-    ///
-    /// This is an associated function that needs to be used as `OwnedMutexGuard::filter_map(...)`.
+    /// Attempts to project this guard to a mutable component of the protected value.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `OwnedMutexGuard::filter_map(...)` so a method with the same name on `T` remains accessible.
     ///
     /// # Examples
     ///
@@ -638,25 +575,10 @@ impl<T: ?Sized> OwnedMutexGuard<T> {
     }
 }
 
-/// RAII structure used to release the exclusive lock on a mutex when dropped, for a mapped
-/// component of the locked data.
+/// A borrowed mutex guard projected to a mutable component of the protected value.
 ///
-/// This structure is created by the [`map`] and [`filter_map`] methods on [`MutexGuard`]. It allows
-/// you to hold a lock on a subfield of the protected data, enabling more fine-grained access
-/// control while maintaining the same locking semantics.
-///
-/// As long as you have this guard, you have exclusive access to the underlying `T`. The guard
-/// internally keeps a reference to the original mutex's semaphore, so the original lock is
-/// maintained until this guard is dropped.
-///
-/// `MappedMutexGuard` implements [`Send`] and [`Sync`]
-/// when the underlying data type supports these traits, allowing it to be used across task
-/// boundaries and shared between threads safely.
-///
-/// See the [module level documentation](self) for more.
-///
-/// [`map`]: MutexGuard::map
-/// [`filter_map`]: MutexGuard::filter_map
+/// [`MutexGuard::map`] and [`MutexGuard::filter_map`] create this guard. It keeps the mutex locked
+/// while exposing only the projected component.
 ///
 /// # Examples
 ///
@@ -694,24 +616,7 @@ impl<T: ?Sized> OwnedMutexGuard<T> {
 /// assert_eq!(profile_guard.email, "user@example.com");
 /// # }
 /// ```
-///
-/// # Variance
-///
-/// The guard is invariant over `T`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::mutex::MappedMutexGuard;
-///
-/// fn shorten<'lock, 'short: 'lock>(
-///     guard: MappedMutexGuard<'lock, &'static str>,
-///     value: &'short str,
-/// ) -> MappedMutexGuard<'lock, &'short str> {
-///     let mut guard: MappedMutexGuard<'lock, &'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the Mutex will immediately unlock"]
+#[must_use = "dropping the guard releases the mutex immediately"]
 pub struct MappedMutexGuard<'a, T: ?Sized> {
     /// Non-null pointer to the mapped data
     d: NonNull<T>,
@@ -766,12 +671,11 @@ impl<T: ?Sized> DerefMut for MappedMutexGuard<'_, T> {
 }
 
 impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
-    /// Makes a new [`MappedMutexGuard`] for a component of the locked data.
-    ///
-    /// This operation cannot fail as the `MappedMutexGuard` passed in already locked the mutex.
+    /// Projects an already mapped guard to a deeper mutable component.
     ///
-    /// This is an associated function that needs to be used as `MappedMutexGuard::map(...)`. A
-    /// method would interfere with methods of the same name on the contents of the locked data.
+    /// The returned guard keeps the same mutex locked. Call this as
+    /// `MappedMutexGuard::map(...)` so a method named `map` on `T` remains accessible through
+    /// deref.
     ///
     /// # Examples
     ///
@@ -828,14 +732,11 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
         }
     }
 
-    /// Attempts to make a new [`MappedMutexGuard`] for a component of the locked data. The
-    /// original guard is returned if the closure returns `None`.
+    /// Attempts to project an already mapped guard to a deeper mutable component.
     ///
-    /// This operation cannot fail as the `MappedMutexGuard` passed in already locked the mutex.
-    ///
-    /// This is an associated function that needs to be used as `MappedMutexGuard::filter_map(...)`.
-    /// A method would interfere with methods of the same name on the contents of the locked
-    /// data.
+    /// The original mapped guard is returned when `f` returns `None`. Call this as
+    /// `MappedMutexGuard::filter_map(...)` so a method with the same name on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
@@ -890,28 +791,10 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
     }
 }
 
-/// An owned handle to a held `Mutex` for a mapped component of the locked data.
-///
-/// This guard is only available from a [`Mutex`] that is wrapped in an [`Arc`]. It is similar to
-/// [`MappedMutexGuard`], except that rather than borrowing the `Mutex`, it clones the `Arc`,
-/// incrementing the reference count. This means that unlike `MappedMutexGuard`, it will have
-/// the `'static` lifetime.
-///
-/// This structure is created by the [`map`] and [`filter_map`] methods on [`OwnedMutexGuard`].
-/// It allows you to hold a lock on a subfield of the protected data, enabling more fine-grained
-/// access control while maintaining the same locking semantics.
-///
-/// As long as you have this guard, you have exclusive access to the underlying `U`. The guard
-/// internally keeps a reference-counted pointer to the original `Mutex`, so even if the lock goes
-/// away, the guard remains valid.
-///
-/// The lock is automatically released whenever the guard is dropped, at which point `lock` will
-/// succeed yet again.
-///
-/// See the [module level documentation](self) for more.
+/// An owned mutex guard projected to a mutable component of the protected value.
 ///
-/// [`map`]: OwnedMutexGuard::map
-/// [`filter_map`]: OwnedMutexGuard::filter_map
+/// [`OwnedMutexGuard::map`] and [`OwnedMutexGuard::filter_map`] create this guard. It keeps the
+/// original mutex alive and locked while exposing only the projected component.
 ///
 /// # Examples
 ///
@@ -935,24 +818,7 @@ impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
 /// assert_eq!(*value_guard, 42);
 /// # }
 /// ```
-///
-/// # Variance
-///
-/// The guard is invariant over its mapped type `U`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::mutex::OwnedMappedMutexGuard;
-///
-/// fn shorten<'short>(
-///     guard: OwnedMappedMutexGuard<(), &'static str>,
-///     value: &'short str,
-/// ) -> OwnedMappedMutexGuard<(), &'short str> {
-///     let mut guard: OwnedMappedMutexGuard<(), &'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the Mutex will immediately unlock"]
+#[must_use = "dropping the guard releases the mutex immediately"]
 pub struct OwnedMappedMutexGuard<T: ?Sized, U: ?Sized> {
     // This Arc acts as an ownership certificate, ensuring the Mutex remains valid
     // and the lock is not released
@@ -1013,13 +879,11 @@ impl<T: ?Sized, U: ?Sized> DerefMut for OwnedMappedMutexGuard<T, U> {
 }
 
 impl<T: ?Sized, U: ?Sized> OwnedMappedMutexGuard<T, U> {
-    /// Makes a new [`OwnedMappedMutexGuard`] for a component of the locked data.
-    ///
-    /// This operation cannot fail as the `OwnedMappedMutexGuard` passed in already locked the
-    /// mutex.
+    /// Projects an owned mapped guard to a deeper mutable component.
     ///
-    /// This is an associated function that needs to be used as `OwnedMappedMutexGuard::map(...)`. A
-    /// method would interfere with methods of the same name on the contents of the locked data.
+    /// The returned guard retains the same `Arc` and keeps the mutex locked. Call this as
+    /// `OwnedMappedMutexGuard::map(...)` so a method named `map` on `U` remains accessible through
+    /// deref.
     ///
     /// # Examples
     ///
@@ -1073,15 +937,11 @@ impl<T: ?Sized, U: ?Sized> OwnedMappedMutexGuard<T, U> {
         }
     }
 
-    /// Attempts to make a new [`OwnedMappedMutexGuard`] for a component of the locked data. The
-    /// original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `OwnedMappedMutexGuard` passed in already locked the
-    /// mutex.
+    /// Attempts to project an owned mapped guard to a deeper mutable component.
     ///
-    /// This is an associated function that needs to be used as
-    /// `OwnedMappedMutexGuard::filter_map(...)`. A method would interfere with methods of the same
-    /// name on the contents of the locked data.
+    /// The original mapped guard is returned when `f` returns `None`. Call this as
+    /// `OwnedMappedMutexGuard::filter_map(...)` so a method with the same name on `U` remains
+    /// accessible through deref.
     ///
     /// # Examples
     ///
@@ -1149,3 +1009,62 @@ impl<T: ?Sized, U: ?Sized> OwnedMappedMutexGuard<T, U> {
         }
     }
 }
+
+#[cfg(doctest)]
+mod compile_fail_tests {
+    /// ```compile_fail
+    /// use asyncband::mutex::MutexGuard;
+    ///
+    /// fn shorten<'lock, 'short: 'lock>(
+    ///     guard: MutexGuard<'lock, &'static str>,
+    ///     value: &'short str,
+    /// ) -> MutexGuard<'lock, &'short str> {
+    ///     let mut guard: MutexGuard<'lock, &'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct MutexGuardIsInvariant;
+
+    /// ```compile_fail
+    /// use asyncband::mutex::OwnedMutexGuard;
+    ///
+    /// fn shorten<'short>(
+    ///     guard: OwnedMutexGuard<&'static str>,
+    ///     value: &'short str,
+    /// ) -> OwnedMutexGuard<&'short str> {
+    ///     let mut guard: OwnedMutexGuard<&'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct OwnedMutexGuardIsInvariant;
+
+    /// ```compile_fail
+    /// use asyncband::mutex::MappedMutexGuard;
+    ///
+    /// fn shorten<'lock, 'short: 'lock>(
+    ///     guard: MappedMutexGuard<'lock, &'static str>,
+    ///     value: &'short str,
+    /// ) -> MappedMutexGuard<'lock, &'short str> {
+    ///     let mut guard: MappedMutexGuard<'lock, &'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct MappedMutexGuardIsInvariant;
+
+    /// ```compile_fail
+    /// use asyncband::mutex::OwnedMappedMutexGuard;
+    ///
+    /// fn shorten<'short>(
+    ///     guard: OwnedMappedMutexGuard<(), &'static str>,
+    ///     value: &'short str,
+    /// ) -> OwnedMappedMutexGuard<(), &'short str> {
+    ///     let mut guard: OwnedMappedMutexGuard<(), &'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct OwnedMappedMutexGuardIsInvariant;
+}
diff --git a/asyncband/src/once/once/mod.rs b/asyncband/src/once/once/mod.rs
index f0cafe2..4870435 100644
--- a/asyncband/src/once/once/mod.rs
+++ b/asyncband/src/once/once/mod.rs
@@ -26,9 +26,9 @@ use crate::semaphore::Semaphore;
 
 /// A synchronization primitive which can be used to run a one-time async initialization.
 ///
-/// Unlike [`std::sync::Once`], this type never blocks a thread. The provided closure must
-/// produce a future and the future is awaited inside the primitive. Coordination happens
-/// with asynchronous [`Semaphore`], which keeps the implementation runtime-agnostic.
+/// Unlike [`std::sync::Once`], this type waits asynchronously rather than blocking a thread. The
+/// provided closure produces a future, and successful completion prevents later calls from running
+/// another initializer.
 ///
 /// 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.
@@ -137,8 +137,8 @@ impl Once {
     /// If the provided operation is cancelled, the initialization attempt is cancelled. If there
     /// are other tasks waiting, one of them will start another attempt.
     ///
-    /// Calling call_once recursively on the same Once from within the closure will deadlock,
-    /// because the closure holds the semaphore permit while trying to acquire it again.
+    /// Calling `call_once` recursively on the same `Once` from its initializer deadlocks because
+    /// the inner call waits for the outer call to finish.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/once/once_cell/mod.rs b/asyncband/src/once/once_cell/mod.rs
index d05775b..39ee75f 100644
--- a/asyncband/src/once/once_cell/mod.rs
+++ b/asyncband/src/once/once_cell/mod.rs
@@ -85,7 +85,7 @@ impl<T> OnceCell<T> {
         }
     }
 
-    /// Returns whether the internal value is set.
+    /// Returns whether the cell contains a value.
     // `OnceMap` and `singleflight` inspect this state, while a standalone `OnceCell` build does
     // not need the internal helper.
     #[allow(dead_code)]
@@ -93,12 +93,12 @@ impl<T> OnceCell<T> {
         self.value.is_initialized()
     }
 
-    /// Returns whether the internal value is set.
+    /// Returns whether the cell contains a value.
     pub(crate) fn initialized_mut(&mut self) -> bool {
         self.value.is_initialized_mut()
     }
 
-    /// Gets the reference to the underlying value.
+    /// Returns the stored value.
     ///
     /// Returns `None` if the cell is uninitialized, or being initialized.
     ///
@@ -107,7 +107,7 @@ impl<T> OnceCell<T> {
         self.value.get()
     }
 
-    /// Gets the mutable reference to the underlying value.
+    /// Returns mutable access to the stored value.
     ///
     /// Returns `None` if the cell is uninitialized.
     ///
@@ -117,8 +117,7 @@ impl<T> OnceCell<T> {
         self.value.get_mut()
     }
 
-    /// Gets the reference to the internal value, initializing it with the provided asynchronous
-    /// function if it is not set yet.
+    /// Returns the value, initializing an empty cell with the provided asynchronous function.
     ///
     /// If some other task is currently working on initializing the `OnceCell`, this call will wait
     /// for that other task to finish, then return the value that the other task produced.
@@ -140,8 +139,8 @@ impl<T> OnceCell<T> {
         }
     }
 
-    /// Gets the reference to the internal value, initializing it with the provided asynchronous
-    /// function if it is not set yet.
+    /// Returns the value, initializing an empty cell with the provided fallible asynchronous
+    /// function.
     ///
     /// If some other task is currently working on initializing the `OnceCell`, this call will wait
     /// for that other task to finish, then return the value that the other task produced.
@@ -171,8 +170,7 @@ impl<T> OnceCell<T> {
         Ok(self.set_value(value, permit))
     }
 
-    /// Gets a mutable reference to the internal value, initializing it with the provided
-    /// asynchronous function if it is not set yet.
+    /// Returns mutable access to the value, initializing an empty cell asynchronously.
     ///
     /// This method never blocks other tasks because it takes `&mut self`, which guarantees
     /// exclusive access to the `OnceCell` and thus no concurrent initialization can be in
@@ -207,8 +205,8 @@ impl<T> OnceCell<T> {
         }
     }
 
-    /// Gets a mutable reference to the internal value, initializing it with the provided
-    /// asynchronous function that may fail if it is not set yet.
+    /// Returns mutable access to the value, initializing an empty cell with a fallible asynchronous
+    /// function.
     ///
     /// This method never blocks other tasks because it takes `&mut self`, which guarantees
     /// exclusive access to the `OnceCell` and thus no concurrent initialization can be in
diff --git a/asyncband/src/pool/bounded.rs b/asyncband/src/pool/bounded.rs
index 6c3009a..4ac17c6 100644
--- a/asyncband/src/pool/bounded.rs
+++ b/asyncband/src/pool/bounded.rs
@@ -207,7 +207,7 @@ impl<M: ManageObject> Pool<M> {
     /// checked-out objects. Existing idle objects count toward the target, and the pool's
     /// maximum size is never exceeded. Targets above the maximum size are treated as the maximum.
     /// Concurrent calls and checkouts can change the observed idle count while this method is
-    /// running, so the target is best effort rather than a postcondition.
+    /// running, so the target is the best effort rather than a postcondition.
     ///
     /// Returns the number of objects created. If [`ManageObject::create`] fails, objects created by
     /// this call before the failure remain in the pool and the error is returned.
diff --git a/asyncband/src/rwlock/mapped_read_guard.rs b/asyncband/src/rwlock/mapped_read_guard.rs
index 741e286..1b06bd8 100644
--- a/asyncband/src/rwlock/mapped_read_guard.rs
+++ b/asyncband/src/rwlock/mapped_read_guard.rs
@@ -13,26 +13,11 @@ use std::ptr::NonNull;
 
 use crate::internal::semaphore;
 
-/// RAII structure used to release the shared read access of a lock when dropped, for a mapped
-/// component of the locked data.
+/// A borrowed read guard projected to one component of the protected value.
 ///
-/// This structure is created by the [`map`] and [`filter_map`] methods on [`RwLockReadGuard`]. It
-/// allows you to hold a read lock on a subfield of the protected data, enabling more fine-grained
-/// access control while maintaining the same locking semantics.
-///
-/// As long as you have this guard, you have shared read access to the underlying `T`. The guard
-/// internally keeps a reference to the original rwlock's semaphore, so the original lock is
-/// maintained until this guard is dropped.
-///
-/// `MappedRwLockReadGuard` implements [`Send`] and [`Sync`] when `T: Sync`, allowing it to be
-/// used across task boundaries and shared between threads safely. Note that [`Send`] does not
-/// require `T: Send` because the read guard only borrows the data rather than owning it.
-///
-/// [`map`]: crate::rwlock::RwLockReadGuard::map
-/// [`filter_map`]: crate::rwlock::RwLockReadGuard::filter_map
-/// [`RwLockReadGuard`]: crate::rwlock::RwLockReadGuard
-///
-/// See the [module level documentation](crate::rwlock) for more.
+/// [`RwLockReadGuard::map`](crate::rwlock::RwLockReadGuard::map) and
+/// [`RwLockReadGuard::filter_map`](crate::rwlock::RwLockReadGuard::filter_map) create this guard.
+/// It keeps the original read access active while exposing only the projected component.
 ///
 /// # Examples
 ///
@@ -70,7 +55,7 @@ use crate::internal::semaphore;
 /// assert_eq!(profile_guard.email, "user@example.com");
 /// # }
 /// ```
-#[must_use = "if unused the RwLock will immediately unlock"]
+#[must_use = "dropping the guard releases its read access immediately"]
 pub struct MappedRwLockReadGuard<'a, T: ?Sized> {
     d: NonNull<T>,
     s: &'a semaphore::Semaphore,
@@ -125,14 +110,10 @@ impl<T: ?Sized> Deref for MappedRwLockReadGuard<'_, T> {
 }
 
 impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> {
-    /// Makes a new [`MappedRwLockReadGuard`] for a component of the locked data.
+    /// Projects this guard to a deeper shared component.
     ///
-    /// This operation cannot fail as the `MappedRwLockReadGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as `MappedRwLockReadGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The returned guard keeps the same read access active. Call this as
+    /// `MappedRwLockReadGuard::map(...)` so a method named `map` on `T` remains accessible.
     ///
     /// # Examples
     ///
@@ -186,16 +167,11 @@ impl<'a, T: ?Sized> MappedRwLockReadGuard<'a, T> {
         MappedRwLockReadGuard::new(d, orig.s)
     }
 
-    /// Attempts to make a new [`MappedRwLockReadGuard`] for a component of the locked data. The
-    /// original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `MappedRwLockReadGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `MappedRwLockReadGuard::filter_map(...)`.
+    /// Attempts to project this guard to a deeper shared component.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `MappedRwLockReadGuard::filter_map(...)` so a method with the same name on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/rwlock/mapped_write_guard.rs b/asyncband/src/rwlock/mapped_write_guard.rs
index 04e4862..4307842 100644
--- a/asyncband/src/rwlock/mapped_write_guard.rs
+++ b/asyncband/src/rwlock/mapped_write_guard.rs
@@ -15,26 +15,11 @@ use std::ptr::NonNull;
 use crate::internal::semaphore;
 use crate::rwlock::MappedRwLockReadGuard;
 
-/// RAII structure used to release the exclusive write access of a lock when dropped, for a mapped
-/// component of the locked data.
+/// A borrowed write guard projected to one component of the protected value.
 ///
-/// This structure is created by the [`map`] and [`filter_map`] methods on [`RwLockWriteGuard`]. It
-/// allows you to hold a write lock on a subfield of the protected data, enabling more fine-grained
-/// access control while maintaining the same locking semantics.
-///
-/// As long as you have this guard, you have exclusive write access to the underlying `T`. The guard
-/// internally keeps a reference to the original rwlock's semaphore and tracks the number of permits
-/// acquired, so the original lock is maintained until this guard is dropped.
-///
-/// `MappedRwLockWriteGuard` implements [`Send`] when the underlying data type implements [`Send`],
-/// and implements [`Sync`] when the underlying data type implements both [`Send`] and [`Sync`],
-/// allowing it to be used across task boundaries and shared between threads safely.
-///
-/// [`map`]: crate::rwlock::RwLockWriteGuard::map
-/// [`filter_map`]: crate::rwlock::RwLockWriteGuard::filter_map
-/// [`RwLockWriteGuard`]: crate::rwlock::RwLockWriteGuard
-///
-/// See the [module level documentation](crate::rwlock) for more.
+/// [`RwLockWriteGuard::map`](crate::rwlock::RwLockWriteGuard::map) and
+/// [`RwLockWriteGuard::filter_map`](crate::rwlock::RwLockWriteGuard::filter_map) create this guard.
+/// It keeps the original write access active while exposing only the projected component.
 ///
 /// # Examples
 ///
@@ -73,24 +58,7 @@ use crate::rwlock::MappedRwLockReadGuard;
 /// assert_eq!(profile_guard.email, "newemail@example.com");
 /// # }
 /// ```
-///
-/// # Variance
-///
-/// The guard is invariant over `T`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::rwlock::MappedRwLockWriteGuard;
-///
-/// fn shorten<'lock, 'short: 'lock>(
-///     guard: MappedRwLockWriteGuard<'lock, &'static str>,
-///     value: &'short str,
-/// ) -> MappedRwLockWriteGuard<'lock, &'short str> {
-///     let mut guard: MappedRwLockWriteGuard<'lock, &'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the RwLock will immediately unlock"]
+#[must_use = "dropping the guard releases its write access immediately"]
 pub struct MappedRwLockWriteGuard<'a, T: ?Sized> {
     d: NonNull<T>,
     s: &'a semaphore::Semaphore,
@@ -153,15 +121,10 @@ impl<T: ?Sized> DerefMut for MappedRwLockWriteGuard<'_, T> {
 }
 
 impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> {
-    /// Makes a new [`MappedRwLockWriteGuard`] for a component of the locked data.
-    ///
-    /// This operation cannot fail as the `MappedRwLockWriteGuard` passed in already locked the
-    /// rwlock.
+    /// Projects this guard to a deeper mutable component.
     ///
-    /// This is an associated function that needs to be used as `MappedRwLockWriteGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked
-    /// data.
+    /// The returned guard keeps the same write access active. Call this as
+    /// `MappedRwLockWriteGuard::map(...)` so a method named `map` on `T` remains accessible.
     ///
     /// # Examples
     ///
@@ -217,16 +180,11 @@ impl<'a, T: ?Sized> MappedRwLockWriteGuard<'a, T> {
         MappedRwLockWriteGuard::new(d, orig.s, permits_acquired)
     }
 
-    /// Attempts to make a new [`MappedRwLockWriteGuard`] for a component of the locked data. The
-    /// original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `MappedRwLockWriteGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `MappedRwLockWriteGuard::filter_map(...)`.
+    /// Attempts to project this guard to a deeper mutable component.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `MappedRwLockWriteGuard::filter_map(...)` so a method with the same name on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/rwlock/mod.rs b/asyncband/src/rwlock/mod.rs
index 80d8b40..346e38b 100644
--- a/asyncband/src/rwlock/mod.rs
+++ b/asyncband/src/rwlock/mod.rs
@@ -5,27 +5,17 @@
 // Upstream source:
 // https://github.com/tokio-rs/tokio/blob/bb9d57017e100985f86d8ca41ac105ee9140423e/tokio/src/sync/rwlock.rs
 
-//! A reader-writer lock that allows multiple readers or a single writer at a time.
+//! Shared read access or exclusive write access to a value.
 //!
-//! This type of lock allows a number of readers or at most one writer at any point in time. The
-//! write portion of this lock typically allows modification of the underlying data (exclusive
-//! access) and the read portion of this lock typically allows for read-only access (shared access).
+//! Any number of readers may hold the lock together. A writer waits for existing readers and then
+//! holds the lock alone, allowing it to modify the protected value.
 //!
-//! In comparison, a [`Mutex`] does not distinguish between readers or writers that acquire the
-//! lock, therefore causing any tasks waiting for the lock to become available to yield. An RwLock
-//! will allow any number of readers to acquire the lock as long as a writer is not holding the
-//! lock.
+//! Requests are considered in arrival order. Once a writer is waiting ahead of a reader, that
+//! reader waits until the writer has acquired and released the lock. This prevents a steady stream
+//! of readers from starving writers.
 //!
-//! The priority policy of this read-write lock is fair (or [write-preferring]), ensuring that
-//! readers cannot starve writers. Fairness is maintained using a first-in, first-out queue for
-//! tasks awaiting the lock. If a writer reaches the head of the queue, readers will not acquire
-//! the lock until that writer has acquired and released it. In contrast, the priority policy of
-//! the Rust standard library's `std::sync::RwLock` depends on the operating system.
-//!
-//! The type parameter `T` represents the data that this lock protects. It is required that `T`
-//! satisfies [`Send`] to be shared across threads. The RAII guards returned from the locking
-//! methods implement [`Deref`] (and [`DerefMut`] for the `write` method) to allow access to the
-//! content of the lock.
+//! Read guards dereference to `&T`; write guards dereference to `&mut T`. Dropping a guard releases
+//! its access. The mapping APIs can narrow a guard to one component without unlocking in between.
 //!
 //! # Examples
 //!
@@ -53,11 +43,6 @@
 //!
 //! # }
 //! ```
-//!
-//! [`Mutex`]: crate::mutex::Mutex
-//! [`Deref`]: std::ops::Deref
-//! [`DerefMut`]: std::ops::DerefMut
-//! [write-preferring]: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock#Priority_policies
 
 use std::cell::UnsafeCell;
 use std::fmt;
@@ -195,3 +180,62 @@ impl<T: ?Sized> RwLock<T> {
         self.c.get_mut()
     }
 }
+
+#[cfg(doctest)]
+mod compile_fail_tests {
+    /// ```compile_fail
+    /// use asyncband::rwlock::RwLockWriteGuard;
+    ///
+    /// fn shorten<'lock, 'short: 'lock>(
+    ///     guard: RwLockWriteGuard<'lock, &'static str>,
+    ///     value: &'short str,
+    /// ) -> RwLockWriteGuard<'lock, &'short str> {
+    ///     let mut guard: RwLockWriteGuard<'lock, &'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct RwLockWriteGuardIsInvariant;
+
+    /// ```compile_fail
+    /// use asyncband::rwlock::OwnedRwLockWriteGuard;
+    ///
+    /// fn shorten<'short>(
+    ///     guard: OwnedRwLockWriteGuard<&'static str>,
+    ///     value: &'short str,
+    /// ) -> OwnedRwLockWriteGuard<&'short str> {
+    ///     let mut guard: OwnedRwLockWriteGuard<&'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct OwnedRwLockWriteGuardIsInvariant;
+
+    /// ```compile_fail
+    /// use asyncband::rwlock::MappedRwLockWriteGuard;
+    ///
+    /// fn shorten<'lock, 'short: 'lock>(
+    ///     guard: MappedRwLockWriteGuard<'lock, &'static str>,
+    ///     value: &'short str,
+    /// ) -> MappedRwLockWriteGuard<'lock, &'short str> {
+    ///     let mut guard: MappedRwLockWriteGuard<'lock, &'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct MappedRwLockWriteGuardIsInvariant;
+
+    /// ```compile_fail
+    /// use asyncband::rwlock::OwnedMappedRwLockWriteGuard;
+    ///
+    /// fn shorten<'short>(
+    ///     guard: OwnedMappedRwLockWriteGuard<(), &'static str>,
+    ///     value: &'short str,
+    /// ) -> OwnedMappedRwLockWriteGuard<(), &'short str> {
+    ///     let mut guard: OwnedMappedRwLockWriteGuard<(), &'short str> = guard;
+    ///     *guard = value;
+    ///     guard
+    /// }
+    /// ```
+    struct OwnedMappedRwLockWriteGuardIsInvariant;
+}
diff --git a/asyncband/src/rwlock/owned_mapped_read_guard.rs b/asyncband/src/rwlock/owned_mapped_read_guard.rs
index c0d6dfc..ae96acd 100644
--- a/asyncband/src/rwlock/owned_mapped_read_guard.rs
+++ b/asyncband/src/rwlock/owned_mapped_read_guard.rs
@@ -15,28 +15,12 @@ use std::sync::Arc;
 
 use crate::rwlock::RwLock;
 
-/// Owned RAII structure used to release the shared read access of a lock when dropped, for a mapped
-/// component of the locked data.
+/// An owned read guard projected to one component of the protected value.
 ///
-/// This guard is only available from a [`RwLock`] that is wrapped in an [`Arc`]. It is similar to
-/// [`MappedRwLockReadGuard`], except that rather than borrowing the `RwLock`, it clones the `Arc`,
-/// incrementing the reference count. This means that unlike `MappedRwLockReadGuard`, it will have
-/// the `'static` lifetime.
-///
-/// As long as you have this guard, you have shared read access to the underlying `U`. The guard
-/// internally keeps an `Arc` reference to the original rwlock, so the original lock is
-/// maintained until this guard is dropped.
-///
-/// `OwnedMappedRwLockReadGuard` implements [`Send`] and [`Sync`]
-/// when the underlying data type supports these traits, allowing it to be used across task
-/// boundaries and shared between threads safely.
-///
-/// [`map`]: crate::rwlock::OwnedRwLockReadGuard::map
-/// [`filter_map`]: crate::rwlock::OwnedRwLockReadGuard::filter_map
-/// [`OwnedRwLockReadGuard`]: crate::rwlock::OwnedRwLockReadGuard
-/// [`MappedRwLockReadGuard`]: crate::rwlock::MappedRwLockReadGuard
-///
-/// See the [module level documentation](crate::rwlock) for more.
+/// [`OwnedRwLockReadGuard::map`](crate::rwlock::OwnedRwLockReadGuard::map) and
+/// [`OwnedRwLockReadGuard::filter_map`](crate::rwlock::OwnedRwLockReadGuard::filter_map) create
+/// this guard. It keeps the lock alive and its read access active while exposing only the projected
+/// component.
 ///
 /// # Examples
 ///
@@ -76,7 +60,7 @@ use crate::rwlock::RwLock;
 /// assert_eq!(profile_guard.email, "user@example.com");
 /// # }
 /// ```
-#[must_use = "if unused the RwLock will immediately unlock"]
+#[must_use = "dropping the guard releases its read access immediately"]
 pub struct OwnedMappedRwLockReadGuard<T: ?Sized, U: ?Sized> {
     // This Arc acts as an ownership certificate, ensuring the RwLock remains valid
     // and the lock is not released
@@ -132,15 +116,10 @@ impl<T: ?Sized, U: ?Sized> Deref for OwnedMappedRwLockReadGuard<T, U> {
 }
 
 impl<T: ?Sized, U: ?Sized> OwnedMappedRwLockReadGuard<T, U> {
-    /// Makes a new [`OwnedMappedRwLockReadGuard`] for a component of the locked data.
+    /// Projects this guard to a deeper shared component.
     ///
-    /// This operation cannot fail as the `OwnedMappedRwLockReadGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `OwnedMappedRwLockReadGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The returned guard keeps the same read access active. Call this as
+    /// `OwnedMappedRwLockReadGuard::map(...)` so a method named `map` on `U` remains accessible.
     ///
     /// # Examples
     ///
@@ -202,16 +181,11 @@ impl<T: ?Sized, U: ?Sized> OwnedMappedRwLockReadGuard<T, U> {
         OwnedMappedRwLockReadGuard::new(d, lock)
     }
 
-    /// Attempts to make a new [`OwnedMappedRwLockReadGuard`] for a component of the locked data.
-    /// The original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `OwnedMappedRwLockReadGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `OwnedMappedRwLockReadGuard::filter_map(...)`.
+    /// Attempts to project this guard to a deeper shared component.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `OwnedMappedRwLockReadGuard::filter_map(...)` so a method with the same name on `U` remains
+    /// accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/rwlock/owned_mapped_write_guard.rs b/asyncband/src/rwlock/owned_mapped_write_guard.rs
index 585b80a..5734494 100644
--- a/asyncband/src/rwlock/owned_mapped_write_guard.rs
+++ b/asyncband/src/rwlock/owned_mapped_write_guard.rs
@@ -16,28 +16,12 @@ use std::sync::Arc;
 use crate::rwlock::OwnedMappedRwLockReadGuard;
 use crate::rwlock::RwLock;
 
-/// Owned RAII structure used to release the exclusive write access of a lock when dropped, for a
-/// mapped component of the locked data.
+/// An owned write guard projected to one component of the protected value.
 ///
-/// This guard is only available from a [`RwLock`] that is wrapped in an [`Arc`]. It is similar to
-/// [`MappedRwLockWriteGuard`], except that rather than borrowing the `RwLock`, it clones the `Arc`,
-/// incrementing the reference count. This means that unlike `MappedRwLockWriteGuard`, it will have
-/// the `'static` lifetime.
-///
-/// As long as you have this guard, you have exclusive write access to the underlying `T`. The guard
-/// internally keeps an `Arc` reference to the original rwlock and tracks the number of permits
-/// acquired, so the original lock is maintained until this guard is dropped.
-///
-/// `OwnedMappedRwLockWriteGuard` implements [`Send`] and [`Sync`]
-/// when the underlying data type supports these traits, allowing it to be used across task
-/// boundaries and shared between threads safely.
-///
-/// [`map`]: crate::rwlock::OwnedRwLockWriteGuard::map
-/// [`filter_map`]: crate::rwlock::OwnedRwLockWriteGuard::filter_map
-/// [`OwnedRwLockWriteGuard`]: crate::rwlock::OwnedRwLockWriteGuard
-/// [`MappedRwLockWriteGuard`]: crate::rwlock::MappedRwLockWriteGuard
-///
-/// See the [module level documentation](crate::rwlock) for more.
+/// [`OwnedRwLockWriteGuard::map`](crate::rwlock::OwnedRwLockWriteGuard::map) and
+/// [`OwnedRwLockWriteGuard::filter_map`](crate::rwlock::OwnedRwLockWriteGuard::filter_map) create
+/// this guard. It keeps the lock alive and its write access active while exposing only the
+/// projected component.
 ///
 /// # Examples
 ///
@@ -78,24 +62,7 @@ use crate::rwlock::RwLock;
 /// assert_eq!(profile_guard.email, "newemail@example.com");
 /// # }
 /// ```
-///
-/// # Variance
-///
-/// The guard is invariant over its mapped type `U`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::rwlock::OwnedMappedRwLockWriteGuard;
-///
-/// fn shorten<'short>(
-///     guard: OwnedMappedRwLockWriteGuard<(), &'static str>,
-///     value: &'short str,
-/// ) -> OwnedMappedRwLockWriteGuard<(), &'short str> {
-///     let mut guard: OwnedMappedRwLockWriteGuard<(), &'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the RwLock will immediately unlock"]
+#[must_use = "dropping the guard releases its write access immediately"]
 pub struct OwnedMappedRwLockWriteGuard<T: ?Sized, U: ?Sized> {
     d: NonNull<U>,
     lock: Arc<RwLock<T>>,
@@ -158,15 +125,10 @@ impl<T: ?Sized, U: ?Sized> DerefMut for OwnedMappedRwLockWriteGuard<T, U> {
 }
 
 impl<T: ?Sized, U: ?Sized> OwnedMappedRwLockWriteGuard<T, U> {
-    /// Makes a new [`OwnedMappedRwLockWriteGuard`] for a component of the locked data.
-    ///
-    /// This operation cannot fail as the `OwnedMappedRwLockWriteGuard` passed in already locked the
-    /// rwlock.
+    /// Projects this guard to a deeper mutable component.
     ///
-    /// This is an associated function that needs to be used as
-    /// `OwnedMappedRwLockWriteGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The returned guard keeps the same write access active. Call this as
+    /// `OwnedMappedRwLockWriteGuard::map(...)` so a method named `map` on `U` remains accessible.
     ///
     /// # Examples
     ///
@@ -231,16 +193,11 @@ impl<T: ?Sized, U: ?Sized> OwnedMappedRwLockWriteGuard<T, U> {
         OwnedMappedRwLockWriteGuard::new(d, lock, permits_acquired)
     }
 
-    /// Attempts to make a new [`OwnedMappedRwLockWriteGuard`] for a component of the locked data.
-    /// The original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `OwnedMappedRwLockWriteGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `OwnedMappedRwLockWriteGuard::filter_map(...)`.
+    /// Attempts to project this guard to a deeper mutable component.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `OwnedMappedRwLockWriteGuard::filter_map(...)` so a method with the same name on `U` remains
+    /// accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/rwlock/owned_read_guard.rs b/asyncband/src/rwlock/owned_read_guard.rs
index 00b8d89..5c2d1be 100644
--- a/asyncband/src/rwlock/owned_read_guard.rs
+++ b/asyncband/src/rwlock/owned_read_guard.rs
@@ -13,28 +13,18 @@ use crate::rwlock::OwnedMappedRwLockReadGuard;
 use crate::rwlock::RwLock;
 
 impl<T: ?Sized> RwLock<T> {
-    /// Locks this `RwLock` with shared read access, causing the current task to yield until the
-    /// lock has been acquired.
+    /// Waits for shared read access and returns a guard that owns this [`Arc`].
     ///
-    /// The calling task will yield until there are no writers which hold the lock. There may be
-    /// other readers inside the lock when the task resumes.
+    /// Other readers may hold the lock at the same time. Owning the `Arc` lets the guard be moved
+    /// wherever a `'static` value is required.
     ///
-    /// This method is identical to [`RwLock::read`], except that the returned guard references the
-    /// `RwLock` with an [`Arc`] rather than by borrowing it. Therefore, the `RwLock` must be
-    /// wrapped in an `Arc` to call this method, and the guard will live for the `'static` lifetime,
-    /// as it keeps the `RwLock` alive by holding an `Arc`.
-    ///
-    /// Note that under the priority policy of [`RwLock`], read locks are not granted until prior
-    /// write locks, to prevent starvation. Therefore, deadlock may occur if a read lock is held
-    /// by the current task, a write lock attempt is made, and then a subsequent read lock attempt
-    /// is made by the current task.
-    ///
-    /// Returns an RAII guard which will drop this read access of the `RwLock` when dropped.
+    /// A writer already waiting ahead of this request must acquire and release the lock first.
+    /// Holding a read guard, queuing a write request, and then waiting for another read guard in
+    /// the same task can therefore deadlock.
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute locks in the order they were requested.
-    /// Cancelling a call to `read_owned` makes you lose your place in the queue.
+    /// Pending lock requests complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
@@ -65,15 +55,9 @@ impl<T: ?Sized> RwLock<T> {
         OwnedRwLockReadGuard { lock: self }
     }
 
-    /// Attempts to acquire this `RwLock` with shared read access.
+    /// Acquires shared read access without waiting and returns a guard that owns this [`Arc`].
     ///
-    /// If the access couldn't be acquired immediately, returns `None`. Otherwise, an RAII guard is
-    /// returned which will release read access when dropped.
-    ///
-    /// This method is identical to [`RwLock::try_read`], except that the returned guard references
-    /// the `RwLock` with an [`Arc`] rather than by borrowing it. Therefore, the `RwLock` must
-    /// be wrapped in an `Arc` to call this method, and the guard will live for the `'static`
-    /// lifetime, as it keeps the `RwLock` alive by holding an `Arc`.
+    /// Returns `None` if read access is unavailable.
     ///
     /// # Examples
     ///
@@ -100,12 +84,11 @@ impl<T: ?Sized> RwLock<T> {
     }
 }
 
-/// Owned RAII structure used to release the shared read access of a lock when dropped.
-///
-/// This structure is created by the [`RwLock::read`] method.
+/// An owned guard that provides shared access to a [`RwLock`]'s value.
 ///
-/// See the [module level documentation](crate::rwlock) for more.
-#[must_use = "if unused the RwLock will immediately unlock"]
+/// [`RwLock::read_owned`] and [`RwLock::try_read_owned`] create this guard. It keeps the lock alive
+/// without borrowing it and releases this reader's access when dropped.
+#[must_use = "dropping the guard releases its read access immediately"]
 pub struct OwnedRwLockReadGuard<T: ?Sized> {
     pub(super) lock: Arc<RwLock<T>>,
 }
@@ -136,15 +119,10 @@ impl<T: ?Sized> Deref for OwnedRwLockReadGuard<T> {
 }
 
 impl<T: ?Sized> OwnedRwLockReadGuard<T> {
-    /// Makes a new [`OwnedMappedRwLockReadGuard`] for a component of the locked
-    /// data.
+    /// Projects this guard to a shared component of the protected value.
     ///
-    /// This operation cannot fail as the `OwnedRwLockReadGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as `OwnedRwLockReadGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// Call this as `OwnedRwLockReadGuard::map(...)` so a method named `map` on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
@@ -190,16 +168,11 @@ impl<T: ?Sized> OwnedRwLockReadGuard<T> {
         OwnedMappedRwLockReadGuard::new(d, lock)
     }
 
-    /// Attempts to make a new [`OwnedMappedRwLockReadGuard`] for a component of the locked data.
-    /// The original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `OwnedRwLockReadGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `OwnedRwLockReadGuard::filter_map(...)`.
+    /// Attempts to project this guard to a shared component of the protected value.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `OwnedRwLockReadGuard::filter_map(...)` so a method with the same name on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/rwlock/owned_write_guard.rs b/asyncband/src/rwlock/owned_write_guard.rs
index fd183da..8d7f558 100644
--- a/asyncband/src/rwlock/owned_write_guard.rs
+++ b/asyncband/src/rwlock/owned_write_guard.rs
@@ -17,23 +17,13 @@ use crate::rwlock::OwnedRwLockReadGuard;
 use crate::rwlock::RwLock;
 
 impl<T: ?Sized> RwLock<T> {
-    /// Locks this `RwLock` with exclusive write access, causing the current task to yield until the
-    /// lock has been acquired.
+    /// Waits for exclusive write access and returns a guard that owns this [`Arc`].
     ///
-    /// The calling task will yield while other writers or readers currently have access to the
-    /// lock.
-    ///
-    /// This method is identical to [`RwLock::write`], except that the returned guard references the
-    /// `RwLock` with an [`Arc`] rather than by borrowing it. Therefore, the `RwLock` must be
-    /// wrapped in an `Arc` to call this method, and the guard will live for the `'static` lifetime,
-    /// as it keeps the `RwLock` alive by holding an `Arc`.
-    ///
-    /// Returns an RAII guard which will drop the write access of this `RwLock` when dropped.
+    /// Owning the `Arc` lets the guard be moved wherever a `'static` value is required.
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute locks in the order they were requested.
-    /// Cancelling a call to `write_owned` makes you lose your place in the queue.
+    /// Pending lock requests complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
@@ -57,15 +47,9 @@ impl<T: ?Sized> RwLock<T> {
         }
     }
 
-    /// Attempts to acquire this `RwLock` with exclusive write access.
-    ///
-    /// If the access couldn't be acquired immediately, returns `None`. Otherwise, an RAII guard is
-    /// returned which will release write access when dropped.
+    /// Acquires exclusive write access without waiting and returns a guard that owns this [`Arc`].
     ///
-    /// This method is identical to [`RwLock::try_write`], except that the returned guard references
-    /// the `RwLock` with an [`Arc`] rather than by borrowing it. Therefore, the `RwLock` must
-    /// be wrapped in an `Arc` to call this method, and the guard will live for the `'static`
-    /// lifetime, as it keeps the `RwLock` alive by holding an `Arc`.
+    /// Returns `None` if write access is unavailable.
     ///
     /// # Examples
     ///
@@ -95,29 +79,11 @@ impl<T: ?Sized> RwLock<T> {
     }
 }
 
-/// Owned RAII structure used to release the exclusive write access of a lock when dropped.
-///
-/// This structure is created by the [`RwLock::write`] method.
-///
-/// See the [module level documentation](crate::rwlock) for more.
-///
-/// # Variance
+/// An owned guard that provides exclusive access to a [`RwLock`]'s value.
 ///
-/// The guard is invariant over `T`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::rwlock::OwnedRwLockWriteGuard;
-///
-/// fn shorten<'short>(
-///     guard: OwnedRwLockWriteGuard<&'static str>,
-///     value: &'short str,
-/// ) -> OwnedRwLockWriteGuard<&'short str> {
-///     let mut guard: OwnedRwLockWriteGuard<&'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the RwLock will immediately unlock"]
+/// [`RwLock::write_owned`] and [`RwLock::try_write_owned`] create this guard. It keeps the lock
+/// alive without borrowing it and releases the lock when dropped.
+#[must_use = "dropping the guard releases its write access immediately"]
 pub struct OwnedRwLockWriteGuard<T: ?Sized> {
     pub(super) permits_acquired: usize,
     pub(super) lock: Arc<RwLock<T>>,
@@ -158,15 +124,10 @@ impl<T: ?Sized> DerefMut for OwnedRwLockWriteGuard<T> {
 }
 
 impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
-    /// Makes a new [`OwnedMappedRwLockWriteGuard`] for a component of the locked
-    /// data.
-    ///
-    /// This operation cannot fail as the `OwnedRwLockWriteGuard` passed in already locked the
-    /// rwlock.
+    /// Projects this guard to a mutable component of the protected value.
     ///
-    /// This is an associated function that needs to be used as `OwnedRwLockWriteGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// Call this as `OwnedRwLockWriteGuard::map(...)` so a method named `map` on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
@@ -215,16 +176,11 @@ impl<T: ?Sized> OwnedRwLockWriteGuard<T> {
         OwnedMappedRwLockWriteGuard::new(d, lock, permits_acquired)
     }
 
-    /// Attempts to make a new [`OwnedMappedRwLockWriteGuard`] for a component of the
-    /// locked data. The original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `OwnedRwLockWriteGuard` passed in already locked the
-    /// rwlock.
-    ///
-    /// This is an associated function that needs to be used as
-    /// `OwnedRwLockWriteGuard::filter_map(...)`.
+    /// Attempts to project this guard to a mutable component of the protected value.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `OwnedRwLockWriteGuard::filter_map(...)` so a method with the same name on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/rwlock/read_guard.rs b/asyncband/src/rwlock/read_guard.rs
index 7400685..48739cf 100644
--- a/asyncband/src/rwlock/read_guard.rs
+++ b/asyncband/src/rwlock/read_guard.rs
@@ -14,23 +14,17 @@ use crate::rwlock::MappedRwLockReadGuard;
 use crate::rwlock::RwLock;
 
 impl<T: ?Sized> RwLock<T> {
-    /// Locks this `RwLock` with shared read access, causing the current task to yield until the
-    /// lock has been acquired.
+    /// Waits for shared read access and returns a borrowed guard.
     ///
-    /// The calling task will yield until there are no writers which hold the lock. There may be
-    /// other readers inside the lock when the task resumes.
+    /// Other readers may hold the lock at the same time. A writer already waiting ahead of this
+    /// request must acquire and release the lock first.
     ///
-    /// Note that under the priority policy of [`RwLock`], read locks are not granted until prior
-    /// write locks, to prevent starvation. Therefore, deadlock may occur if a read lock is held
-    /// by the current task, a write lock attempt is made, and then a subsequent read lock attempt
-    /// is made by the current task.
-    ///
-    /// Returns an RAII guard which will drop this read access of the `RwLock` when dropped.
+    /// Holding a read guard, queuing a write request, and then waiting for another read guard in
+    /// the same task can therefore deadlock.
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute locks in the order they were requested.
-    /// Cancelling a call to `read` makes you lose your place in the queue.
+    /// Pending lock requests complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
@@ -61,10 +55,7 @@ impl<T: ?Sized> RwLock<T> {
         RwLockReadGuard { lock: self }
     }
 
-    /// Attempts to acquire this `RwLock` with shared read access.
-    ///
-    /// If the access couldn't be acquired immediately, returns `None`. Otherwise, an RAII guard is
-    /// returned which will release read access when dropped.
+    /// Acquires shared read access without waiting, or returns `None` if it is unavailable.
     ///
     /// # Examples
     ///
@@ -91,12 +82,11 @@ impl<T: ?Sized> RwLock<T> {
     }
 }
 
-/// RAII structure used to release the shared read access of a lock when dropped.
+/// A borrowed guard that provides shared access to a [`RwLock`]'s value.
 ///
-/// This structure is created by the [`RwLock::read`] method.
-///
-/// See the [module level documentation](crate::rwlock) for more.
-#[must_use = "if unused the RwLock will immediately unlock"]
+/// [`RwLock::read`] and [`RwLock::try_read`] create this guard. Dropping it releases this reader's
+/// access.
+#[must_use = "dropping the guard releases its read access immediately"]
 pub struct RwLockReadGuard<'a, T: ?Sized> {
     pub(super) lock: &'a RwLock<T>,
 }
@@ -130,13 +120,9 @@ impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
 }
 
 impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
-    /// Makes a new [`MappedRwLockReadGuard`] for a component of the locked data.
-    ///
-    /// This operation cannot fail as the `RwLockReadGuard` passed in already locked the rwlock.
+    /// Projects this guard to a shared component of the protected value.
     ///
-    /// This is an associated function that needs to be used as `RwLockReadGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// Call this as `RwLockReadGuard::map(...)` so a method named `map` on `T` remains accessible.
     ///
     /// # Examples
     ///
@@ -167,14 +153,10 @@ impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
         MappedRwLockReadGuard::new(d, &orig.lock.s)
     }
 
-    /// Attempts to make a new [`MappedRwLockReadGuard`] for a component of the
-    /// locked data. The original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `RwLockReadGuard` passed in already locked the rwlock.
-    ///
-    /// This is an associated function that needs to be used as `RwLockReadGuard::filter_map(...)`.
+    /// Attempts to project this guard to a shared component of the protected value.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `RwLockReadGuard::filter_map(...)` so a method with the same name on `T` remains accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/rwlock/write_guard.rs b/asyncband/src/rwlock/write_guard.rs
index de29337..896ed60 100644
--- a/asyncband/src/rwlock/write_guard.rs
+++ b/asyncband/src/rwlock/write_guard.rs
@@ -16,18 +16,11 @@ use crate::rwlock::RwLock;
 use crate::rwlock::RwLockReadGuard;
 
 impl<T: ?Sized> RwLock<T> {
-    /// Locks this `RwLock` with exclusive write access, causing the current task to yield until the
-    /// lock has been acquired.
-    ///
-    /// The calling task will yield while other writers or readers currently have access to the
-    /// lock.
-    ///
-    /// Returns an RAII guard which will drop the write access of this `RwLock` when dropped.
+    /// Waits for exclusive write access and returns a borrowed guard.
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute locks in the order they were requested.
-    /// Cancelling a call to `write` makes you lose your place in the queue.
+    /// Pending lock requests complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
@@ -49,10 +42,7 @@ impl<T: ?Sized> RwLock<T> {
         }
     }
 
-    /// Attempts to acquire this `RwLock` with exclusive write access.
-    ///
-    /// If the access couldn't be acquired immediately, returns `None`. Otherwise, an RAII guard is
-    /// returned which will release write access when dropped.
+    /// Acquires exclusive write access without waiting, or returns `None` if it is unavailable.
     ///
     /// # Examples
     ///
@@ -82,29 +72,10 @@ impl<T: ?Sized> RwLock<T> {
     }
 }
 
-/// RAII structure used to release the exclusive write access of a lock when dropped.
-///
-/// This structure is created by the [`RwLock::write`] method.
-///
-/// See the [module level documentation](crate::rwlock) for more.
-///
-/// # Variance
+/// A borrowed guard that provides exclusive access to a [`RwLock`]'s value.
 ///
-/// The guard is invariant over `T`, as required for mutable access:
-///
-/// ```compile_fail
-/// use asyncband::rwlock::RwLockWriteGuard;
-///
-/// fn shorten<'lock, 'short: 'lock>(
-///     guard: RwLockWriteGuard<'lock, &'static str>,
-///     value: &'short str,
-/// ) -> RwLockWriteGuard<'lock, &'short str> {
-///     let mut guard: RwLockWriteGuard<'lock, &'short str> = guard;
-///     *guard = value;
-///     guard
-/// }
-/// ```
-#[must_use = "if unused the RwLock will immediately unlock"]
+/// [`RwLock::write`] and [`RwLock::try_write`] create this guard. Dropping it releases the lock.
+#[must_use = "dropping the guard releases its write access immediately"]
 pub struct RwLockWriteGuard<'a, T: ?Sized> {
     pub(super) permits_acquired: usize,
     pub(super) lock: &'a RwLock<T>,
@@ -145,13 +116,9 @@ impl<T: ?Sized> DerefMut for RwLockWriteGuard<'_, T> {
 }
 
 impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
-    /// Makes a new [`MappedRwLockWriteGuard`] for a component of the locked data.
-    ///
-    /// This operation cannot fail as the `RwLockWriteGuard` passed in already locked the rwlock.
+    /// Projects this guard to a mutable component of the protected value.
     ///
-    /// This is an associated function that needs to be used as `RwLockWriteGuard::map(...)`.
-    ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// Call this as `RwLockWriteGuard::map(...)` so a method named `map` on `T` remains accessible.
     ///
     /// # Examples
     ///
@@ -190,14 +157,11 @@ impl<'a, T: ?Sized> RwLockWriteGuard<'a, T> {
         MappedRwLockWriteGuard::new(d, &orig.lock.s, permits_acquired)
     }
 
-    /// Attempts to make a new [`MappedRwLockWriteGuard`] for a component of the
-    /// locked data. The original guard is returned if the closure returns `None`.
-    ///
-    /// This operation cannot fail as the `RwLockWriteGuard` passed in already locked the rwlock.
-    ///
-    /// This is an associated function that needs to be used as `RwLockWriteGuard::filter_map(...)`.
+    /// Attempts to project this guard to a mutable component of the protected value.
     ///
-    /// A method would interfere with methods of the same name on the contents of the locked data.
+    /// The original guard is returned when `f` returns `None`. Call this as
+    /// `RwLockWriteGuard::filter_map(...)` so a method with the same name on `T` remains
+    /// accessible.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/semaphore/mod.rs b/asyncband/src/semaphore/mod.rs
index 8ce06e5..2910fa7 100644
--- a/asyncband/src/semaphore/mod.rs
+++ b/asyncband/src/semaphore/mod.rs
@@ -155,9 +155,9 @@ impl Semaphore {
     ///
     /// # Panics
     ///
-    /// Panics if adding the permits would cause the total number of permits to overflow or if a
-    /// registered waker panics while being notified. Added permits remain committed, and every
-    /// remaining registered waker is still notified before the first panic resumes.
+    /// Panics if adding the permits would overflow the total permit count, or if notifying a waiter
+    /// panics. Added permits remain available, and notification is still attempted for every other
+    /// eligible waiter before the panic resumes.
     ///
     /// # Examples
     ///
@@ -213,8 +213,7 @@ impl Semaphore {
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute permits in the order they were requested.
-    /// Cancelling a call to `acquire` makes you lose your place in the queue.
+    /// Pending acquisitions complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
@@ -291,8 +290,7 @@ impl Semaphore {
     ///
     /// # Cancel safety
     ///
-    /// This method uses a queue to fairly distribute permits in the order they were requested.
-    /// Cancelling a call to `acquire_owned` makes you lose your place in the queue.
+    /// Pending acquisitions complete in order. Cancelling this call loses its place among them.
     ///
     /// # Examples
     ///
diff --git a/asyncband/src/watch/mod.rs b/asyncband/src/watch/mod.rs
index 53accee..54b770a 100644
--- a/asyncband/src/watch/mod.rs
+++ b/asyncband/src/watch/mod.rs
@@ -166,7 +166,7 @@ impl<T> Sender<T> {
     ///
     /// # Panics
     ///
-    /// Panics if the internal version counter overflows.
+    /// Panics if the channel has already published `u64::MAX` updates.
     pub fn send(&self, value: T) -> Result<(), SendError<T>> {
         let (wakers, replaced) = {
             let mut state = self.shared.state.lock();
@@ -195,7 +195,7 @@ impl<T> Sender<T> {
     ///
     /// # Panics
     ///
-    /// Panics if the internal version counter overflows.
+    /// Panics if the channel has already published `u64::MAX` updates.
     pub fn send_replace(&self, value: T) -> T {
         let (wakers, replaced) = {
             let mut state = self.shared.state.lock();
diff --git a/licenserc.toml b/licenserc.toml
index f004664..eec57c5 100644
--- a/licenserc.toml
+++ b/licenserc.toml
@@ -24,7 +24,6 @@ builtin = "Apache-2.0-ASF"
 excludes = [
   "asyncband/src/blocking/executor.rs",
   "asyncband/src/blocking/parker.rs",
-  "asyncband/src/mutex/mod.rs",
   "asyncband/src/pool/bounded.rs",
   "asyncband/src/pool/common.rs",
   "asyncband/src/pool/mod.rs",


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