pub struct DynamicSemaphore { /* private fields */ }Expand description
A wrapper around Tokio’s Semaphore that supports dynamic capacity reduction.
Unlike the standard Tokio semaphore, this implementation allows for reduction of the effective capacity even when permits are already acquired and other tasks are waiting. This is particularly useful for rate limiting scenarios where we need to dynamically adjust the concurrency level based on runtime conditions.
§Key Features
- Dynamic Capacity Reduction: Can reduce capacity even when permits are in use
- Queued Waiter Preservation: Existing waiters remain in queue during capacity changes
- Thread-Safe: All operations are atomic and safe for concurrent use
- Drop Safety: Automatically manages capacity when permits are released
§Example
use feroxbuster::sync::DynamicSemaphore;
#[tokio::main]
async fn main() {
let semaphore = DynamicSemaphore::new(2);
// Acquire permits
let _permit1 = semaphore.acquire().await.unwrap();
let _permit2 = semaphore.acquire().await.unwrap();
// Reduce capacity from 2 to 1 (takes effect when permits are released)
semaphore.reduce_capacity(1);
// When permits are dropped, only 1 permit will be available instead of 2
}Implementations§
Source§impl DynamicSemaphore
impl DynamicSemaphore
Sourcepub fn new(permits: usize) -> Self
pub fn new(permits: usize) -> Self
Creates a new DynamicSemaphore with the specified number of permits.
§Arguments
permits- The initial number of permits available in the semaphore
§Panics
Panics if permits exceeds the maximum number of permits supported by
the underlying Tokio semaphore implementation.
§Examples
use feroxbuster::sync::DynamicSemaphore;
let semaphore = DynamicSemaphore::new(10);
assert_eq!(semaphore.current_capacity(), 10);Sourcepub async fn acquire(&self) -> Result<DynamicSemaphorePermit<'_>, AcquireError>
pub async fn acquire(&self) -> Result<DynamicSemaphorePermit<'_>, AcquireError>
Acquires a permit from the semaphore.
This method will wait until a permit becomes available. The returned permit will automatically manage capacity constraints when dropped.
§Returns
A Result containing a DynamicSemaphorePermit on success, or an
tokio::sync::AcquireError if the semaphore has been closed.
§Examples
use feroxbuster::sync::DynamicSemaphore;
#[tokio::main]
async fn main() {
let semaphore = DynamicSemaphore::new(1);
let permit = semaphore.acquire().await.unwrap();
// permit is automatically released when dropped
}Sourcepub fn try_acquire(&self) -> Result<DynamicSemaphorePermit<'_>, TryAcquireError>
pub fn try_acquire(&self) -> Result<DynamicSemaphorePermit<'_>, TryAcquireError>
Attempts to acquire a permit without waiting.
If a permit is immediately available, it is returned. Otherwise, this method returns an error indicating why the permit could not be acquired.
§Returns
A Result containing a DynamicSemaphorePermit if successful, or a
tokio::sync::TryAcquireError if no permit is available or the semaphore is closed.
§Examples
use feroxbuster::sync::DynamicSemaphore;
use tokio::sync::TryAcquireError;
let semaphore = DynamicSemaphore::new(1);
match semaphore.try_acquire() {
Ok(permit) => println!("Got permit"),
Err(TryAcquireError::NoPermits) => println!("No permits available"),
Err(TryAcquireError::Closed) => println!("Semaphore closed"),
};Sourcepub fn reduce_capacity(&self, new_capacity: usize) -> usize
pub fn reduce_capacity(&self, new_capacity: usize) -> usize
Reduces the maximum capacity of the semaphore.
This method sets a new maximum capacity for the semaphore. The change takes effect immediately for new permit acquisitions. If there are currently more permits in use than the new capacity allows, the reduction will take effect gradually as permits are released.
§Arguments
new_capacity- The new maximum number of permits that should be available
§Returns
The previous capacity value before the change.
§Notes
- This operation is atomic and thread-safe
- Existing permit holders are not affected until they release their permits
- Queued waiters remain in the queue and will eventually be served
- If available permits exceed the new capacity, excess permits are immediately forgotten
§Examples
use feroxbuster::sync::DynamicSemaphore;
#[tokio::main]
async fn main() {
let semaphore = DynamicSemaphore::new(5);
// Reduce capacity from 5 to 2
let old_capacity = semaphore.reduce_capacity(2);
assert_eq!(old_capacity, 5);
assert_eq!(semaphore.current_capacity(), 2);
}Sourcepub fn increase_capacity(&self, new_capacity: usize) -> usize
pub fn increase_capacity(&self, new_capacity: usize) -> usize
Increases the maximum capacity of the semaphore.
This method sets a new maximum capacity that is higher than the current one. Additional permits are immediately added to the semaphore up to the new capacity.
§Arguments
new_capacity- The new maximum number of permits that should be available
§Returns
The previous capacity value before the change.
§Panics
Panics if the new capacity would cause the semaphore to exceed its maximum supported permit count.
§Examples
use feroxbuster::sync::DynamicSemaphore;
#[tokio::main]
async fn main() {
let semaphore = DynamicSemaphore::new(2);
// Increase capacity from 2 to 5
let old_capacity = semaphore.increase_capacity(5);
assert_eq!(old_capacity, 2);
assert_eq!(semaphore.current_capacity(), 5);
}Sourcepub fn current_capacity(&self) -> usize
pub fn current_capacity(&self) -> usize
Returns the current maximum capacity of the semaphore.
This represents the maximum number of permits that can be available at any given time, which may be different from the number of currently available permits.
§Examples
use feroxbuster::sync::DynamicSemaphore;
let semaphore = DynamicSemaphore::new(10);
assert_eq!(semaphore.current_capacity(), 10);Sourcepub fn available_permits(&self) -> usize
pub fn available_permits(&self) -> usize
Returns the number of permits currently available for immediate acquisition.
This value represents permits that can be acquired without waiting. Note that this number may be less than the capacity if permits are currently in use.
§Examples
use feroxbuster::sync::DynamicSemaphore;
#[tokio::main]
async fn main() {
let semaphore = DynamicSemaphore::new(3);
assert_eq!(semaphore.available_permits(), 3);
let _permit = semaphore.acquire().await.unwrap();
assert_eq!(semaphore.available_permits(), 2);
}Sourcepub fn close(&self)
pub fn close(&self)
Closes the semaphore, preventing new permits from being acquired.
This will wake up all tasks currently waiting to acquire a permit, causing
them to receive an tokio::sync::AcquireError. Existing permits remain
valid until dropped.
§Examples
use feroxbuster::sync::DynamicSemaphore;
#[tokio::main]
async fn main() {
let semaphore = DynamicSemaphore::new(1);
semaphore.close();
// This will return an error
assert!(semaphore.acquire().await.is_err());
}Sourcepub fn is_closed(&self) -> bool
pub fn is_closed(&self) -> bool
Returns whether the semaphore has been closed.
§Examples
use feroxbuster::sync::DynamicSemaphore;
let semaphore = DynamicSemaphore::new(1);
assert!(!semaphore.is_closed());
semaphore.close();
assert!(semaphore.is_closed());Sourcepub fn permits_in_use(&self) -> usize
pub fn permits_in_use(&self) -> usize
Returns the current number of permits in use (for debugging).
This is primarily useful for debugging and testing to understand the internal state of the semaphore.
§Examples
use feroxbuster::sync::DynamicSemaphore;
#[tokio::main]
async fn main() {
let semaphore = DynamicSemaphore::new(3);
assert_eq!(semaphore.permits_in_use(), 0);
let _permit = semaphore.acquire().await.unwrap();
assert_eq!(semaphore.permits_in_use(), 1);
}Trait Implementations§
Auto Trait Implementations§
impl !Freeze for DynamicSemaphore
impl !RefUnwindSafe for DynamicSemaphore
impl Send for DynamicSemaphore
impl Sync for DynamicSemaphore
impl Unpin for DynamicSemaphore
impl !UnwindSafe for DynamicSemaphore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more