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

Code before:
/// Before a task sends a request, it must acquire a permit from the semaphore by calling [`Semaphore::acquire`].
/// This ensures that at most 10 requests are sent in parallel at any given time.
/// After a task has sent a request, it drops the permit to allow other tasks to send requests.
///
/// ```
/// use std::sync::Arc;
/// use tokio::sync::Semaphore;
///
/// #[tokio::main]
/// async fn main() {
///     // Define maximum number of parallel requests.
///     let semaphore = Arc::new(Semaphore::new(10));
///     // Spawn many tasks that will send requests.
///     let mut jhs = Vec::new();
///     for task_id in 0..100 {
///         let semaphore = semaphore.clone();
///         let jh = tokio::spawn(async move {
///             // Acquire permit before sending request.
///             let _permit = semaphore.acquire().await.unwrap();
///             // Send the request.
///             let response = send_request(task_id).await;
///             // Drop the permit after the request has been sent.
///             drop(_permit);
///             // Handle response.
///             // ...
///         });
///         jhs.push(jh);
///     }
///     for jh in jhs {
///         jh.await.unwrap();
///     }
/// }
/// # async fn send_request(task_id: usize) {
/// #     // Send request.
/// # }
/// ```
///
/// ## 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.)
///

Review comment (suggestion):
It could make sense to return the responses from the tasks and collect them into a vector in the loop at the bottom.

Code after:
/// a task sends a request, it must acquire a permit from the semaphore by
/// calling [`Semaphore::acquire`]. This ensures that at most 10 requests are
/// sent in parallel at any given time. After a task has sent a request, it
/// drops the permit to allow other tasks to send requests.
///
/// ```
/// use std::sync::Arc;
/// use tokio::sync::Semaphore;
///
/// #[tokio::main]
/// async fn main() {
///     // Define maximum number of parallel requests.
///     let semaphore = Arc::new(Semaphore::new(10));
///     // Spawn many tasks that will send requests.
///     let mut jhs = Vec::new();
///     for task_id in 0..100 {
///         let semaphore = semaphore.clone();
///         let jh = tokio::spawn(async move {
///             // Acquire permit before sending request.
///             let _permit = semaphore.acquire().await.unwrap();
///             // Send the request.
///             let response = send_request(task_id).await;
///             // Drop the permit after the request has been sent.
///             drop(_permit);
///             // Handle response.
///             // ...
///
///             response
///         });
///         jhs.push(jh);
///     }
///     // Collect responses from tasks.
///     let mut responses = Vec::new();
///     for jh in jhs {
///         let response = jh.await.unwrap();
///         responses.push(response);
///     }
///     // Process responses.
///     // ...
/// }
/// # async fn send_request(task_id: usize) {
/// #     // Send request.
/// # }
/// ```
///
/// ## 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.
///