use core::num::NonZeroUsize;
use crate::{with_execution_policy, ExecutionPolicy, Result, StridedError};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExecContext {
kind: ExecContextKind,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ExecContextKind {
Serial,
MaxThreads(NonZeroUsize),
Ambient,
}
impl ExecContext {
#[inline]
pub const fn serial() -> Self {
Self {
kind: ExecContextKind::Serial,
}
}
#[inline]
pub fn max_threads(max_threads: usize) -> Result<Self> {
match NonZeroUsize::new(max_threads) {
Some(max_threads) => Ok(Self {
kind: ExecContextKind::MaxThreads(max_threads),
}),
None => Err(StridedError::InvalidThreadBudget { max_threads }),
}
}
#[inline]
pub const fn ambient() -> Self {
Self {
kind: ExecContextKind::Ambient,
}
}
#[inline]
pub fn is_serial(&self) -> bool {
matches!(self.kind, ExecContextKind::Serial)
}
#[inline]
pub fn is_ambient(&self) -> bool {
matches!(self.kind, ExecContextKind::Ambient)
}
#[inline]
pub fn max_threads_limit(&self) -> Option<NonZeroUsize> {
match self.kind {
ExecContextKind::MaxThreads(max_threads) => Some(max_threads),
ExecContextKind::Serial | ExecContextKind::Ambient => None,
}
}
#[inline]
pub fn run<R>(&self, operation: impl FnOnce() -> R) -> R {
match self.kind {
ExecContextKind::Serial => {
with_execution_policy(ExecutionPolicy::Sequential, operation)
}
ExecContextKind::MaxThreads(max_threads) => {
with_execution_policy(ExecutionPolicy::Rayon { max_threads }, operation)
}
ExecContextKind::Ambient => operation(),
}
}
}
#[cfg(feature = "parallel")]
impl ExecContext {
pub fn run_on<R: Send>(
pool: &rayon::ThreadPool,
max_threads: usize,
operation: impl FnOnce(ExecContext) -> R + Send,
) -> Result<R> {
let budget = NonZeroUsize::new(max_threads)
.ok_or(StridedError::InvalidThreadBudget { max_threads })?;
crate::threading::run_on_pool(pool, budget, move |width| {
let ctx = if width <= 1 {
Self::serial()
} else {
Self::max_threads(width)?
};
Ok(ctx.run(|| operation(ctx)))
})
}
}
impl Default for ExecContext {
#[inline]
fn default() -> Self {
Self::serial()
}
}
#[cfg(feature = "parallel")]
#[inline]
pub fn with_execution_pool<R>(
pool: &rayon::ThreadPool,
max_threads: NonZeroUsize,
operation: impl FnOnce() -> R,
) -> R {
crate::threading::with_execution_pool(pool, max_threads, operation)
}
#[cfg(test)]
#[path = "exec_context/tests/tests.rs"]
mod tests;