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

Code before:
///         let (mut socket, _) = listener.accept().await?;
///
///         tokio::spawn(async move {
///                 let mut buf = [0; 1024];
///
///                 // In a loop, read data from the socket and write the data back.
///                 loop {
///                     let n = match socket.read(&mut buf).await {
///                         // socket closed
///                         Ok(n) if n == 0 => break,
///                         Ok(n) => n,
///                         Err(e) => {
///                             eprintln!("failed to read from socket; err = {:?}", e);
///                             break;
///                         }
///                     };
///
///                     // Write the data back
///                     if let Err(e) = socket.write_all(&buf[0..n]).await {
///                         eprintln!("failed to write to socket; err = {:?}", e);
///                         break;
///                     }
///                 }
///
///                 // Drop the permit, so more tasks can be created
///                 drop(permit);
///         });
///     }
/// }
/// ```
///
/// 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);

Review comment (suggestion):
I would drop the socket first. Otherwise, there's technically a short period where the socket is still open, even though we have released the permit.
```suggestion
///                 drop(socket);
///                 // Drop the permit, so more tasks can be created
///                 drop(permit);
```

Code after:
///
/// 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<Semaphore>` 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)]
pub struct Semaphore {
    /// The low level semaphore