use std::sync::Arc;
use thiserror::Error;
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
#[error("slot capacity {requested} must be in 1..={maximum}")]
pub(crate) struct SlotBudgetCapacityError {
pub(crate) requested: usize,
pub(crate) maximum: usize,
}
#[derive(Clone, Debug)]
pub(crate) struct SlotBudget {
capacity: usize,
permits: Arc<Semaphore>,
}
impl SlotBudget {
pub(crate) fn new(capacity: usize) -> Result<Self, SlotBudgetCapacityError> {
if capacity == 0 || capacity > Semaphore::MAX_PERMITS {
return Err(SlotBudgetCapacityError {
requested: capacity,
maximum: Semaphore::MAX_PERMITS,
});
}
Ok(Self {
capacity,
permits: Arc::new(Semaphore::new(capacity)),
})
}
pub(crate) fn capacity(&self) -> usize {
self.capacity
}
pub(crate) fn reserved(&self) -> usize {
self.capacity
.saturating_sub(self.permits.available_permits())
}
pub(crate) fn try_reserve(&self) -> Option<SlotPermit> {
self.permits
.clone()
.try_acquire_owned()
.ok()
.map(|permit| SlotPermit { _permit: permit })
}
pub(crate) async fn reserve(&self) -> SlotPermit {
let permit = self
.permits
.clone()
.acquire_owned()
.await
.expect("slot budget semaphore stays open because this type never closes it");
SlotPermit { _permit: permit }
}
}
#[derive(Debug)]
#[must_use = "dropping a slot permit releases its capacity"]
pub(crate) struct SlotPermit {
_permit: OwnedSemaphorePermit,
}