diff --git a/tokio/src/sync/semaphore.rs b/tokio/src/sync/semaphore.rs index c1d66a78a0b..1edbb156863 100644 --- a/tokio/src/sync/semaphore.rs +++ b/tokio/src/sync/semaphore.rs @@ -47,7 +47,7 @@ use std::sync::Arc; /// } /// ``` /// -/// Limit the number of simultaneously opened files in your program. +/// ## Limit the number of simultaneously opened files in your program. /// /// Most operating systems have limits on the number of open file /// handles. Even in systems without explicit limits, resource constraints @@ -76,7 +76,54 @@ use std::sync::Arc; /// } /// ``` /// -/// Implement a simple token bucket for rate limiting +/// ## Limit the number of incoming requests being handled at the same time. +/// +/// Similar to limiting the number of simultaneously opened files, network handles +/// are a limited resource. Allowing an unbounded amount of requests to be processed +/// could result in a denial-of-service, among many other issues. +/// +/// This example uses an `Arc` instead of a global variable. +/// To limit the number of requests that can be processed at the time, +/// we acquire a permit for each task before spawning it. Once acquired, +/// a new task is spawned; and once finished, the permit is dropped inside +/// of the task to allow others to spawn. Permits must be acquired via +/// [`Semaphore::acquire_owned`] to be movable across the task boundary. +/// (Since our semaphore is not a global variable — if it was, then `acquire` would be enough.) +/// +/// ```no_run +/// use std::sync::Arc; +/// use tokio::sync::Semaphore; +/// use tokio::net::TcpListener; +/// +/// #[tokio::main] +/// async fn main() -> std::io::Result<()> { +/// let semaphore = Arc::new(Semaphore::new(3)); +/// let listener = TcpListener::bind("127.0.0.1:8080").await?; +/// +/// loop { +/// // Acquire permit before accepting the next socket. +/// // +/// // We use `acquire_owned` so that we can move `permit` into +/// // other tasks. +/// let permit = semaphore.clone().acquire_owned().await.unwrap(); +/// let (mut socket, _) = listener.accept().await?; +/// +/// tokio::spawn(async move { +/// // Do work using the socket. +/// handle_connection(&mut socket).await; +/// // Drop socket while the permit is still live. +/// drop(socket); +/// // Drop the permit, so more tasks can be created. +/// drop(permit); +/// }); +/// } +/// } +/// # async fn handle_connection(_socket: &mut tokio::net::TcpStream) { +/// # // Do work +/// # } +/// ``` +/// +/// ## Rate limiting using a token bucket /// /// Many applications and systems have constraints on the rate at which certain /// operations should occur. Exceeding this rate can result in suboptimal @@ -159,53 +206,6 @@ use std::sync::Arc; /// } /// ``` /// -/// Limit the number of incoming requests being handled at the same time. -/// -/// Similar to limiting the number of simultaneously opened files, network handles -/// are a limited resource. Allowing an unbounded amount of requests to be processed -/// could result in a denial-of-service, among many other issues. -/// -/// This example uses an `Arc` instead of a global variable. -/// To limit the number of requests that can be processed at the time, -/// we acquire a permit for each task before spawning it. Once acquired, -/// a new task is spawned; and once finished, the permit is dropped inside -/// of the task to allow others to spawn. Permits must be acquired via -/// [`Semaphore::acquire_owned`] to be movable across the task boundary. -/// (Since our semaphore is not a global variable — if it was, then `acquire` would be enough.) -/// -/// ```no_run -/// use std::sync::Arc; -/// use tokio::sync::Semaphore; -/// use tokio::net::TcpListener; -/// -/// #[tokio::main] -/// async fn main() -> std::io::Result<()> { -/// let semaphore = Arc::new(Semaphore::new(3)); -/// let listener = TcpListener::bind("127.0.0.1:8080").await?; -/// -/// loop { -/// // Acquire permit before accepting the next socket. -/// // -/// // We use `acquire_owned` so that we can move `permit` into -/// // other tasks. -/// let permit = semaphore.clone().acquire_owned().await.unwrap(); -/// let (mut socket, _) = listener.accept().await?; -/// -/// tokio::spawn(async move { -/// // Do work using the socket. -/// handle_connection(&mut socket).await; -/// // Drop socket while the permit is still live. -/// drop(socket); -/// // Drop the permit, so more tasks can be created. -/// drop(permit); -/// }); -/// } -/// } -/// # async fn handle_connection(_socket: &mut tokio::net::TcpStream) { -/// # // Do work -/// # } -/// ``` -/// /// [`PollSemaphore`]: https://docs.rs/tokio-util/latest/tokio_util/sync/struct.PollSemaphore.html /// [`Semaphore::acquire_owned`]: crate::sync::Semaphore::acquire_owned #[derive(Debug)]