use std::sync::Arc;
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
use tokio_util::sync::CancellationToken;
use super::{CoreExecutionError, CoreOperationContext, operation_wait};
#[derive(Debug)]
pub(crate) struct CoreExecutionAdmission {
slots: Arc<Semaphore>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CoreExecutionAdmissionInput {
pub(crate) max_concurrent_executions: usize,
}
impl TryFrom<CoreExecutionAdmissionInput> for CoreExecutionAdmission {
type Error = CoreExecutionError;
fn try_from(value: CoreExecutionAdmissionInput) -> Result<Self, Self::Error> {
let CoreExecutionAdmissionInput {
max_concurrent_executions,
} = value;
if max_concurrent_executions == 0 {
return Err(CoreExecutionError::InvalidComposition(
"the aggregate execution limit is zero",
));
}
if max_concurrent_executions > Semaphore::MAX_PERMITS {
return Err(CoreExecutionError::InvalidComposition(
"the aggregate execution limit exceeds the permit ceiling",
));
}
Ok(Self {
slots: Arc::new(Semaphore::new(max_concurrent_executions)),
})
}
}
impl CoreExecutionAdmission {
pub(super) async fn acquire(
&self,
operation: &CoreOperationContext,
cancellation: &CancellationToken,
) -> Result<OwnedSemaphorePermit, CoreExecutionError> {
operation_wait(
operation,
cancellation,
Arc::clone(&self.slots).acquire_owned(),
)
.await?
.map_err(|_| CoreExecutionError::ExecutionAdmissionClosed)
}
}