use std::any::{TypeId, type_name};
use std::fmt;
use std::future::Future;
use std::num::NonZeroUsize;
use std::pin::Pin;
use std::sync::Arc;
use crate::RunContext;
use crate::registry::{Error, Result};
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct ExecutionDomain {
id: TypeId,
name: &'static str,
}
impl ExecutionDomain {
pub fn of<T: ?Sized + 'static>() -> Self {
Self { id: TypeId::of::<T>(), name: type_name::<T>() }
}
}
impl fmt::Debug for ExecutionDomain {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
f.debug_tuple("ExecutionDomain").field(&self.name).finish()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ExecutorKind {
CurrentThread,
MultiThread { threads: NonZeroUsize },
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub enum ExecutorSpec {
#[default]
Inherit,
Shared(ExecutionDomain),
Dedicated(ExecutorKind),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum PollPriority {
Low,
Normal,
High,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub enum SchedulingPolicy {
#[default]
Inherit,
Priority(PollPriority),
Weighted(NonZeroUsize),
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub struct ExecutionSpec {
pub executor: ExecutorSpec,
pub scheduling: SchedulingPolicy,
}
impl ExecutionSpec {
pub fn with_executor(
mut self,
executor: ExecutorSpec,
) -> Self {
self.executor = executor;
self
}
pub fn with_scheduling(
mut self,
scheduling: SchedulingPolicy,
) -> Self {
self.scheduling = scheduling;
self
}
}
pub type ExecutionFuture =
Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
pub trait ExecutionDriver: Send + Sync + 'static {
fn prepare(
&self,
spec: ExecutionSpec,
) -> Result<Arc<dyn PreparedExecution>>;
fn drain(&self) -> ExecutionFuture {
Box::pin(async { Ok(()) })
}
fn finalize(&self) -> ExecutionFuture {
Box::pin(async { Ok(()) })
}
}
pub trait PreparedExecution: Send + Sync + 'static {
fn execute(
&self,
task: ExecutionFuture,
context: RunContext,
) -> ExecutionFuture;
}
#[derive(Clone, Default)]
pub(crate) struct ExecutionPlan {
pub(crate) spec: ExecutionSpec,
pub(crate) executor: Option<Arc<dyn PreparedExecution>>,
}
impl ExecutionPlan {
pub(crate) fn prepare(
spec: ExecutionSpec,
driver: Option<&dyn ExecutionDriver>,
) -> Result<Self> {
if spec == ExecutionSpec::default() {
return Ok(Self::default());
}
let driver = driver.ok_or_else(|| {
Error::msg(format!("no execution driver is installed for {spec:?}"))
})?;
Ok(Self { spec, executor: Some(driver.prepare(spec)?) })
}
}
impl fmt::Debug for ExecutionPlan {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
f.debug_struct("ExecutionPlan").field("spec", &self.spec).finish()
}
}
#[cfg(test)]
#[path = "../tests/execution/unit.rs"]
mod tests;