#![cfg(feature = "multi")]
#![allow(clippy::type_complexity)]
use crate::valtron::multi;
pub use crate::valtron::multi::{block_on, get_pool};
use crate::valtron::CancellableFutureTask;
use crate::valtron::FutureTask;
use crate::valtron::ReadyValues;
use crate::valtron::StreamConfig;
use crate::valtron::StreamTask;
use crate::valtron::ThreadedValue;
use crate::valtron::{
collect_one, collect_result, ExecutionAction, GenericResult, NotificationItem,
NotifyQueueStreamIterator, NotifyRecvIterator, Stream, StreamIterator, TaskIterator,
TaskStatus,
};
use core::future::Future;
use std::sync::Arc;
use core::sync::atomic::AtomicBool;
use crate::valtron::{DEFAULT_MAX_TURNS, DEFAULT_PARK_DURATION, DEFAULT_WAIT_CYCLE};
use super::inline_action::InlineActionBehaviour;
use crate::valtron::executors::local::NotifyQueue;
use crate::valtron::{BoxedExecutionEngine, ConsumingIter, ExecutorError, SpawnInfo};
#[allow(clippy::type_complexity)]
pub struct InlineAction<Done, Pending, Action, Task>(
Option<(
InlineActionBehaviour,
Task,
Arc<NotifyQueue<TaskStatus<Done, Pending, Action>>>,
)>,
)
where
Action: ExecutionAction + Send + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + Send + 'static;
impl<Done, Pending, Action, Task> InlineAction<Done, Pending, Action, Task>
where
Done: Send + 'static,
Pending: Send + 'static,
Action: ExecutionAction + Send + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + Send + 'static,
{
pub fn new(
behaviour: InlineActionBehaviour,
task: Task,
wait_cycle: std::time::Duration,
) -> (
Self,
crate::valtron::executors::local::NotifyRecvIterator<TaskStatus<Done, Pending, Action>>,
) {
let iter_chan: Arc<NotifyQueue<TaskStatus<Done, Pending, Action>>> =
Arc::new(NotifyQueue::unbounded());
(
Self(Some((behaviour, task, iter_chan.clone()))),
crate::valtron::executors::local::NotifyRecvIterator::from_notify_queue(
iter_chan,
wait_cycle,
),
)
}
pub fn from_parts(
behaviour: InlineActionBehaviour,
task: Task,
channel: Arc<NotifyQueue<TaskStatus<Done, Pending, Action>>>,
) -> Self {
Self(Some((behaviour, task, channel)))
}
}
impl<Done, Pending, Action, Task> ExecutionAction
for InlineAction<Done, Pending, Action, Task>
where
Done: Send + 'static,
Pending: Send + 'static,
Action: ExecutionAction + Send + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + Send + 'static,
{
fn apply(
&mut self,
key: Option<crate::synca::Entry>,
executor: BoxedExecutionEngine,
) -> GenericResult<SpawnInfo> {
if let Some((behaviour, task, channel)) = self.0.take() {
match behaviour {
InlineActionBehaviour::Sequenced => {
tracing::debug!("Sequence action for InlineAction");
let Some(parent) = key else {
return Err(Box::new(ExecutorError::ParentMustBeSupplied));
};
let consuming_iter = ConsumingIter::new(task, channel.clone());
executor
.sequenced(consuming_iter.into(), parent)
.map_err(Into::into)
}
InlineActionBehaviour::LiftWithParent => {
tracing::debug!("Lift action for InlineAction");
let Some(parent) = key else {
return Err(Box::new(ExecutorError::ParentMustBeSupplied));
};
let consuming_iter = ConsumingIter::new(task, channel.clone());
executor
.lift(consuming_iter.into(), Some(parent))
.map_err(Into::into)
}
InlineActionBehaviour::Lift => {
tracing::debug!("Lift action for InlineAction");
let consuming_iter = ConsumingIter::new(task, channel.clone());
executor
.lift(consuming_iter.into(), key)
.map_err(Into::into)
}
InlineActionBehaviour::Schedule => {
tracing::debug!("Schedule action for InlineAction");
let consuming_iter = ConsumingIter::new(task, channel.clone());
executor.schedule(consuming_iter.into()).map_err(Into::into)
}
InlineActionBehaviour::Broadcast => {
tracing::debug!("Broadcast action for InlineAction");
let consuming_iter = ConsumingIter::new(task, channel.clone());
executor
.broadcast(consuming_iter.into())
.map_err(Into::into)
}
}
} else {
Err("Action has being used up".into())
}
}
}
#[must_use]
pub fn initialize_pool(
seed_for_rng: u64,
user_thread_num: Option<usize>,
) -> crate::valtron::multi::PoolGuard {
tracing::debug!("Starting under feature=multi");
multi::initialize_pool(seed_for_rng, user_thread_num)
}
pub fn run_background_job(job: impl FnOnce() + Send + 'static) -> GenericResult<()> {
#[cfg(target_family = "wasm")]
{
super::single::run_background_job(job)
}
#[cfg(not(target_family = "wasm"))]
{
super::multi::run_background_job(job)
}
}
pub fn execute_as_task<T>(
task: T,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<DrivenRecvIterator<T>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
tracing::debug!("Executing as a multi-threaded stream in no-wasm with multi=off");
use crate::valtron::multi;
let iter = multi::spawn()
.with_task(task)
.schedule_iter(wait_cycle.unwrap_or(DEFAULT_WAIT_CYCLE))?;
Ok(drive_receiver(iter))
}
pub fn execute<T>(
task: T,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<DrivenStreamIterator<T>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
tracing::debug!("Executing as a multi-threaded stream in no-wasm");
let iter = multi::spawn().with_task(task).stream_iter_with_config(
wait_cycle.unwrap_or(DEFAULT_PARK_DURATION),
DEFAULT_MAX_TURNS,
)?;
Ok(drive_stream(iter))
}
pub fn execute_with_config<T>(
task: T,
config: StreamConfig,
) -> GenericResult<DrivenStreamIterator<T>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
use crate::valtron::multi;
tracing::debug!("Executing as a multi-threaded stream in no-wasm with config");
let iter = multi::spawn()
.with_task(task)
.stream_iter_with_config(config.park_duration, config.max_turns)?;
Ok(drive_stream(iter))
}
pub fn send<T>(task: T) -> GenericResult<()>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
tracing::debug!("Executing as a multi-threaded stream in no-wasm");
multi::spawn().with_task(task).schedule()?;
Ok(())
}
pub fn run_future_iter<F, Fut, I, T, E>(
future_fn: F,
queue_size: Option<usize>,
backpressure_sleep: Option<std::time::Duration>,
) -> crate::valtron::GenericResult<impl Iterator<Item = ThreadedValue<T, E>>>
where
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = Result<I, E>> + 'static,
I: Iterator<Item = Result<T, E>> + 'static,
T: Send + 'static,
E: Send + 'static,
{
use crate::valtron::ThreadedIterFuture;
tracing::debug!("Executing as a multi-threaded future iterator");
let threaded = ThreadedIterFuture::with_backpressure_sleep(
future_fn,
queue_size.unwrap_or(16),
backpressure_sleep.or(Some(std::time::Duration::from_millis(10))),
);
threaded.execute()
}
pub fn run_future<F>(future: F) -> crate::valtron::GenericResult<Vec<F::Output>>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let task = FutureTask::new(future);
let iter: DrivenRecvIterator<FutureTask<F>> = execute_as_task(task, None)?;
let values_iter = ReadyValues::new(iter);
let values: Vec<F::Output> = values_iter
.filter_map(super::super::task::ReadyValue::inner)
.collect();
Ok(values)
}
pub fn from_stream<S>(stream: S) -> StreamTask<S>
where
S: futures_core::Stream + Send + 'static,
S::Item: Send + 'static,
{
StreamTask::new(stream)
}
pub fn from_future<F>(future: F) -> FutureTask<F>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
FutureTask::new(future)
}
pub fn drive_future<F>(future: F) -> DrivenTaskIterator<FutureTask<F>>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
drive_iterator(crate::valtron::from_future(future))
}
pub fn drive_future_stream<S>(stream: S) -> DrivenTaskIterator<StreamTask<S>>
where
S: futures_core::Stream + Send + 'static,
S::Item: Send + 'static,
{
drive_iterator(crate::valtron::from_stream(stream))
}
pub fn from_cancellable_future<F>(
future: F,
cancel: Arc<AtomicBool>,
) -> CancellableFutureTask<F>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
CancellableFutureTask::new(future, cancel)
}
#[must_use]
pub fn cancel_signal() -> Arc<AtomicBool> {
Arc::new(AtomicBool::new(false))
}
pub fn drive_cancellable_future<F>(
future: F,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<(
DrivenStreamIterator<CancellableFutureTask<F>>,
Arc<AtomicBool>,
)>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let signal = cancel_signal();
let task = from_cancellable_future(future, Arc::clone(&signal));
let stream = execute(task, wait_cycle)?;
Ok((stream, signal))
}
pub fn execute_cancellable_futures<F>(
futures: Vec<F>,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<(
CollectNextFromAllStream<CancellableFutureTask<F>>,
Vec<Arc<AtomicBool>>,
)>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let mut signals = Vec::with_capacity(futures.len());
let tasks: Vec<CancellableFutureTask<F>> = futures
.into_iter()
.map(|f| {
let sig = cancel_signal();
signals.push(Arc::clone(&sig));
from_cancellable_future(f, sig)
})
.collect();
let stream = execute_collect_next_from_all(tasks, wait_cycle)?;
Ok((stream, signals))
}
#[must_use]
pub fn inlined_task<Done, Pending, Action, Task>(
behaviour: InlineActionBehaviour,
task: Task,
wait_cycle: std::time::Duration,
) -> (
InlineAction<Done, Pending, Action, Task>,
DrivenRecvIterator<Task>,
)
where
Done: Send + 'static,
Pending: Send + 'static,
Action: ExecutionAction + Send + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + Send + 'static,
{
let (task_action, task_receiver) = InlineAction::new(behaviour, task, wait_cycle);
(task_action, drive_receiver(task_receiver))
}
#[must_use]
#[tracing::instrument(skip(incoming))]
pub fn drive_iterator<T>(incoming: T) -> DrivenTaskIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
DrivenTaskIterator::new(incoming)
}
#[must_use]
#[tracing::instrument(skip(incoming))]
pub fn drive_receiver<T>(
incoming: NotifyRecvIterator<TaskStatus<T::Ready, T::Pending, T::Spawner>>,
) -> DrivenRecvIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
DrivenRecvIterator::new(incoming)
}
#[must_use]
#[tracing::instrument(skip(incoming))]
pub fn drive_stream<T>(
incoming: NotifyQueueStreamIterator<T::Ready, T::Pending>,
) -> DrivenStreamIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
DrivenStreamIterator::new(incoming)
}
pub fn sync_collect_one<T>(task: T) -> GenericResult<T::Ready>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
let stream = execute(task, None)?;
collect_one(stream).ok_or_else(|| "sync_collect_one: stream produced no result".into())
}
pub fn block_on_future<F>(future: F) -> F::Output
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
sync_collect_one(from_future(future)).expect(
"block_on_future: the future produced no result (pool not initialized or torn down early)",
)
}
pub fn sync_one<T>(task: T) -> GenericResult<Vec<T::Ready>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
let stream = execute(task, None)?;
Ok(collect_result(stream))
}
pub fn sync_all<T>(tasks: Vec<T>) -> GenericResult<Vec<T::Ready>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
if tasks.is_empty() {
return Err("empty tasks not allowed".into());
}
let stream = execute_collect_all(tasks, None)?;
stream
.filter_map(|s| match s {
Stream::Next(v) => Some(v),
_ => None,
})
.next()
.ok_or_else(|| "sync_all: no results produced by execute_collect_all".into())
}
pub fn execute_collect_all<T>(
tasks: Vec<T>,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<CollectAllTaskStream<T>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
let streams: Vec<DrivenStreamIterator<T>> = tasks
.into_iter()
.map(|t| execute(t, wait_cycle))
.collect::<GenericResult<_>>()?;
Ok(CollectAllTaskStream::new(streams))
}
pub struct DrivenTaskIterator<T>(Option<T>)
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static;
impl<T> DrivenTaskIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
pub fn new(task_iterator: T) -> Self {
Self(Some(task_iterator))
}
}
unsafe impl<T> Send for DrivenTaskIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
}
impl<T> Iterator for DrivenTaskIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
type Item = TaskStatus<T::Ready, T::Pending, T::Spawner>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut task_iterator) = self.0.take() {
let next_value = task_iterator.next_status();
if next_value.is_some() {
self.0.replace(task_iterator);
}
next_value
} else {
None
}
}
}
pub struct DrivenStreamIterator<T>(Option<NotifyQueueStreamIterator<T::Ready, T::Pending>>)
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static;
impl<T> DrivenStreamIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
#[must_use]
pub fn new(task_iterator: NotifyQueueStreamIterator<T::Ready, T::Pending>) -> Self {
Self(Some(task_iterator))
}
pub fn is_empty(&self) -> bool {
match &self.0 {
Some(inner) => inner.is_empty(),
None => true,
}
}
#[must_use]
pub fn is_closed(&self) -> bool {
match &self.0 {
Some(inner) => inner.is_closed(),
None => true,
}
}
}
unsafe impl<T> Send for DrivenStreamIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
}
impl<T> Iterator for DrivenStreamIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
type Item = Stream<T::Ready, T::Pending>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut task_iterator) = self.0.take() {
let next_value = task_iterator.next();
if next_value.is_some() {
self.0.replace(task_iterator);
}
next_value
} else {
None
}
}
}
pub struct DrivenRecvIterator<T>(
Option<NotifyRecvIterator<TaskStatus<T::Ready, T::Pending, T::Spawner>>>,
)
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static;
impl<T> DrivenRecvIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
#[must_use]
pub fn new(
task_iterator: NotifyRecvIterator<TaskStatus<T::Ready, T::Pending, T::Spawner>>,
) -> Self {
Self(Some(task_iterator))
}
}
unsafe impl<T> Send for DrivenRecvIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
}
impl<T> Iterator for DrivenRecvIterator<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
type Item = TaskStatus<T::Ready, T::Pending, T::Spawner>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut task_iterator) = self.0.take() {
let next_value = task_iterator.next();
match next_value {
Some(NotificationItem::Ready(status)) => {
self.0.replace(task_iterator);
Some(status)
}
Some(NotificationItem::None) => {
self.0.replace(task_iterator);
Some(TaskStatus::Wait)
}
None => None, }
} else {
None
}
}
}
pub struct CollectAllTaskStream<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
sources: Vec<DrivenStreamIterator<T>>,
collected: Vec<T::Ready>,
done: bool,
current_index: usize,
}
impl<T> CollectAllTaskStream<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
#[must_use]
pub fn new(sources: Vec<DrivenStreamIterator<T>>) -> Self {
Self {
sources,
collected: Vec::new(),
done: false,
current_index: 0,
}
}
}
impl<T> Iterator for CollectAllTaskStream<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
type Item = Stream<Vec<T::Ready>, usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
if self.sources.is_empty() {
self.done = true;
if self.collected.is_empty() {
return None;
}
return Some(Stream::Next(std::mem::take(&mut self.collected)));
}
let idx = self.current_index;
match self.sources[idx].next() {
Some(Stream::Next(value)) => {
self.collected.push(value);
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Pending(self.sources.len()))
}
Some(Stream::Pending(_)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Pending(self.sources.len()))
}
Some(Stream::Delayed(d)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Delayed(d))
}
Some(Stream::Init) => {
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Pending(self.sources.len()))
}
Some(Stream::Ignore) => {
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Pending(self.sources.len()))
}
Some(Stream::Wait) => {
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Pending(self.sources.len()))
}
Some(Stream::Spread(items)) => {
for item in items {
if let crate::valtron::streams::StreamSpread::Done(d) = item {
self.collected.push(d);
}
}
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Pending(self.sources.len()))
}
None => {
self.sources.swap_remove(idx);
if self.current_index >= self.sources.len() && !self.sources.is_empty() {
self.current_index = 0;
}
if self.sources.is_empty() {
self.done = true;
if self.collected.is_empty() {
return None;
}
Some(Stream::Next(std::mem::take(&mut self.collected)))
} else {
Some(Stream::Pending(self.sources.len()))
}
}
}
}
}
pub fn collect_all_streams<S>(streams: Vec<S>) -> CollectAllStreams<S>
where
S: StreamIterator<P = usize> + Send + 'static,
S::D: Send + 'static,
{
CollectAllStreams::new(streams)
}
pub fn one_shot<D, P>(value: D) -> OneShotStream<D, P>
where
D: Send + 'static,
P: Send + 'static,
{
OneShotStream {
value: Some(value),
_phantom: std::marker::PhantomData,
}
}
pub struct OneShotStream<D, P> {
value: Option<D>,
_phantom: std::marker::PhantomData<P>,
}
impl<D, P> Iterator for OneShotStream<D, P>
where
D: Send + 'static,
P: Send + 'static,
{
type Item = Stream<D, P>;
fn next(&mut self) -> Option<Self::Item> {
self.value.take().map(Stream::Next)
}
}
pub struct CollectAllStreams<S>
where
S: StreamIterator<P = usize> + Send + 'static,
S::D: Send + 'static,
{
sources: Vec<S>,
collected: Vec<S::D>,
done: bool,
current_index: usize,
}
impl<S> CollectAllStreams<S>
where
S: StreamIterator<P = usize> + Send + 'static,
S::D: Send + 'static,
{
#[must_use]
pub fn new(sources: Vec<S>) -> Self {
Self {
sources,
collected: Vec::new(),
done: false,
current_index: 0,
}
}
}
impl<S> Iterator for CollectAllStreams<S>
where
S: StreamIterator<P = usize> + Send + 'static,
S::D: Send + 'static,
{
type Item = Stream<Vec<S::D>, usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
if self.sources.is_empty() {
self.done = true;
if self.collected.is_empty() {
return None;
}
return Some(Stream::Next(std::mem::take(&mut self.collected)));
}
let mut exhausted_indices = Vec::new();
for _ in 0..self.sources.len() {
let idx = self.current_index;
match self.sources[idx].next() {
Some(Stream::Next(value)) => {
self.collected.push(value);
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Pending(self.sources.len()));
}
Some(Stream::Pending(_)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Pending(self.sources.len()));
}
Some(Stream::Delayed(d)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Delayed(d));
}
Some(Stream::Init) => {
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Pending(self.sources.len()));
}
Some(Stream::Ignore) => {
self.current_index = (self.current_index + 1) % self.sources.len();
}
Some(Stream::Wait) => {
self.current_index = (self.current_index + 1) % self.sources.len();
}
Some(Stream::Spread(items)) => {
for item in items {
if let crate::valtron::streams::StreamSpread::Done(d) = item {
self.collected.push(d);
}
}
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Pending(self.sources.len()));
}
None => {
exhausted_indices.push(idx);
self.current_index = (self.current_index + 1) % self.sources.len();
}
}
}
exhausted_indices.sort_by(|a, b| b.cmp(a));
for idx in exhausted_indices {
self.sources.swap_remove(idx);
}
if !self.sources.is_empty() && self.current_index >= self.sources.len() {
self.current_index = 0;
}
if self.sources.is_empty() {
self.done = true;
if self.collected.is_empty() {
return None;
}
return Some(Stream::Next(std::mem::take(&mut self.collected)));
}
Some(Stream::Pending(self.sources.len()))
}
}
pub fn execute_map_all<T, F, O>(
tasks: Vec<T>,
mapper: F,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<MapAllDoneStream<T, F, O>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
F: Fn(Vec<T::Ready>) -> O + Send + 'static,
O: Send + 'static,
{
let streams: Vec<DrivenStreamIterator<T>> = tasks
.into_iter()
.map(|t| execute(t, wait_cycle))
.collect::<GenericResult<_>>()?;
Ok(MapAllDoneStream::new(streams, mapper))
}
pub struct MapAllDoneStream<T, F, O>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
sources: Vec<DrivenStreamIterator<T>>,
mapper: F,
buffer: Vec<Option<T::Ready>>,
done: bool,
_phantom: std::marker::PhantomData<O>,
}
impl<T, F, O> MapAllDoneStream<T, F, O>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
pub fn new(sources: Vec<DrivenStreamIterator<T>>, mapper: F) -> Self {
let len = sources.len();
Self {
sources,
mapper,
buffer: (0..len).map(|_| None).collect(),
done: false,
_phantom: std::marker::PhantomData,
}
}
}
impl<T, F, O> Iterator for MapAllDoneStream<T, F, O>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
F: Fn(Vec<T::Ready>) -> O,
{
type Item = Stream<O, usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
let mut all_done = true;
let mut has_pending = false;
let mut max_delayed: Option<std::time::Duration> = None;
for (i, source) in self.sources.iter_mut().enumerate() {
if self.buffer[i].is_some() {
continue; }
match source.next() {
Some(Stream::Next(value)) => {
self.buffer[i] = Some(value);
}
Some(Stream::Pending(_)) => {
all_done = false;
has_pending = true;
}
Some(Stream::Delayed(d)) => {
all_done = false;
max_delayed = Some(match max_delayed {
Some(current) => current.max(d),
None => d,
});
}
Some(Stream::Init) => {
all_done = false;
}
Some(Stream::Ignore) => {
all_done = false;
}
Some(Stream::Wait) => {
all_done = false;
}
Some(Stream::Spread(values)) => {
if let Some(last) = values.into_iter().find_map(|item| match item {
crate::valtron::streams::StreamSpread::Done(d) => Some(d),
_ => None,
}) {
self.buffer[i] = Some(last);
}
all_done = false;
has_pending = true;
}
None => {
}
}
}
if self.buffer.iter().all(std::option::Option::is_some) {
self.done = true;
let values: Vec<T::Ready> = self.buffer.drain(..).flatten().collect();
let result = (self.mapper)(values);
return Some(Stream::Next(result));
}
if all_done {
self.done = true;
return None;
}
if let Some(delay) = max_delayed {
Some(Stream::Delayed(delay))
} else if has_pending {
let collected: usize = self.buffer.iter().filter(|x| x.is_some()).count();
Some(Stream::Pending(collected))
} else {
Some(Stream::Init)
}
}
}
pub fn execute_map_all_pending_and_done<T, F, O, R>(
tasks: Vec<T>,
mapper: F,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<MapAllPendingAndDoneStream<T, F, O, R>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
F: Fn(Vec<Stream<T::Ready, T::Pending>>) -> Vec<Stream<O, R>> + Send + 'static,
O: Send + 'static,
R: Send + 'static,
{
let streams: Vec<DrivenStreamIterator<T>> = tasks
.into_iter()
.map(|t| execute(t, wait_cycle))
.collect::<GenericResult<_>>()?;
Ok(MapAllPendingAndDoneStream::new(streams, mapper))
}
pub struct MapAllPendingAndDoneStream<T, F, O, R>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
sources: Vec<DrivenStreamIterator<T>>,
mapper: F,
done: bool,
_phantom: std::marker::PhantomData<(O, R)>,
}
impl<T, F, O, R> MapAllPendingAndDoneStream<T, F, O, R>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
pub fn new(sources: Vec<DrivenStreamIterator<T>>, mapper: F) -> Self {
Self {
sources,
mapper,
done: false,
_phantom: std::marker::PhantomData,
}
}
}
impl<T, F, O, R> Iterator for MapAllPendingAndDoneStream<T, F, O, R>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
F: Fn(Vec<Stream<T::Ready, T::Pending>>) -> Vec<Stream<O, R>>,
O: Send + 'static,
R: Send + 'static,
{
type Item = Stream<Vec<Stream<O, R>>, ()>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
let mut states: Vec<Stream<T::Ready, T::Pending>> = Vec::with_capacity(self.sources.len());
for source in &mut self.sources {
if let Some(state) = source.next() {
states.push(state);
}
}
if states.is_empty() {
self.done = true;
return None;
}
Some(Stream::Next((self.mapper)(states)))
}
}
#[cfg(feature = "multi")]
pub fn execute_collect_next_from_all<T>(
tasks: Vec<T>,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<CollectNextFromAllStream<T>>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
let streams: Vec<DrivenStreamIterator<T>> = tasks
.into_iter()
.map(|t| execute(t, wait_cycle))
.collect::<GenericResult<_>>()?;
Ok(CollectNextFromAllStream::new(streams))
}
pub struct CollectNextFromAllStream<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
sources: Vec<DrivenStreamIterator<T>>,
current_index: usize,
_phantom: std::marker::PhantomData<T>,
}
impl<T> CollectNextFromAllStream<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
#[must_use]
pub fn new(sources: Vec<DrivenStreamIterator<T>>) -> Self {
Self {
sources,
current_index: 0,
_phantom: std::marker::PhantomData,
}
}
}
impl<T> Iterator for CollectNextFromAllStream<T>
where
T: TaskIterator + Send + 'static,
T::Ready: Send + 'static,
T::Pending: Send + 'static,
T::Spawner: ExecutionAction + Send + 'static,
{
type Item = Stream<T::Ready, usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.sources.is_empty() {
return None;
}
let mut exhausted_indices = Vec::new();
for _ in 0..self.sources.len() {
let idx = self.current_index;
match self.sources[idx].next() {
Some(Stream::Next(value)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Next(value));
}
Some(Stream::Pending(_)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Pending(self.sources.len()));
}
Some(Stream::Delayed(d)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Delayed(d));
}
Some(Stream::Init) => {
self.current_index = (self.current_index + 1) % self.sources.len();
return Some(Stream::Pending(self.sources.len()));
}
Some(Stream::Ignore) => {
self.current_index = (self.current_index + 1) % self.sources.len();
}
Some(Stream::Wait) => {
self.current_index = (self.current_index + 1) % self.sources.len();
}
Some(Stream::Spread(items)) => {
self.current_index = (self.current_index + 1) % self.sources.len();
for item in items {
if let crate::valtron::streams::StreamSpread::Done(first) = item {
return Some(Stream::Next(first));
}
}
}
None => {
exhausted_indices.push(idx);
self.current_index = (self.current_index + 1) % self.sources.len();
}
}
}
exhausted_indices.sort_by(|a, b| b.cmp(a));
for idx in exhausted_indices {
self.sources.swap_remove(idx);
}
if !self.sources.is_empty() && self.current_index >= self.sources.len() {
self.current_index = 0;
}
if self.sources.is_empty() {
return None;
}
Some(Stream::Pending(self.sources.len()))
}
}
pub fn schedule_future<T, E, F>(
future: F,
) -> GenericResult<impl Iterator<Item = Stream<Result<T, E>, ()>> + Send + 'static>
where
F: Future<Output = Result<T, E>> + Send + 'static,
F::Output: Send + 'static,
T: Send + 'static,
E: Send + 'static,
{
let task = from_future(future);
execute(task, None).map(|stream| {
stream.map(|item| match item {
Stream::Next(result) => Stream::Next(result),
Stream::Pending(_) => Stream::Pending(()),
Stream::Init => Stream::Init,
Stream::Ignore => Stream::Ignore,
Stream::Wait => Stream::Wait,
Stream::Delayed(d) => Stream::Delayed(d),
Stream::Spread(items) => Stream::Spread(items.into_iter().map(|item| {
match item {
crate::valtron::streams::StreamSpread::Done(result) => {
crate::valtron::streams::StreamSpread::Done(result)
}
crate::valtron::streams::StreamSpread::Pending(_) => {
crate::valtron::streams::StreamSpread::Pending(())
}
}
}).collect()),
})
})
}
pub fn exec_future<T, E, F>(future: F) -> GenericResult<Result<T, E>>
where
F: Future<Output = Result<T, E>> + Send + 'static,
F::Output: Send + 'static,
T: Send + 'static,
E: Send + 'static,
{
let task = from_future(future);
let stream = execute(task, None)?;
let result: Option<Result<T, E>> = collect_one(stream);
match result {
Some(Ok(v)) => Ok(Ok(v)),
Some(Err(e)) => Ok(Err(e)),
None => Err("no result from future execution".into()),
}
}
pub fn sleep(
duration: std::time::Duration,
) -> crate::valtron::GenericResult<super::DrivenStreamIterator<super::sleep::SleepingTask>>
{
execute(super::sleep::SleepingTask::sleep(duration), None)
}
pub async fn sleep_async(duration: std::time::Duration) {
use crate::valtron::StreamIteratorExt;
let _ = sleep(duration).expect("sleep").into_ready_future().await;
}