Skip to content

Commit

Permalink
docs: add countdown latch example to Semaphore
Browse files Browse the repository at this point in the history
## Motivation

Some users have requested that `tokio::sync` add a countdown latch
synchronization primitive. This can be implemented using the existing
`Semaphore` API, so rather than adding a countdown latch type, we should
add documentation examples showing how the `Semaphore` can be used as a
countdown latch.

## Solution

This branch adds an example to the `tokio::sync::Semaphore` docs
demonstrating its usage as a countdown latch. This example was extracted
from #6087 (comment).

Closes #6087
  • Loading branch information
hawkw committed Oct 24, 2023
1 parent 58acb56 commit d801664
Showing 1 changed file with 71 additions and 1 deletion.
72 changes: 71 additions & 1 deletion tokio/src/sync/semaphore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ use std::sync::Arc;
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
/// async fn main() {
/// let capacity = 5;
/// let update_interval = Duration::from_secs_f32(1.0 / capacity as f32);
/// let update_interval = Duration::from_secs_f32(1.0 / capacity as f32);
/// let bucket = TokenBucket::new(update_interval, capacity);
///
/// for _ in 0..5 {
Expand All @@ -333,6 +333,76 @@ use std::sync::Arc;
/// }
/// ```
///
/// ## Countdown latch
///
/// A `Semaphore` may be used to implement a countdown latch, a synchronization
/// primitive where one task waits until a set number of other tasks to have
/// completed before continuing.
///
/// The task awaiting the countdown should call [`Semaphore::acquire_many`] to
/// acquire the a number of permits equal to the number of tasks which hold the
/// latch open. The tasks whose completion is awaited by the latch should call
/// [`Semaphore::add_permit`] with a single permit to increment the countdown
/// latch as they complete. For example:
///
/// ```rust
/// use tokio::sync::Semaphore;
/// use std::{future::Future, sync::Arc};
///
/// /// A token that holds a countdown latch open until it is dropped.
/// #[derive(Clone)]
/// pub struct Countdown(Arc<Semaphore>);
///
/// impl Drop for Countdown {
/// fn drop(&mut self) {
/// self.0.add_permits(1);
/// }
/// }
///
/// impl Countdown {
/// /// Returns a new countdown latch which completes when `n` tasks
/// /// have exited.
/// ///
/// /// This method returns a tuple of a `Countdown` token which
/// /// holds the latch open and incrememts the count of completed
/// /// tasks when it is dropped, and a `Future` which completes when
/// /// `n` clones of the `Countdown` token have been dropped.
/// pub fn latch(n: u32) -> (Self, impl Future + Send) {
/// let sem = Arc::new(Semaphore::new(0));
/// let latch = Self(sem.clone());
///
/// let wait = async move {
/// let _ = sem.acquire_many(n).await;
/// };
///
/// (latch, wait)
/// }
/// }
///
/// #[tokio::main]
/// async fn main() {
/// let (latch, wait) = Countdown::new(5);
/// for i in 1..=5 {
/// let latch = latch.clone();
/// tokio::spawn(async move {
/// // move the latch into the task.
/// let _latch = latch;
///
/// // do stuff...
/// println!("countdown task {i} running...");
/// tokio::task::yield_now().await;
///
/// // when the task completes, the latch is dropped.
/// println!("countdown task {i} done!");
/// });
/// }
///
/// println!("waiting for tasks to complete...");
/// wait.await;
/// println!("tasks completed!");
/// }
/// ```
///
/// [`PollSemaphore`]: https://docs.rs/tokio-util/latest/tokio_util/sync/struct.PollSemaphore.html
/// [`Semaphore::acquire_owned`]: crate::sync::Semaphore::acquire_owned
#[derive(Debug)]
Expand Down

0 comments on commit d801664

Please sign in to comment.