DynamicSemaphore

Struct DynamicSemaphore 

Source
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

Source

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);
Source

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
}
Source

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"),
};
Source

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);
}
Source

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);
}
Source

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);
Source

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);
}
Source

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());
}
Source

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());
Source

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§

Source§

impl Debug for DynamicSemaphore

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,