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

Code before:
/// use std::sync::Arc;
/// use tokio::sync::{AcquireError, Semaphore};
/// use tokio::time::{interval, Duration};
///
/// struct TokenBucket {
///     sem: Arc<Semaphore>,
///     jh: tokio::task::JoinHandle<()>,
/// }
///
/// impl TokenBucket {
///     fn new(duration: Duration) -> Self {
///         let rate = (1.0 / duration.as_secs_f32()) as usize;
///         let sem = Arc::new(Semaphore::new(rate));
///
///         // refills the tokens at the end of each interval
///         let jh = tokio::spawn({
///             let sem = sem.clone();
///             let mut interval = interval(duration);
///             interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
///
///             async move {
///                 loop {
///                     interval.tick().await;
///
///                     let cap = rate - sem.available_permits();
///                     sem.add_permits(cap.min(1));
///                 }
///             }
///         });
///
///         Self { jh, sem }
///     }
///
///     async fn acquire(&self) -> Result<(), AcquireError> {
///         self.sem.acquire().await.map(|p| p.forget())
///     }
///
///     async fn close(self) {
///         self.jh.abort();
///         let _ = self.jh.await;
///
///         self.sem.close();
///     }
/// }
///
/// #[tokio::main]
/// async fn main() {
///     let ops = 1.0; // operation per second
///     let update_interval = 1.0 / ops;
///     let bucket = TokenBucket::new(Duration::from_secs_f32(update_interval));
///

Review comment (suggestion):
I think the code is clearer this way:
```suggestion
///                     if sem.available_permits() < rate {
///                         sem.add_permits(1);
///                     }
```

Code after:
///             let sem = sem.clone();
///             let mut interval = interval(duration);
///             interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
///
///             async move {
///                 loop {
///                     interval.tick().await;
///
///                     if sem.available_permits() < capacity {
///                         sem.add_permits(1);
///                     }
///                 }
///             }
///         });
///
///         Self { jh, sem }
///     }
///
///     async fn acquire(&self) -> Result<(), AcquireError> {
///         self.sem.acquire().await.map(|p| p.forget())
///     }
///
///     async fn close(self) {
///         self.sem.close();
///         self.jh.abort();
///         let _ = self.jh.await;
///     }
/// }
///
/// #[tokio::main]
/// async fn main() {
///     let capacity = 5; // operation per second
///     let update_interval = Duration::from_secs_f32(1.0 / capacity as f32);
///     let bucket = TokenBucket::new(update_interval, capacity);
///
///     for _ in 0..5 {
///         bucket.acquire().await.unwrap();
///
///         // do the operation
///     }
///
///     bucket.close().await;
/// }
/// ```
///
/// 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.
///