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

Code before:
///
/// 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 move the `Arc` into our task, where we will wait to acquire a new permit
/// via [`Semaphore::acquire_owned`] so we can start processing. Once our processing is done, we
/// will drop the permit to allow other tasks to acquire it.
///
/// 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 semaphore_clone = semaphore.clone();
///         join_handles.push(tokio::spawn(async move {
///             perform_task(semaphore_clone).await;
///         }));
///     }
///
///     for handle in join_handles {
///         handle.await.unwrap();
///     }
/// }
///
/// async fn perform_task(semaphore: Arc<Semaphore>) {
///     let permit = semaphore.acquire_owned().await.unwrap();
///     // perform task...
///     // explicitly own `permit` in the task
///     drop(permit);
/// }
/// ```
///
/// [`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
    ll_sem: ll::Semaphore,
    #[cfg(all(tokio_unstable, feature = "tracing"))]
    resource_span: tracing::Span,
}


Review comment (suggestion):
I think we probably still want to acquire the permit before spawning. That way, we don't spawn more tasks than we have permits.

Also, with the way you're using it now, you don't need `acquire_owned`. Using `acquire` would work fine, since the permit itself is not moved across tasks.

Code after:
/// 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.
///
/// 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