use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use crate::runtime::BlockDescription;
use crate::runtime::BlockId;
use crate::runtime::BlockMessage;
use crate::runtime::BlockStatus;
use crate::runtime::Edge;
use crate::runtime::Error;
use crate::runtime::FlowgraphDescription;
use crate::runtime::FlowgraphId;
use crate::runtime::FlowgraphMessage;
use crate::runtime::Pmt;
use crate::runtime::PortId;
use crate::runtime::PortIndex;
use crate::runtime::PortName;
use crate::runtime::Timer;
use crate::runtime::block_inbox::BlockEndpoint;
use crate::runtime::channel::mpsc::Sender;
use crate::runtime::channel::oneshot;
use crate::runtime::resolve_port_index;
#[derive(Debug)]
pub(crate) struct RunningFlowgraphRegistry {
blocks: Vec<RunningBlockEntry>,
stream_edges: Vec<Edge>,
message_edges: Vec<Edge>,
}
impl RunningFlowgraphRegistry {
pub(crate) fn new(
blocks: Vec<RunningBlockEntry>,
stream_edges: Vec<Edge>,
message_edges: Vec<Edge>,
) -> Self {
Self {
blocks,
stream_edges,
message_edges,
}
}
pub(crate) fn mark_terminated(&self, block_id: BlockId) {
if let Some(entry) = self.blocks.get(block_id.0) {
entry.mark_terminated();
}
}
pub(crate) fn endpoints(&self) -> impl Iterator<Item = &BlockEndpoint> {
self.blocks.iter().map(|entry| &entry.endpoint)
}
fn entry(&self, block_id: BlockId) -> Result<&RunningBlockEntry, Error> {
self.blocks
.get(block_id.0)
.ok_or(Error::InvalidBlock(block_id))
}
fn message_input_index(
&self,
block_id: BlockId,
port_id: impl Into<PortId>,
) -> Result<PortIndex, Error> {
let port_id = port_id.into();
self.entry(block_id)?.message_input_index(block_id, port_id)
}
fn message_target(
&self,
block_id: BlockId,
port_id: impl Into<PortId>,
) -> Result<RunningMessageTarget, Error> {
let entry = self.entry(block_id)?;
let port_id = entry.message_input_index(block_id, port_id.into())?;
entry.ensure_running()?;
Ok(RunningMessageTarget {
endpoint: entry.endpoint.clone(),
port_id,
})
}
pub(crate) fn describe(&self) -> FlowgraphDescription {
FlowgraphDescription {
blocks: self
.blocks
.iter()
.map(RunningBlockEntry::description)
.collect(),
stream_edges: self.stream_edges.clone(),
message_edges: self.message_edges.clone(),
}
}
fn describe_block(&self, block_id: BlockId) -> Result<BlockDescription, Error> {
Ok(self.entry(block_id)?.description())
}
}
#[derive(Debug)]
struct RunningMessageTarget {
endpoint: BlockEndpoint,
port_id: PortIndex,
}
#[derive(Debug)]
pub(crate) struct RunningBlockEntry {
endpoint: BlockEndpoint,
description: BlockDescription,
terminated: AtomicBool,
}
impl RunningBlockEntry {
pub(crate) fn new(endpoint: BlockEndpoint, description: BlockDescription) -> Self {
Self {
endpoint,
description,
terminated: AtomicBool::new(false),
}
}
fn message_input_index(&self, block_id: BlockId, port_id: PortId) -> Result<PortIndex, Error> {
resolve_port_index(&port_id, &self.description.message_inputs)
.ok_or(Error::InvalidMessagePort(block_id, port_id))
}
fn ensure_running(&self) -> Result<(), Error> {
match self.status() {
BlockStatus::Running => Ok(()),
BlockStatus::Terminated => Err(Error::BlockTerminated),
}
}
fn mark_terminated(&self) {
self.terminated.store(true, Ordering::Release);
}
fn status(&self) -> BlockStatus {
if self.terminated.load(Ordering::Acquire) {
BlockStatus::Terminated
} else {
BlockStatus::Running
}
}
fn description(&self) -> BlockDescription {
let mut description = self.description.clone();
description.status = self.status();
description
}
}
#[derive(Debug, Clone)]
pub struct FlowgraphHandle {
id: FlowgraphId,
inbox: Sender<FlowgraphMessage>,
registry: Arc<RunningFlowgraphRegistry>,
}
#[derive(Debug, Clone)]
pub struct FlowgraphBlockHandle {
flowgraph: FlowgraphHandle,
block_id: BlockId,
}
impl FlowgraphHandle {
pub(crate) fn new(
id: FlowgraphId,
inbox: Sender<FlowgraphMessage>,
registry: Arc<RunningFlowgraphRegistry>,
) -> FlowgraphHandle {
FlowgraphHandle {
id,
inbox,
registry,
}
}
pub fn id(&self) -> FlowgraphId {
self.id
}
pub fn is_terminated(&self) -> bool {
self.inbox.is_closed()
}
fn description(&self, block_id: BlockId) -> Result<BlockDescription, Error> {
if self.is_terminated() {
return Err(Error::FlowgraphTerminated);
}
self.registry.describe_block(block_id)
}
fn message_input_index(
&self,
block_id: BlockId,
port_id: impl Into<PortId>,
) -> Result<PortIndex, Error> {
self.registry.message_input_index(block_id, port_id)
}
fn message_target(
&self,
block_id: BlockId,
port_id: impl Into<PortId>,
) -> Result<RunningMessageTarget, Error> {
if self.is_terminated() {
return Err(Error::FlowgraphTerminated);
}
self.registry.message_target(block_id, port_id)
}
pub fn block(&self, block_id: impl Into<BlockId>) -> FlowgraphBlockHandle {
FlowgraphBlockHandle {
flowgraph: self.clone(),
block_id: block_id.into(),
}
}
pub fn message_input_id(
&self,
block_id: impl Into<BlockId>,
name: impl Into<PortName>,
) -> Result<PortIndex, Error> {
self.message_input_index(block_id.into(), PortId::from(name.into()))
}
pub async fn post(
&self,
block_id: impl Into<BlockId>,
port_id: impl Into<PortId>,
data: Pmt,
) -> Result<(), Error> {
let block_id = block_id.into();
let target = self.message_target(block_id, port_id)?;
target
.endpoint
.send(BlockMessage::Post {
port_id: target.port_id,
data,
})
.await
.map_err(|_| Error::BlockTerminated)
}
pub async fn call(
&self,
block_id: impl Into<BlockId>,
port_id: impl Into<PortId>,
data: Pmt,
) -> Result<Pmt, Error> {
let block_id = block_id.into();
let target = self.message_target(block_id, port_id)?;
let (tx, rx) = oneshot::channel::<Result<Pmt, Error>>();
target
.endpoint
.send(BlockMessage::Call {
port_id: target.port_id,
data,
tx,
})
.await
.map_err(|_| Error::BlockTerminated)?;
rx.await.map_err(|_| Error::BlockTerminated)?
}
pub fn describe(&self) -> Result<FlowgraphDescription, Error> {
if self.is_terminated() {
return Err(Error::FlowgraphTerminated);
}
Ok(self.registry.describe())
}
pub fn describe_block(&self, block_id: impl Into<BlockId>) -> Result<BlockDescription, Error> {
self.description(block_id.into())
}
pub async fn stop(&self) -> Result<(), Error> {
self.inbox
.send(FlowgraphMessage::Terminate)
.await
.map_err(|_| Error::FlowgraphTerminated)?;
Ok(())
}
pub async fn stop_and_wait(&self) -> Result<(), Error> {
match self.stop().await {
Ok(()) | Err(Error::FlowgraphTerminated) => {}
Err(e) => return Err(e),
}
while !self.inbox.is_closed() {
Timer::after(std::time::Duration::from_millis(200)).await;
}
Ok(())
}
}
impl FlowgraphBlockHandle {
pub fn id(&self) -> BlockId {
self.block_id
}
pub fn message_input_id(&self, name: impl Into<PortName>) -> Result<PortIndex, Error> {
self.flowgraph.message_input_id(self.block_id, name)
}
pub async fn post(&self, port_id: impl Into<PortId>, data: Pmt) -> Result<(), Error> {
self.flowgraph.post(self.block_id, port_id, data).await
}
pub async fn call(&self, port_id: impl Into<PortId>, data: Pmt) -> Result<Pmt, Error> {
self.flowgraph.call(self.block_id, port_id, data).await
}
pub fn describe(&self) -> Result<BlockDescription, Error> {
self.flowgraph.describe_block(self.block_id)
}
}