Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ Decision: [#257](https://github.com/apache/asyncband/pull/257).

Keep each Markdown prose paragraph and list item on one source line. Format Markdown tables so their columns and separators align in the source. Keep skill resource links within the skill directory; reference other skills or agents by name and resolve repository paths from the supplied repository root.

Write public API documentation from Asyncband's observable contracts. Start with a short action or query summary, then describe waiting conditions, ordering, ownership, and state changes relevant to callers. Keep implementation details out unless callers need them to use the API correctly.

Use applicable sections in this order: `Returns`, `Errors`, `Cancellation`, `Panics`, `Deadlocks`, and `Examples`. Omit empty or redundant sections. Explain simple return values in the summary or body; reserve `Returns` for nontrivial outcomes, snapshots, or resource ownership. Document `Option::None` and boolean outcomes as return semantics, not errors. For `Result`, use `Errors` to explain failure conditions, returned ownership, and any state changes. Keep examples last and focused on useful behavior rather than mechanically demonstrating syntax.

Document cancellation for public async operations, methods returning futures, and public `Future` or `IntoFuture` types. Explain when effects occur, what dropping a pending future retains or releases, and whether retrying restarts, resumes, or repeats an effect. Distinguish cancellation of active work from cancellation of an observer, and state when caller-owned inputs are dropped or external side effects are not rolled back. Do not use an unqualified cancellation-safety claim in place of these observable guarantees. Owned variants should include a `Cancellation` section and may link to shared semantics while explaining ownership differences.

Put concrete panic conditions and important unwind effects in `Panics`, and recursive acquisition or callback reentrancy hazards in `Deadlocks`. Do not enumerate arbitrary panics in caller-provided code unless their effect on the primitive matters. Apply the same conventions to public trait methods. Preserve existing provenance and license notices independently of documentation rewrites.

## Changelog

- Update `CHANGELOG.md` for significant user-visible changes by comparing the final behavior with the latest release tag, not by recording the sequence of commits in the current development cycle.
Expand Down
29 changes: 13 additions & 16 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -274,25 +274,22 @@ 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.

Portions of the following files originated from Tokio 1.47.0's OnceCell. Each
local path is followed by its upstream source path:

asyncband/src/once/once_cell/mod.rs
tokio/src/sync/once_cell.rs
asyncband/src/internal/value_cell.rs
tokio/src/sync/once_cell.rs

The exact Tokio 1.47.0 source is:
Portions of asyncband/src/once/once_cell/mod.rs originated from Tokio 1.47.0's
OnceCell:

https://github.com/tokio-rs/tokio/blob/3911cb8523f190142f61c64b66881c07c0d3e7be/tokio/src/sync/once_cell.rs

Asyncband substantially changed this implementation. OnceCell uses a permit
only to serialize initialization and does not close its semaphore when a value
is published. It can remove a value and later initialize the cell again, and
its set operation waits for an in-progress initializer instead of reporting a
closed or initializing state. ValueCell factors storage out of OnceCell,
contains no semaphore or closed-state invariant, supports mutation, removal,
and reinitialization, and is shared by OnceCell and LazyCell.
Asyncband substantially changed this implementation. OnceCell delegates value
storage to Asyncband's own ValueCell, uses permits only to serialize
initialization, and does not close its semaphore when a value is published. It
can remove a value and later initialize the cell again, and its set operation
waits for an in-progress initializer instead of reporting a closed or
initializing state.

The standard ASF source header on asyncband/src/once/once_cell/mod.rs applies
to 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 originated from Tokio 1.42.0's public and
internal semaphore implementations:
Expand Down
54 changes: 34 additions & 20 deletions asyncband/src/barrier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,34 @@
//! # #[tokio::main]
//! # async fn main() {
//! use std::sync::Arc;
//! use std::sync::atomic::AtomicUsize;
//! use std::sync::atomic::Ordering;
//!
//! use asyncband::barrier::Barrier;
//!
//! let barrier = Arc::new(Barrier::new(3));
//! let mut tasks = vec![];
//! let stage = Arc::new([const { AtomicUsize::new(0) }; 3]);
//! let mut workers = Vec::with_capacity(3);
//!
//! for _ in 0..3 {
//! for slot in 0..3 {
//! let barrier = barrier.clone();
//! let task = tokio::spawn(async move { barrier.wait().await.is_leader() });
//! tasks.push(task);
//! let stage = stage.clone();
//! workers.push(tokio::spawn(async move {
//! stage[slot].store(slot + 1, Ordering::Release);
//! let result = barrier.wait().await;
//! let sum: usize = stage.iter().map(|v| v.load(Ordering::Acquire)).sum();
//! assert_eq!(sum, 6);
//! result.is_leader()
//! }));
//! }
//!
//! let mut leaders = 0;
//! for task in tasks {
//! leaders += usize::from(task.await.unwrap());
//! let mut leader_count = 0;
//! for worker in workers {
//! if worker.await.unwrap() {
//! leader_count += 1;
//! }
//! }
//! assert_eq!(leaders, 1);
//! assert_eq!(leader_count, 1);
//! # }
//! ```

Expand All @@ -60,7 +71,7 @@ use crate::internal::wakerset::WakerToken;
#[cfg(test)]
mod tests;

/// A synchronization primitive for multiple tasks that need to wait for each other.
/// A reusable rendezvous point that releases tasks in fixed-size generations.
///
/// See the [module level documentation](self) for more.
#[derive(Debug)]
Expand Down Expand Up @@ -150,26 +161,29 @@ impl Barrier {
}
}

/// Waits for all tasks to reach this point.
/// Arrives at the barrier and waits until all `n` participants in the current generation have
/// arrived.
///
/// The barrier holds the current task until all `n` participants have arrived. The final
/// participant is designated as the leader for this generation.
/// The final participant to arrive completes the generation, wakes the waiting participants,
/// and is designated as that generation's leader.
///
/// # Cancel safety
/// # Returns
///
/// This method is not cancellation safe.
/// Returns a [`BarrierWaitResult`] identifying whether this call completed the generation
/// and was selected as its leader.
///
/// # Cancellation
///
/// An arrival is recorded when the future returned by `wait` is first polled. Once recorded,
/// the arrival is not retracted if that future is dropped before the barrier completes.
/// Cancellation therefore stops that caller from observing completion, but it does not make the
/// caller leave the current barrier generation.
///
/// Calling `wait` again after canceling a pending call records another arrival. Repeatedly
/// canceling and retrying can therefore cause a generation to complete with fewer than `n` live
/// wait futures. Callers that need to keep waiting after another operation completes first
/// should retain and continue polling the same `wait` future instead of creating a new one.
///
/// Returns a [`BarrierWaitResult`] that identifies whether this participant is the leader.
/// Calling `wait` again after cancelling a pending call records another arrival. Repeatedly
/// cancelling and retrying can therefore cause a generation to complete with fewer than `n`
/// live wait futures. Callers that need to keep waiting after another operation completes
/// first should retain and continue polling the same `wait` future instead of creating a
/// new one.
///
/// # Examples
///
Expand Down
16 changes: 13 additions & 3 deletions asyncband/src/blocking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ use std::time::Duration;

/// Extension methods for waiting on a future from synchronous code.
///
/// # Example
/// # Examples
///
/// ```
/// use std::time::Duration;
Expand All @@ -87,12 +87,22 @@ pub trait FutureExt: IntoFuture {
/// Blocks the current thread until this future is ready or `timeout` elapses.
///
/// The future is polled before checking the timeout, so an immediately ready future succeeds
/// even when `timeout` is zero. Returns [`None`] on timeout and drops the future, cancelling it
/// according to that future's cancellation semantics.
/// even when `timeout` is zero.
///
/// The timeout cannot interrupt a call to [`Future::poll`]. See the
/// [`blocking`](crate::blocking) module documentation for other runtime limitations and
/// executor-thread caveats.
///
/// # Returns
///
/// Returns `Some(output)` if the future completes, or `None` when the timeout is observed
/// while the future is pending.
///
/// # Cancellation
///
/// On timeout, the supplied future is dropped. Its own cancellation contract determines which
/// effects remain. Passing a mutable reference to a retained future allows that future to be
/// polled again after the timeout.
fn wait_timeout(self, timeout: Duration) -> Option<Self::Output>
where
Self: Sized,
Expand Down
Loading
Loading