use futures::future::Future;
use crate::runtime::BlockId;
use crate::runtime::BlockMessage;
use crate::runtime::Edge;
use crate::runtime::Error;
use crate::runtime::FlowgraphMessage;
use crate::runtime::block::Block;
use crate::runtime::block_inbox::BlockEndpoint;
use crate::runtime::channel::mpsc::Sender;
use crate::runtime::channel::oneshot;
use crate::runtime::local_domain::LocalDomainInbox;
use crate::runtime::scheduler::Task;
pub(crate) type NormalBlock = Box<dyn Block>;
pub(crate) type NormalBlocks = Vec<NormalBlock>;
#[derive(Debug, Clone)]
pub struct DomainTopology {
pub(crate) blocks: Vec<BlockId>,
pub(crate) stream_edges: Vec<Edge>,
pub(crate) message_edges: Vec<Edge>,
}
impl DomainTopology {
pub(crate) fn new(
blocks: Vec<BlockId>,
stream_edges: Vec<Edge>,
message_edges: Vec<Edge>,
) -> Self {
Self {
blocks,
stream_edges,
message_edges,
}
}
pub fn blocks(&self) -> &[BlockId] {
&self.blocks
}
pub fn stream_edges(&self) -> &[Edge] {
&self.stream_edges
}
pub fn message_edges(&self) -> &[Edge] {
&self.message_edges
}
}
pub struct NormalDomainSpec {
pub(crate) blocks: NormalBlocks,
pub(crate) topology: DomainTopology,
pub(crate) main_channel: Sender<FlowgraphMessage>,
}
impl NormalDomainSpec {
pub(crate) fn new(
blocks: NormalBlocks,
topology: DomainTopology,
main_channel: Sender<FlowgraphMessage>,
) -> Self {
Self {
blocks,
topology,
main_channel,
}
}
pub fn topology(&self) -> &DomainTopology {
&self.topology
}
pub fn blocks(&self) -> impl Iterator<Item = BlockId> + '_ {
self.blocks.iter().map(|block| block.id())
}
pub fn take_block(&mut self, block_id: BlockId) -> Result<RunnableBlock, Error> {
let pos = self
.blocks
.iter()
.position(|block| block.id() == block_id)
.ok_or(Error::InvalidBlock(block_id))?;
let block = self.blocks.swap_remove(pos);
let stop = BlockStop {
block_id,
endpoint: block.inbox(),
};
Ok(RunnableBlock {
block,
main_channel: self.main_channel.clone(),
stop,
})
}
}
#[derive(Clone)]
pub struct BlockStop {
block_id: BlockId,
endpoint: BlockEndpoint,
}
impl BlockStop {
pub fn id(&self) -> BlockId {
self.block_id
}
pub async fn stop(&self) -> Result<(), Error> {
self.endpoint.send(BlockMessage::Terminate).await
}
}
pub struct RunnableBlock {
block: NormalBlock,
main_channel: Sender<FlowgraphMessage>,
stop: BlockStop,
}
impl RunnableBlock {
pub fn id(&self) -> BlockId {
self.block.id()
}
pub fn stop_handle(&self) -> BlockStop {
self.stop.clone()
}
pub async fn run(self) -> StoppedBlock {
let Self {
mut block,
main_channel,
..
} = self;
block.run(main_channel).await;
StoppedBlock { block }
}
}
pub struct StoppedBlock {
block: NormalBlock,
}
impl StoppedBlock {
pub fn id(&self) -> BlockId {
self.block.id()
}
pub(crate) fn into_block(self) -> NormalBlock {
self.block
}
}
pub(crate) struct PreparedLocalDomain {
pub(crate) domain_id: usize,
pub(crate) inbox: LocalDomainInbox,
pub(crate) slots: Vec<(BlockId, usize)>,
pub(crate) topology: DomainTopology,
pub(crate) main_channel: Sender<FlowgraphMessage>,
}
impl PreparedLocalDomain {
pub(crate) fn new(
domain_id: usize,
inbox: LocalDomainInbox,
slots: Vec<(BlockId, usize)>,
topology: DomainTopology,
main_channel: Sender<FlowgraphMessage>,
) -> Self {
Self {
domain_id,
inbox,
slots,
topology,
main_channel,
}
}
pub(crate) fn start(self) -> Result<LocalRunningDomain, Error> {
let completion =
self.inbox
.start_run(self.domain_id, self.slots, self.topology, self.main_channel)?;
Ok(LocalRunningDomain::new(self.inbox, completion))
}
}
pub struct NormalRunningDomain {
blocks: Vec<(Task<StoppedBlock>, BlockStop)>,
stop_requested: bool,
}
impl NormalRunningDomain {
pub fn new(blocks: Vec<(Task<StoppedBlock>, BlockStop)>) -> Self {
Self {
blocks,
stop_requested: false,
}
}
pub(crate) async fn stop(&mut self) {
if self.stop_requested {
return;
}
self.stop_requested = true;
for (_, stop) in &self.blocks {
if let Err(e) = stop.stop().await {
debug!(
"normal domain tried to terminate block {:?}: {e}",
stop.id()
);
}
}
}
pub(crate) async fn join(self) -> NormalBlocks {
let mut stopped = Vec::with_capacity(self.blocks.len());
for (task, _) in self.blocks {
stopped.push(task.await.into_block());
}
stopped
}
}
pub(crate) struct LocalRunningDomain {
inbox: LocalDomainInbox,
completion: oneshot::Receiver<Result<(), Error>>,
}
impl LocalRunningDomain {
pub(crate) fn new(
inbox: LocalDomainInbox,
completion: oneshot::Receiver<Result<(), Error>>,
) -> Self {
Self { inbox, completion }
}
pub(crate) async fn stop(&mut self) -> Result<(), Error> {
self.inbox.stop_run().await
}
pub(crate) async fn join(self) -> Result<(), Error> {
self.completion
.await
.map_err(|_| Error::RuntimeError("local domain task canceled".to_string()))??;
Ok(())
}
}
pub trait Scheduler: Clone + 'static
where
#[cfg(not(target_arch = "wasm32"))]
Self: Send,
{
fn start_normal_domain(&self, spec: NormalDomainSpec) -> Result<NormalRunningDomain, Error>;
fn spawn<T: Send + 'static>(&self, future: impl Future<Output = T> + Send + 'static)
-> Task<T>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::runtime::BlockId;
use crate::runtime::BlockMessage;
use crate::runtime::FlowgraphMessage;
use crate::runtime::PortIndex;
use crate::runtime::PortName;
use crate::runtime::Result;
use crate::runtime::block::BlockObject;
use crate::runtime::block_inbox::BlockInbox;
use crate::runtime::buffer::DynBufferReader;
use crate::runtime::buffer::DynBufferWriter;
use crate::runtime::channel::mpsc::Sender;
struct TestBlock {
id: BlockId,
endpoint: BlockEndpoint,
}
impl BlockObject for TestBlock {
fn inbox(&self) -> BlockEndpoint {
self.endpoint.clone()
}
fn id(&self) -> BlockId {
self.id
}
fn type_name(&self) -> &str {
"TestBlock"
}
fn instance_name(&self) -> Option<&str> {
None
}
fn is_blocking(&self) -> bool {
false
}
fn stream_input_at(
&mut self,
_index: PortIndex,
) -> Option<(PortName, &mut dyn DynBufferReader)> {
None
}
fn stream_output_at(
&mut self,
_index: PortIndex,
) -> Option<(PortName, &mut dyn DynBufferWriter)> {
None
}
fn message_inputs(&self) -> &'static [&'static str] {
&[]
}
fn message_outputs(&self) -> &'static [&'static str] {
&[]
}
fn connect_message(
&mut self,
_src_port: PortIndex,
_dst: BlockEndpoint,
_dst_port: PortIndex,
) -> Result<(), Error> {
Ok(())
}
}
#[async_trait::async_trait]
impl Block for TestBlock {
async fn run(&mut self, _main_inbox: Sender<FlowgraphMessage>) {}
}
#[test]
fn normal_domain_stop_sends_terminate_to_blocks_once() {
let block_id = BlockId(0);
let (inbox, mut reader) = BlockInbox::pair(4);
let endpoint = BlockEndpoint::Direct(inbox);
let task_endpoint = endpoint.clone();
let (_runnable, task) = async_task::spawn(
async move {
StoppedBlock {
block: Box::new(TestBlock {
id: block_id,
endpoint: task_endpoint,
}),
}
},
|_| {},
);
let stop = BlockStop { block_id, endpoint };
let mut domain = NormalRunningDomain::new(vec![(task, stop)]);
crate::runtime::block_on(domain.stop());
assert!(matches!(reader.try_recv(), Some(BlockMessage::Terminate)));
crate::runtime::block_on(domain.stop());
assert!(reader.try_recv().is_none());
}
}