Repository: tokio-rs/tokio
File: tokio/src/sync/semaphore.rs (Rust)

Code before:
///
/// Basic usage:
///
/// ```
/// use tokio::sync::{Semaphore, TryAcquireError};
///
/// #[tokio::main]
/// async fn main() {
///     let semaphore = Semaphore::new(3);
///
///     let a_permit = semaphore.acquire().await.unwrap();
///     let two_permits = semaphore.acquire_many(2).await.unwrap();
///
///     assert_eq!(semaphore.available_permits(), 0);
///
///     let permit_attempt = semaphore.try_acquire();
///     assert_eq!(permit_attempt.err(), Some(TryAcquireError::NoPermits));
/// }
/// ```
///
/// Limit number of incoming requests being handled at the same time, using a task-safe semaphore
/// via [`Semaphore::acquire_owned`].
///
/// Similar to limiting the numer of simultaneous opened files, network handles are a limited resource.
/// Allowing an unbounded amount of requests to be processed could result in a denial-of-service,
/// and many other issues.
///
/// However, in contrast to the file example, this example uses a non-static, `Arc`-wrapped `Semaphore`
/// for more fine-grained lifetime and ownership control. The `Arc` allows multiple threads and tasks
/// to share ownership of the semaphore. Here we create a shallow-copy of the `Arc<Semaphore>` reference
/// using `clone`. Then we acquire a permit through the `Arc` from the `Semaphore` via [`Semaphore::acquire_owned`],
/// and move it inside the task. This ensures no non-`'static` variables are referenced from within
/// said task.
///
/// If we leave the scope where the `Arc<Semaphore>` was defined, and references still exist in any
/// of our tasks to it, the `Semaphore` will continue to exist in a static-like context, until all
/// `Arc`s have been dropped.
///
/// ```
/// use std::sync::Arc;
/// use tokio::sync::Semaphore;
///
/// #[tokio::main]
/// async fn main() {
///     let semaphore = Arc::new(Semaphore::new(3));
///     let mut join_handles = Vec::new();
///
///     for _ in 0..5 {
///         let permit = semaphore.clone().acquire_owned().await.unwrap();
///         join_handles.push(tokio::spawn(async move {
///             // perform task...

Review comment (suggestion):
Network handles are a good example. I suggest that we update the example to accept on a `TcpListener` in a very similar way to the example in the README of this repository. Except this time, we acquire a permit before accepting a connection, and we drop the permit after handling an incoming connection, inside `tokio::spawn`.

I suggest this because right now, we're not really handling "incoming requests". It's just a simple loop.

Code after:
///
/// Basic usage:
///
/// ```
/// use tokio::sync::{Semaphore, TryAcquireError};
///
/// #[tokio::main]
/// async fn main() {
///     let semaphore = Semaphore::new(3);
///
///     let a_permit = semaphore.acquire().await.unwrap();
///     let two_permits = semaphore.acquire_many(2).await.unwrap();
///
///     assert_eq!(semaphore.available_permits(), 0);
///
///     let permit_attempt = semaphore.try_acquire();
///     assert_eq!(permit_attempt.err(), Some(TryAcquireError::NoPermits));
/// }
/// ```
///
/// 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
/// implicitly set an upper bound on the number of open files. If your
/// program attempts to open a large number of files and exceeds this
/// limit, it will result in an error.
///
/// This example uses a Semaphore with 100 permits. By acquiring a permit from
/// the Semaphore before accessing a file, you ensure that your program opens
/// no more than 100 files at a time. When trying to open the 101st
/// file, the program will wait until a permit becomes available before
/// proceeding to open another file.
/// ```
/// use std::io::Result;
/// use tokio::fs::File;
/// use tokio::sync::Semaphore;
/// use tokio::io::AsyncWriteExt;
///
/// static PERMITS: Semaphore = Semaphore::const_new(100);
///
/// async fn write_to_file(message: &[u8]) -> Result<()> {
///     let _permit = PERMITS.acquire().await.unwrap();
///     let mut buffer = File::create("example.txt").await?;
///     buffer.write_all(message).await?;
///     Ok(()) // Permit goes out of scope here, and is available again for acquisition
/// }
/// ```
///
/// Limit the number of incoming requests being handled at the same time.
///