#![cfg(not(feature = "multi"))]
use super::single;
use crate::valtron::InlineAction;
use crate::valtron::InlineActionBehaviour;
use crate::valtron::StreamConfig;
use crate::valtron::TaskStatusMapper;
use crate::valtron::DEFAULT_WAIT_CYCLE;
use crate::valtron::{
collect_one, collect_result, ExecutionAction, NotificationItem, NotifyQueueStreamIterator,
NotifyRecvIterator, ProgressIndicator, State, Stream, StreamSpread, TaskIterator, TaskStatus,
};
use crate::valtron::{GenericResult, StreamIterator};
#[cfg(any(feature = "std", feature = "alloc"))]
use core::future::Future;
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::valtron::FutureTask;
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::valtron::StreamTask;
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::valtron::ReadyValues;
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::valtron::ThreadedIterFuture;
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::valtron::ThreadedValue;
#[must_use]
pub fn initialize_pool(
seed_for_rng: u64,
_user_thread_num: Option<usize>,
) -> super::single::PoolGuard {
tracing::debug!("Starting under not(feature=multi), so single threaded");
single::initialize_pool(seed_for_rng);
single::PoolGuard
}
pub fn run_background_job(job: impl FnOnce() + 'static) -> GenericResult<()> {
super::single::run_background_job(job)
}
#[must_use]
#[tracing::instrument(skip(incoming))]
pub fn drive_stream<T>(
incoming: NotifyQueueStreamIterator<T::Ready, T::Pending>,
) -> DrivenStreamIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
DrivenStreamIterator::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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
DrivenRecvIterator::new(incoming)
}
#[must_use]
#[tracing::instrument(skip(incoming))]
pub fn drive_iterator<T>(incoming: T) -> DrivenTaskIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
DrivenTaskIterator::new(incoming)
}
#[must_use]
pub fn inlined_task<Done, Pending, Action, Task>(
behaviour: InlineActionBehaviour,
mappers: Vec<Box<dyn TaskStatusMapper<Done, Pending, Action> + 'static>>,
task: Task,
wait_cycle: std::time::Duration,
) -> (
InlineAction<
Done,
Pending,
Action,
Task,
Box<dyn TaskStatusMapper<Done, Pending, Action> + 'static>,
>,
DrivenRecvIterator<Task>,
)
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
{
let (task_action, task_receiver) =
InlineAction::boxed_mapper(behaviour, mappers, task, wait_cycle);
(task_action, drive_receiver(task_receiver))
}
#[must_use]
pub fn inlined_mapped_task<Done, Pending, Action, Task, Mapper>(
behaviour: InlineActionBehaviour,
mappers: Vec<Mapper>,
task: Task,
wait_cycle: std::time::Duration,
) -> (
InlineAction<Done, Pending, Action, Task, Mapper>,
DrivenRecvIterator<Task>,
)
where
Done: 'static,
Pending: 'static,
Action: ExecutionAction + 'static,
Mapper: TaskStatusMapper<Done, Pending, Action> + 'static,
Task: TaskIterator<Pending = Pending, Ready = Done, Spawner = Action> + 'static,
{
let (task_action, task_receiver) = InlineAction::new(behaviour, mappers, task, wait_cycle);
(task_action, drive_receiver(task_receiver))
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[must_use]
#[tracing::instrument(skip(stream))]
pub fn drive_future_stream<S>(stream: S) -> DrivenTaskIterator<StreamTask<S>>
where
S: futures_core::Stream + 'static,
S::Item: 'static,
{
drive_iterator(crate::valtron::from_stream(stream))
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn drive_future<F>(future: F) -> DrivenTaskIterator<FutureTask<F>>
where
F: Future + 'static,
F::Output: 'static,
{
drive_iterator(crate::valtron::from_future(future))
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn from_future<F>(future: F) -> FutureTask<F>
where
F: Future + 'static,
F::Output: 'static,
{
FutureTask::new(future)
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn from_stream<S>(stream: S) -> StreamTask<S>
where
S: futures_core::Stream + 'static,
S::Item: 'static,
{
StreamTask::new(stream)
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn run_future<F>(future: F) -> crate::valtron::GenericResult<Vec<F::Output>>
where
F: Future + 'static,
F::Output: 'static,
{
let task = FutureTask::new(future);
let iter: crate::valtron::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)
}
#[cfg(any(feature = "std", feature = "alloc"))]
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 + 'static,
Fut: Future<Output = Result<I, E>> + 'static,
I: Iterator<Item = Result<T, E>> + 'static,
T: 'static,
E: 'static,
{
let _ = (queue_size, backpressure_sleep);
Ok(ThreadedIterFuture::new(future_fn).execute())
}
pub fn send<T>(task: T) -> GenericResult<()>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
use super::single;
tracing::debug!("Executing as a single stream in wasm");
single::spawn().with_task(task).schedule()?;
Ok(())
}
pub fn execute_with_config<T>(
task: T,
config: StreamConfig,
) -> GenericResult<DrivenStreamIterator<T>>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
use super::single;
tracing::debug!("Executing as a single stream in wasm with config");
let iter = single::spawn()
.with_task(task)
.scheduled_stream_iter_with_config(config.park_duration, config.max_turns)?;
Ok(drive_stream(iter))
}
pub fn execute<T>(
task: T,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<DrivenStreamIterator<T>>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
use super::single;
use crate::valtron::executors::{DEFAULT_MAX_TURNS, DEFAULT_PARK_DURATION};
tracing::debug!("Executing as a single-threaded stream in no-wasm with multi=off");
let iter = single::spawn()
.with_task(task)
.scheduled_stream_iter_with_config(
wait_cycle.unwrap_or(DEFAULT_PARK_DURATION),
DEFAULT_MAX_TURNS,
)?;
Ok(drive_stream(iter))
}
pub fn execute_as_task<T>(
task: T,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<DrivenRecvIterator<T>>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
use crate::valtron::{drive_receiver, single};
tracing::debug!("Executing as a single-threaded stream in no-wasm");
let iter = single::spawn()
.with_task(task)
.schedule_iter(wait_cycle.unwrap_or(DEFAULT_WAIT_CYCLE))?;
Ok(drive_receiver(iter))
}
pub fn sync_collect_one<T>(task: T) -> GenericResult<T::Ready>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
let stream = execute(task, None)?;
collect_one(stream).ok_or_else(|| "sync_collect_one: stream produced no result".into())
}
pub fn sync_one<T>(task: T) -> GenericResult<Vec<T::Ready>>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
if tasks.is_empty() {
return Err("empty tasks not allowed".into());
}
let mut stream = execute_collect_all(tasks, None)?;
stream
.find_map(|s| match s {
Stream::Next(v) => Some(v),
_ => None,
})
.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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
let streams: Vec<DrivenStreamIterator<T>> = tasks
.into_iter()
.map(|t| execute(t, wait_cycle))
.collect::<GenericResult<_>>()?;
Ok(CollectAllTaskStream::new(streams))
}
#[tracing::instrument()]
pub fn run_until_next_state() {
run_until_next_acceptable_state(|candidate| {
!matches!(
candidate,
State::SpawnFailed(_)
| State::SpawnFinished(_)
| State::Reschedule
| State::Done
| State::Wait
)
});
}
pub fn run_until_next_acceptable_state<T>(checker: T)
where
T: Fn(&State) -> bool,
{
#[cfg_attr(not(feature = "multi"), allow(unused_variables))]
run_until(|indicator: ProgressIndicator| {
if let ProgressIndicator::CanProgress(Some(state)) = indicator {
if checker(&state) {
tracing::debug!("Valtron: checker returned true for state={state:?}");
true
} else {
false
}
} else {
false
}
});
}
pub fn run_until_next_state_within(target_states: &[State]) {
run_until(|indicator: ProgressIndicator| {
if let ProgressIndicator::CanProgress(Some(state)) = indicator {
if target_states.contains(&state) {
tracing::debug!("Valtron: Seen new state value from state={state:?}");
true
} else {
false
}
} else {
false
}
});
}
#[tracing::instrument()]
pub fn run_until_ready_state() {
run_until(|indicator: ProgressIndicator| {
if let ProgressIndicator::CanProgress(Some(State::ReadyValue(task_id))) = indicator {
tracing::debug!("Valtron: Seen ready value from task id={task_id:?}");
true
} else {
false
}
});
}
#[tracing::instrument(skip(stream, checker))]
pub fn run_until_receiver_has_value<T, S>(
stream: NotifyRecvIterator<TaskStatus<T::Ready, T::Pending, T::Spawner>>,
#[allow(unused_variables)] checker: S,
) -> NotifyRecvIterator<TaskStatus<T::Ready, T::Pending, T::Spawner>>
where
S: Fn(ProgressIndicator) -> bool,
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
while stream.is_empty() {
single::run_until(&checker);
}
stream
}
#[tracing::instrument(skip(stream, checker))]
pub fn run_until_stream_has_value<T, S>(
stream: NotifyQueueStreamIterator<T::Ready, T::Pending>,
#[allow(unused_variables)] checker: S,
) -> NotifyQueueStreamIterator<T::Ready, T::Pending>
where
S: Fn(ProgressIndicator) -> bool,
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
while stream.is_empty() && !stream.is_closed() {
single::run_until(&checker);
}
stream
}
#[tracing::instrument(skip(checker))]
pub fn run_until<T>(#[allow(unused_variables)] checker: T)
where
T: Fn(ProgressIndicator) -> bool,
{
tracing::debug!("Executing as a single-threaded stream in no-wasm");
single::run_until(checker);
}
#[tracing::instrument()]
pub fn run_until_complete() {
single::run_until_complete();
}
#[tracing::instrument()]
pub fn run_once() {
tracing::trace!("Executing as a single stream in wasm");
let _ = single::run_once();
}
pub struct DrivenStreamIterator<T>(Option<NotifyQueueStreamIterator<T::Ready, T::Pending>>)
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static;
impl<T> DrivenStreamIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
#[must_use]
pub fn new(task_iterator: NotifyQueueStreamIterator<T::Ready, T::Pending>) -> Self {
Self(Some(task_iterator))
}
}
impl<T> Iterator for DrivenStreamIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
type Item = Stream<T::Ready, T::Pending>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut task_iterator) = self.0.take() {
tracing::trace!("Run: run_until_next_state");
run_until_next_state();
let next_value = task_iterator.next();
if next_value.is_some() {
self.0.replace(task_iterator);
}
next_value
} else {
None
}
}
}
pub struct DrivenTaskIterator<T>(Option<T>)
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static;
impl<T> DrivenTaskIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
pub fn new(task_iterator: T) -> Self {
Self(Some(task_iterator))
}
}
impl<T> Iterator for DrivenTaskIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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() {
run_until_next_state();
let next_value = task_iterator.next_status();
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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static;
impl<T> DrivenRecvIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
#[must_use]
pub fn new(
task_iterator: NotifyRecvIterator<TaskStatus<T::Ready, T::Pending, T::Spawner>>,
) -> Self {
Self(Some(task_iterator))
}
}
impl<T> Iterator for DrivenRecvIterator<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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() {
tracing::trace!("Run: run_until_next_state");
run_until_next_state();
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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
sources: Vec<DrivenStreamIterator<T>>,
collected: Vec<T::Ready>,
done: bool,
current_index: usize,
}
impl<T> CollectAllTaskStream<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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)) => {
let pending_count = items.iter().filter(|i| matches!(i, StreamSpread::Pending(_))).count();
for item in items {
if let StreamSpread::Done(d) = item {
self.collected.push(d);
}
}
self.current_index = (self.current_index + 1) % self.sources.len();
Some(Stream::Pending(self.sources.len() + pending_count))
}
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> + 'static,
S::D: 'static,
{
CollectAllStreams::new(streams)
}
pub fn one_shot<D, P>(value: D) -> OneShotStream<D, P>
where
D: 'static,
P: '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: 'static,
P: '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> + 'static,
S::D: 'static,
{
sources: Vec<S>,
collected: Vec<S::D>,
done: bool,
current_index: usize,
}
impl<S> CollectAllStreams<S>
where
S: StreamIterator<P = usize> + 'static,
S::D: '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> + 'static,
S::D: '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 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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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(items)) => {
for item in items {
match item {
StreamSpread::Done(d) => {
self.buffer[i] = Some(d);
}
StreamSpread::Pending(_) => {
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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
F: Fn(Vec<Stream<T::Ready, T::Pending>>) -> Vec<Stream<O, R>> + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
F: Fn(Vec<Stream<T::Ready, T::Pending>>) -> Vec<Stream<O, R>>,
{
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)))
}
}
pub fn execute_collect_next_from_all<T>(
tasks: Vec<T>,
wait_cycle: Option<std::time::Duration>,
) -> GenericResult<CollectNextFromAllStream<T>>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + 'static,
{
sources: Vec<DrivenStreamIterator<T>>,
current_index: usize,
_phantom: std::marker::PhantomData<T>,
}
impl<T> CollectNextFromAllStream<T>
where
T: TaskIterator + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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 + 'static,
T::Ready: 'static,
T::Pending: 'static,
T::Spawner: ExecutionAction + '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();
let mapped: Vec<StreamSpread<T::Ready, usize>> = items
.into_iter()
.map(|item| match item {
StreamSpread::Done(d) => StreamSpread::Done(d),
StreamSpread::Pending(_) => StreamSpread::Pending(self.sources.len()),
})
.collect();
return Some(Stream::Spread(mapped));
}
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()))
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn schedule_future<T, E, F>(
future: F,
) -> GenericResult<impl Iterator<Item = Stream<Result<T, E>, ()>> + 'static>
where
F: Future<Output = Result<T, E>> + 'static,
F::Output: 'static,
T: 'static,
E: '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 {
StreamSpread::Done(result) => StreamSpread::Done(result),
StreamSpread::Pending(_) => StreamSpread::Pending(()),
}
}).collect()),
})
})
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn exec_future<T, E, F>(future: F) -> GenericResult<Result<T, E>>
where
F: Future<Output = Result<T, E>> + 'static,
F::Output: 'static,
T: 'static,
E: '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()),
}
}