use std::any::Any;
use std::fmt;
use std::sync::Arc;
use super::EventDomainId;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum EventDomainOperation {
BeginRun,
Enqueue,
Drain,
TransferBridge,
ValidateCompletion,
}
impl fmt::Display for EventDomainOperation {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Self::BeginRun => "begin run",
Self::Enqueue => "enqueue",
Self::Drain => "drain",
Self::TransferBridge => "transfer bridge",
Self::ValidateCompletion => "validate completion",
};
formatter.write_str(name)
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum EventDomainError {
#[error("event domain {domain:?} has no registered driver")]
MissingDriver {
domain: EventDomainId,
},
#[error(
"{operation} node {node_index:?} returned run domain {actual:?}, expected {expected:?}"
)]
RunDomainMismatch {
operation: EventDomainOperation,
node_index: Option<usize>,
expected: EventDomainId,
actual: EventDomainId,
},
#[error(
"{operation} node {node_index:?} received dependency origin {actual:?}, expected {expected:?}"
)]
DependencyDomainMismatch {
operation: EventDomainOperation,
node_index: Option<usize>,
expected: EventDomainId,
actual: EventDomainId,
},
#[error(
"{operation} node {node_index:?} returned completion origin {actual:?}, expected {expected:?}"
)]
CompletionTokenDomainMismatch {
operation: EventDomainOperation,
node_index: Option<usize>,
expected: EventDomainId,
actual: EventDomainId,
},
#[error("{operation} for event domain {domain:?} panicked: {message}")]
DrainPanicked {
operation: EventDomainOperation,
domain: EventDomainId,
message: String,
},
#[error(
"{operation} node {node_index:?} cannot admit token type {token_type} for event domain {actual:?} (expected {expected:?})"
)]
IncompatibleTokenType {
operation: EventDomainOperation,
node_index: Option<usize>,
expected: EventDomainId,
actual: EventDomainId,
token_type: &'static str,
},
#[error(
"{operation} node {node_index:?} failed waiting for dependency origin {actual:?} before destination domain {expected:?}: {source}"
)]
DependencyWaitFailed {
operation: EventDomainOperation,
node_index: Option<usize>,
expected: EventDomainId,
actual: EventDomainId,
#[source]
source: crate::error::BoxError,
},
}
pub trait EventToken: fmt::Debug + Send + Sync + 'static {
fn as_any(&self) -> &dyn Any;
fn origin(&self) -> EventDomainId;
fn wait(&self) -> crate::Result<()>;
}
pub trait EventDomainRun: fmt::Debug + Send {
fn domain(&self) -> EventDomainId;
fn enable_single_domain_coalescing(&mut self) -> bool {
false
}
fn enqueue(
&mut self,
dependencies: &[Arc<dyn EventToken>],
launch: &mut dyn FnMut() -> crate::Result<()>,
) -> crate::Result<Arc<dyn EventToken>>;
fn drain(&mut self) -> crate::Result<()>;
}
pub trait EventDomainDriver: fmt::Debug + Send + Sync + 'static {
fn supports_scoped_read_only(&self) -> bool {
false
}
fn begin_run(&self, domain: EventDomainId) -> crate::Result<Box<dyn EventDomainRun>>;
}
#[derive(Debug)]
struct ReadyEventToken {
origin: EventDomainId,
}
impl EventToken for ReadyEventToken {
fn as_any(&self) -> &dyn Any {
self
}
fn origin(&self) -> EventDomainId {
self.origin
}
fn wait(&self) -> crate::Result<()> {
Ok(())
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ImmediateEventDomainDriver;
impl ImmediateEventDomainDriver {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl EventDomainDriver for ImmediateEventDomainDriver {
fn supports_scoped_read_only(&self) -> bool {
true
}
fn begin_run(&self, domain: EventDomainId) -> crate::Result<Box<dyn EventDomainRun>> {
Ok(Box::new(ImmediateEventDomainRun {
domain,
coalesced: false,
shared_token: None,
}))
}
}
#[derive(Debug)]
struct ImmediateEventDomainRun {
domain: EventDomainId,
coalesced: bool,
shared_token: Option<Arc<dyn EventToken>>,
}
impl EventDomainRun for ImmediateEventDomainRun {
fn domain(&self) -> EventDomainId {
self.domain
}
fn enable_single_domain_coalescing(&mut self) -> bool {
self.coalesced = true;
true
}
fn enqueue(
&mut self,
dependencies: &[Arc<dyn EventToken>],
launch: &mut dyn FnMut() -> crate::Result<()>,
) -> crate::Result<Arc<dyn EventToken>> {
for dependency in dependencies {
let actual = dependency.origin();
if actual != self.domain {
return Err(crate::Error::from(
EventDomainError::DependencyDomainMismatch {
operation: EventDomainOperation::Enqueue,
node_index: None,
expected: self.domain,
actual,
},
));
}
dependency.wait()?;
}
launch()?;
if self.coalesced {
let token = self.shared_token.get_or_insert_with(|| {
Arc::new(ReadyEventToken {
origin: self.domain,
}) as Arc<dyn EventToken>
});
return Ok(Arc::clone(token));
}
Ok(Arc::new(ReadyEventToken {
origin: self.domain,
}))
}
fn drain(&mut self) -> crate::Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests;