use crate::service_protocol::messages::{CombinatorType, Future};
use crate::service_protocol::{CompletionId, Notification, NotificationId, NotificationResult};
use crate::vm::errors::BadProposeRunCompletionAck;
use crate::{Error, NotificationHandle, UnresolvedFuture, CANCEL_NOTIFICATION_HANDLE};
use std::collections::{HashMap, HashSet, VecDeque};
use tracing::instrument;
#[derive(Debug)]
pub(crate) struct AsyncResultsState {
to_process: VecDeque<Notification>,
ready: HashMap<NotificationId, NotificationResult>,
cached_run_completions: HashMap<CompletionId, NotificationResult>,
handle_mapping: HashMap<NotificationHandle, NotificationId>,
next_notification_handle: NotificationHandle,
}
impl Default for AsyncResultsState {
fn default() -> Self {
Self {
to_process: Default::default(),
ready: Default::default(),
cached_run_completions: Default::default(),
handle_mapping: HashMap::from([(
CANCEL_NOTIFICATION_HANDLE,
NotificationId::SignalId(1),
)]),
next_notification_handle: NotificationHandle(17),
}
}
}
#[derive(Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub(crate) enum ResolveFutureResult {
AnyCompleted,
WaitExternalInput(UnresolvedFuture),
}
impl AsyncResultsState {
#[instrument(
level = "trace",
skip_all,
fields(
restate.journal.notification.id = ?notification.id,
),
ret
)]
pub(crate) fn enqueue(&mut self, notification: Notification) {
self.to_process.push_back(notification);
}
#[instrument(
level = "trace",
skip_all,
fields(
restate.journal.completion.id = ?completion_id,
),
ret
)]
pub(crate) fn cache_run_completion(
&mut self,
completion_id: CompletionId,
notification_result: NotificationResult,
) {
self.cached_run_completions
.insert(completion_id, notification_result);
}
#[instrument(
level = "trace",
skip_all,
fields(
restate.journal.completion.id = ?completion_id,
),
ret
)]
pub(crate) fn enqueue_run_completion_ack(
&mut self,
completion_id: CompletionId,
) -> Result<(), Error> {
if let Some(res) = self.cached_run_completions.remove(&completion_id) {
self.to_process.push_back(Notification {
id: NotificationId::CompletionId(completion_id),
result: res,
});
Ok(())
} else {
Err(BadProposeRunCompletionAck::new(completion_id).into())
}
}
#[instrument(
level = "trace",
skip_all,
fields(
restate.journal.notification.id = ?notification.id,
),
ret
)]
pub(crate) fn insert_ready(&mut self, notification: Notification) {
self.ready.insert(notification.id, notification.result);
}
pub(crate) fn create_handle_mapping(
&mut self,
notification_id: NotificationId,
) -> NotificationHandle {
let assigned_handle = self.next_notification_handle;
self.next_notification_handle.0 += 1;
self.handle_mapping.insert(assigned_handle, notification_id);
assigned_handle
}
#[instrument(level = "trace", skip(self), ret)]
pub(crate) fn try_resolve_future(
&mut self,
mut unresolved_future: UnresolvedFuture,
) -> ResolveFutureResult {
loop {
let reduce_future_res = self._try_resolve_future(&mut unresolved_future);
match reduce_future_res {
Ok(handle_state) if handle_state.is_completed() => {
return ResolveFutureResult::AnyCompleted;
}
Err(_) => {
return ResolveFutureResult::AnyCompleted;
}
Ok(_) => {
if !self.pop_notification_queue() {
return ResolveFutureResult::WaitExternalInput(unresolved_future);
}
}
}
}
}
fn _try_resolve_future(
&self,
unresolved_future: &mut UnresolvedFuture,
) -> Result<HandleState, HandleState> {
match unresolved_future {
UnresolvedFuture::Single(h) => Ok(self.resolve_handle_state(*h)),
UnresolvedFuture::FirstCompleted(futures) | UnresolvedFuture::Unknown(futures) => {
let mut any_completed = false;
for fut in futures.iter_mut() {
if self._try_resolve_future(fut)?.is_completed() {
any_completed = true;
break;
}
}
if any_completed {
futures.clear();
Err(HandleState::Succeeded)
} else {
Ok(HandleState::Pending)
}
}
UnresolvedFuture::AllCompleted(futures) => {
let mut i = 0;
while i < futures.len() {
if self._try_resolve_future(&mut futures[i])?.is_completed() {
futures.swap_remove(i);
} else {
i += 1;
}
}
if futures.is_empty() {
Ok(HandleState::Succeeded)
} else {
Ok(HandleState::Pending)
}
}
UnresolvedFuture::FirstSucceededOrAllFailed(futures) => {
let mut i = 0;
while i < futures.len() {
let state = self._try_resolve_future(&mut futures[i])?;
if state == HandleState::Succeeded {
futures.clear();
return Err(HandleState::Succeeded);
} else if state == HandleState::Failed {
futures.swap_remove(i);
} else {
i += 1;
}
}
if futures.is_empty() {
Ok(HandleState::Failed)
} else {
Ok(HandleState::Pending)
}
}
UnresolvedFuture::AllSucceededOrFirstFailed(futures) => {
let mut i = 0;
while i < futures.len() {
let state = self._try_resolve_future(&mut futures[i])?;
if state == HandleState::Failed {
futures.clear();
return Err(HandleState::Failed);
} else if state == HandleState::Succeeded {
futures.swap_remove(i);
} else {
i += 1;
}
}
if futures.is_empty() {
Ok(HandleState::Succeeded)
} else {
Ok(HandleState::Pending)
}
}
}
}
fn pop_notification_queue(&mut self) -> bool {
if let Some(notif) = self.to_process.pop_front() {
self.ready.insert(notif.id, notif.result);
true
} else {
false
}
}
#[instrument(
level = "trace",
skip_all,
fields(
restate.shared_core.notification.handle = ?handle,
),
ret
)]
pub(crate) fn is_handle_completed(&self, handle: NotificationHandle) -> bool {
self.handle_mapping
.get(&handle)
.is_none_or(|id| self.ready.contains_key(id))
}
fn resolve_handle_state(&self, handle: NotificationHandle) -> HandleState {
match self
.handle_mapping
.get(&handle)
.and_then(|id| self.ready.get(id))
{
Some(notif) if notif.is_failure() => HandleState::Failed,
Some(_) => HandleState::Succeeded,
None => HandleState::Pending,
}
}
pub(crate) fn non_deterministic_find_id(&self, id: &NotificationId) -> bool {
if self.ready.contains_key(id) {
return true;
}
self.to_process.iter().any(|notif| notif.id == *id)
}
pub(crate) fn resolve_notification_handles(
&self,
handles: &[NotificationHandle],
) -> HashSet<NotificationId> {
handles
.iter()
.filter_map(|h| self.handle_mapping.get(h).cloned())
.collect()
}
pub(crate) fn resolve_unresolved_future(&self, unresolved_future: UnresolvedFuture) -> Future {
let mut future = Future::default();
let children = match unresolved_future {
UnresolvedFuture::Single(handle) => {
future.combinator_type = CombinatorType::FirstCompleted as i32;
self.push_handle(&mut future, &handle);
return future;
}
UnresolvedFuture::Unknown(c) => c,
UnresolvedFuture::FirstCompleted(c) => {
future.combinator_type = CombinatorType::FirstCompleted as i32;
c
}
UnresolvedFuture::AllCompleted(c) => {
future.combinator_type = CombinatorType::AllCompleted as i32;
c
}
UnresolvedFuture::FirstSucceededOrAllFailed(c) => {
future.combinator_type = CombinatorType::FirstSucceededOrAllFailed as i32;
c
}
UnresolvedFuture::AllSucceededOrFirstFailed(c) => {
future.combinator_type = CombinatorType::AllSucceededOrFirstFailed as i32;
c
}
};
for child in children {
match child {
UnresolvedFuture::Single(handle) => self.push_handle(&mut future, &handle),
other => future
.nested_futures
.push(self.resolve_unresolved_future(other)),
}
}
future
}
fn push_handle(&self, future: &mut Future, handle: &NotificationHandle) {
match self.handle_mapping.get(handle) {
Some(NotificationId::CompletionId(id)) => future.waiting_completions.push(*id),
Some(NotificationId::SignalId(id)) => future.waiting_signals.push(*id),
Some(NotificationId::SignalName(name)) => {
future.waiting_named_signals.push(name.clone())
}
None => {}
}
}
pub(crate) fn must_resolve_notification_handle(
&self,
handle: &NotificationHandle,
) -> NotificationId {
self.handle_mapping
.get(handle)
.expect("If there is an handle, there must be a corresponding id")
.clone()
}
#[instrument(
level = "trace",
skip_all,
fields(
restate.shared_core.notification.handle = ?handle,
),
ret
)]
pub(crate) fn take_handle(&mut self, handle: NotificationHandle) -> Option<NotificationResult> {
let id = self.handle_mapping.get(&handle)?;
if let Some(res) = self.ready.remove(id) {
self.handle_mapping.remove(&handle);
Some(res)
} else {
None
}
}
#[instrument(
level = "trace",
skip_all,
fields(
restate.shared_core.notification.handle = ?handle,
),
ret
)]
pub(crate) fn copy_handle(&mut self, handle: NotificationHandle) -> Option<NotificationResult> {
self.ready.get(self.handle_mapping.get(&handle)?).cloned()
}
}
#[derive(Debug, Eq, PartialEq)]
enum HandleState {
Succeeded,
Failed,
Pending,
}
impl HandleState {
fn is_completed(&self) -> bool {
matches!(self, HandleState::Succeeded | HandleState::Failed)
}
}
impl UnresolvedFuture {
pub(crate) fn handles(&self) -> Vec<NotificationHandle> {
let mut handles = vec![];
match self {
UnresolvedFuture::Single(h) => handles.push(*h),
UnresolvedFuture::Unknown(inner)
| UnresolvedFuture::FirstCompleted(inner)
| UnresolvedFuture::AllCompleted(inner)
| UnresolvedFuture::FirstSucceededOrAllFailed(inner)
| UnresolvedFuture::AllSucceededOrFirstFailed(inner) => {
for fut in inner {
handles.extend(fut.handles());
}
}
};
handles
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::ResolveFutureResult::*;
use crate::service_protocol::messages::{Failure, Void};
use crate::service_protocol::{Notification, NotificationId, NotificationResult};
use crate::{
all_completed, all_succeeded_or_first_failed, first_completed,
first_succeeded_or_all_failed, unknown,
};
use googletest::prelude::*;
use pastey::paste;
fn success(id: u32) -> Notification {
Notification {
id: NotificationId::CompletionId(id),
result: NotificationResult::Void(Void {}),
}
}
fn failure(id: u32) -> Notification {
Notification {
id: NotificationId::CompletionId(id),
result: NotificationResult::Failure(Failure {
code: 500,
message: "fail".to_string(),
..Default::default()
}),
}
}
fn state(enqueued: impl IntoIterator<Item = Notification>) -> AsyncResultsState {
let mut state = AsyncResultsState::default();
for i in 1..=10 {
state
.handle_mapping
.insert(i.into(), NotificationId::CompletionId(i));
}
for notification in enqueued {
state.enqueue(notification);
}
state
}
fn handles(handles: impl IntoIterator<Item = (u32, NotificationId)>) -> AsyncResultsState {
let mut state = AsyncResultsState::default();
for (handle, id) in handles {
state.handle_mapping.insert(handle.into(), id);
}
state
}
macro_rules! test_try_future_resolve {
($test_name:ident: $state:expr, $input:expr => $expected:expr) => {
paste! {
#[test]
fn [<try_resolve_ $test_name>] () {
let mut state = $state;
let fut: UnresolvedFuture = ($input).into();
assert_eq!(state.try_resolve_future(fut), $expected);
}
}
};
}
macro_rules! test_convert_unresolved_future {
($test_name:ident: $state:expr, $input:expr => $expected:expr) => {
paste! {
#[test]
fn [<unresolved_future_to_message_ $test_name>] () {
let state = $state;
let fut: UnresolvedFuture = ($input).into();
assert_that!(state.resolve_unresolved_future(fut), $expected);
}
}
};
}
test_try_future_resolve!(single_succeeded:
state([success(1)]), 1 => AnyCompleted);
test_try_future_resolve!(single_failed:
state([failure(1)]), 1 => AnyCompleted);
test_try_future_resolve!(single_pending:
state([]), 1 => WaitExternalInput(1.into()));
test_try_future_resolve!(first_completed_none_ready:
state([]), first_completed!(1, 2, 3)
=> WaitExternalInput(first_completed!(1, 2, 3)));
test_try_future_resolve!(first_completed_one_succeeded:
state([success(2)]), first_completed!(1, 2, 3)
=> AnyCompleted);
test_try_future_resolve!(first_completed_one_failed:
state([failure(1)]), first_completed!(1, 2, 3)
=> AnyCompleted);
test_try_future_resolve!(first_completed_with_unknown_resolves:
state([success(2)]), first_completed!(1, unknown!(2))
=> AnyCompleted);
test_try_future_resolve!(first_completed_unknown_wrapping_combinator_resolves_on_leaf:
state([success(3)]),
first_completed!(unknown!(all_completed!(1, 2)), 3)
=> AnyCompleted);
test_try_future_resolve!(first_completed_unknown_wrapping_combinator_partial_inner:
state([success(1)]),
first_completed!(unknown!(all_completed!(1, 2)), 3)
=> WaitExternalInput(first_completed!(unknown!(all_completed!(2)), 3)));
test_try_future_resolve!(first_completed_unknown_wrapping_combinator_inner_done:
state([success(1), success(2)]),
first_completed!(unknown!(all_completed!(1, 2)), 3)
=> AnyCompleted);
test_try_future_resolve!(deep_unknown_not_prematurely_resolved:
state([success(1)]),
first_completed!(unknown!(all_completed!(1, unknown!(2))))
=> WaitExternalInput(first_completed!(unknown!(all_completed!(unknown!(2))))));
test_try_future_resolve!(deep_unknown_resolves_when_all_done:
state([success(1), success(2)]),
first_completed!(unknown!(all_completed!(1, unknown!(2))))
=> AnyCompleted);
test_try_future_resolve!(all_completed_none_ready:
state([]), all_completed!(1, 2, 3)
=> WaitExternalInput(all_completed!(1, 2, 3)));
test_try_future_resolve!(all_completed_partial:
state([success(1), failure(3)]), all_completed!(1, 2, 3)
=> WaitExternalInput(all_completed!(2)));
test_try_future_resolve!(all_completed_all_done:
state([success(1), failure(2)]), all_completed!(1, 2)
=> AnyCompleted);
test_try_future_resolve!(all_completed_with_unknown_partial:
state([success(1)]), all_completed!(1, unknown!(2))
=> WaitExternalInput(all_completed!(unknown!(2))));
test_try_future_resolve!(all_completed_with_unknown_all_done:
state([success(1), success(2)]), all_completed!(1, unknown!(2))
=> AnyCompleted);
test_try_future_resolve!(first_succeeded_or_all_failed_none_ready:
state([]), first_succeeded_or_all_failed!(1, 2, 3)
=> WaitExternalInput(first_succeeded_or_all_failed!(1, 2, 3)));
test_try_future_resolve!(first_succeeded_or_all_failed_one_succeeded:
state([success(2)]), first_succeeded_or_all_failed!(1, 2, 3)
=> AnyCompleted);
test_try_future_resolve!(first_succeeded_or_all_failed_some_failed_some_pending:
state([failure(1)]), first_succeeded_or_all_failed!(1, 2, 3)
=> WaitExternalInput(first_succeeded_or_all_failed!(3, 2)));
test_try_future_resolve!(first_succeeded_or_all_failed_all_failed:
state([failure(1), failure(2)]), first_succeeded_or_all_failed!(1, 2)
=> AnyCompleted);
test_try_future_resolve!(fsaf_asff_unknown_success:
state([success(3)]),
first_succeeded_or_all_failed!(1, all_succeeded_or_first_failed!(2, unknown!(3)))
=> AnyCompleted);
test_try_future_resolve!(fsaf_asff_unknown_failure:
state([failure(3)]),
first_succeeded_or_all_failed!(1, all_succeeded_or_first_failed!(2, unknown!(3)))
=> AnyCompleted);
test_try_future_resolve!(nested_fsaf_asff_partial:
state([failure(1)]),
first_succeeded_or_all_failed!(1, all_succeeded_or_first_failed!(2, unknown!(3)))
=> WaitExternalInput(first_succeeded_or_all_failed!(all_succeeded_or_first_failed!(2, unknown!(3)))));
test_try_future_resolve!(fsaf_with_nested_unknown_in_all_completed:
state([success(3)]),
first_succeeded_or_all_failed!(1, all_completed!(2, unknown!(3)))
=> AnyCompleted);
test_try_future_resolve!(fsaf_unknown_asff_resolves_when_inner_done:
state([success(2), success(3)]),
first_succeeded_or_all_failed!(1, unknown!(all_succeeded_or_first_failed!(2, 3)))
=> AnyCompleted);
test_try_future_resolve!(fsaf_unknown_asff_inner_failure_resolves:
state([failure(2)]),
first_succeeded_or_all_failed!(1, unknown!(all_succeeded_or_first_failed!(2, 3)))
=> AnyCompleted);
test_try_future_resolve!(fsaf_unknown_asff_inner_partial:
state([success(2)]),
first_succeeded_or_all_failed!(1, unknown!(all_succeeded_or_first_failed!(2, 3)))
=> WaitExternalInput(first_succeeded_or_all_failed!(1, unknown!(all_succeeded_or_first_failed!(3)))));
test_try_future_resolve!(all_succeeded_or_first_failed_none_ready:
state([]), all_succeeded_or_first_failed!(1, 2, 3)
=> WaitExternalInput(all_succeeded_or_first_failed!(1, 2, 3)));
test_try_future_resolve!(all_succeeded_or_first_failed_all_succeeded:
state([success(1), success(2)]), all_succeeded_or_first_failed!(1, 2)
=> AnyCompleted);
test_try_future_resolve!(all_succeeded_or_first_failed_one_failed:
state([failure(2)]), all_succeeded_or_first_failed!(1, 2, 3)
=> AnyCompleted);
test_try_future_resolve!(all_succeeded_or_first_failed_some_succeeded_some_pending:
state([success(1)]), all_succeeded_or_first_failed!(1, 2, 3)
=> WaitExternalInput(all_succeeded_or_first_failed!(3, 2)));
test_try_future_resolve!(promise_all_short_circuits_on_nested_failure:
state([failure(2)]),
all_succeeded_or_first_failed!(all_succeeded_or_first_failed!(1, 2), 3)
=> AnyCompleted);
test_try_future_resolve!(asff_with_unknown_shortcircuits:
state([failure(2)]),
all_succeeded_or_first_failed!(1, unknown!(2))
=> AnyCompleted);
test_try_future_resolve!(asff_unknown_fsaf_resolves_on_leaf:
state([success(1)]),
all_succeeded_or_first_failed!(1, unknown!(2, first_succeeded_or_all_failed!(3, 4)))
=> WaitExternalInput(all_succeeded_or_first_failed!(unknown!(2, first_succeeded_or_all_failed!(3, 4)))));
test_try_future_resolve!(asff_unknown_fsaf_resolves_on_inner_fsaf_success:
state([success(3)]),
all_succeeded_or_first_failed!(1, unknown!(2, first_succeeded_or_all_failed!(3, 4)))
=> AnyCompleted);
test_try_future_resolve!(asff_unknown_fsaf_failure_doesnt_resolve:
state([failure(3)]),
all_succeeded_or_first_failed!(1, unknown!(2, first_succeeded_or_all_failed!(3, 4)))
=> WaitExternalInput(all_succeeded_or_first_failed!(1, unknown!(2, first_succeeded_or_all_failed!(4)))));
test_try_future_resolve!(asff_unknown_fsaf_all_inner_fail:
state([failure(3), failure(4)]),
all_succeeded_or_first_failed!(1, unknown!(2, first_succeeded_or_all_failed!(3, 4)))
=> AnyCompleted);
test_try_future_resolve!(asff_unknown_fsaf_pending:
state([]),
all_succeeded_or_first_failed!(1, unknown!(2, first_succeeded_or_all_failed!(3, 4)))
=> WaitExternalInput(all_succeeded_or_first_failed!(1, unknown!(2, first_succeeded_or_all_failed!(3, 4)))));
test_try_future_resolve!(asff_unknown_all_completed_with_unknown_partial_1:
state([success(1)]),
all_succeeded_or_first_failed!(1, unknown!(all_completed!(2, unknown!(3))))
=> WaitExternalInput(all_succeeded_or_first_failed!(unknown!(all_completed!(2, unknown!(3))))));
test_try_future_resolve!(asff_unknown_all_completed_with_unknown_partial_2:
state([success(2)]),
all_succeeded_or_first_failed!(1, unknown!(all_completed!(2, unknown!(3))))
=> WaitExternalInput(all_succeeded_or_first_failed!(1, unknown!(all_completed!(unknown!(3))))));
test_try_future_resolve!(asff_unknown_all_completed_with_unknown_shortcircuits_failure:
state([failure(1)]),
all_succeeded_or_first_failed!(1, unknown!(all_completed!(2, unknown!(3))))
=> AnyCompleted);
test_try_future_resolve!(asff_unknown_all_completed_with_unknown_all_done:
state([success(2), success(3)]),
all_succeeded_or_first_failed!(1, unknown!(all_completed!(2, unknown!(3))))
=> AnyCompleted);
test_try_future_resolve!(asff_with_nested_first_completed:
state([failure(2)]),
all_succeeded_or_first_failed!(first_completed!(1, 2), first_completed!(3, 4))
=> AnyCompleted);
test_try_future_resolve!(asff_with_nested_all_completed:
state([failure(1), failure(2)]),
all_succeeded_or_first_failed!(all_completed!(1, 2), all_completed!(3, 4))
=> WaitExternalInput(all_succeeded_or_first_failed!(all_completed!(3, 4))));
test_try_future_resolve!(asff_with_nested_all_completed_only_one_resolved:
state([failure(1)]),
all_succeeded_or_first_failed!(all_completed!(1, 2), all_completed!(3, 4))
=> WaitExternalInput(all_succeeded_or_first_failed!(all_completed!(2), all_completed!(3, 4))));
test_try_future_resolve!(unknown_none_ready:
state([]), unknown!(1, 2)
=> WaitExternalInput(unknown!(1, 2)));
test_try_future_resolve!(unknown_one_ready:
state([success(2)]), unknown!(1, 2)
=> AnyCompleted);
test_try_future_resolve!(nested_all_inside_first_completed:
state([success(3)]), first_completed!(all_completed!(1, 2), 3)
=> AnyCompleted);
test_try_future_resolve!(nested_first_completed_inside_all_partial:
state([success(1)]),
all_completed!(first_completed!(1, 2), first_completed!(3, 4))
=> AnyCompleted);
test_try_future_resolve!(nested_first_completed_inside_all_complete:
state([success(1), success(4)]),
all_completed!(first_completed!(1, 2), first_completed!(3, 4))
=> AnyCompleted);
test_try_future_resolve!(nested_asff_inside_all_partial:
state([failure(1)]),
all_completed!(all_succeeded_or_first_failed!(1, 2), first_completed!(3, 4))
=> AnyCompleted);
test_try_future_resolve!(nested_fsaf_inside_all_partial:
state([success(1)]),
all_completed!(first_succeeded_or_all_failed!(1, 2), first_completed!(3, 4))
=> AnyCompleted);
test_try_future_resolve!(duplicated_leaf_in_all_completed:
state([success(1)]), all_completed!(1, 1)
=> AnyCompleted);
test_try_future_resolve!(duplicated_leaf_in_first_completed:
state([success(1)]), first_completed!(1, 1, 2)
=> AnyCompleted);
test_try_future_resolve!(duplicated_leaf_failure_in_promise_all:
state([failure(1)]), all_succeeded_or_first_failed!(1, 1, 2)
=> AnyCompleted);
test_try_future_resolve!(duplicated_leaf_success_in_promise_any:
state([success(1)]), first_succeeded_or_all_failed!(1, 1)
=> AnyCompleted);
test_try_future_resolve!(duplicated_leaf_across_nested_combinators:
state([success(1)]),
all_completed!(first_completed!(1, 2), first_completed!(1, 3))
=> AnyCompleted);
test_try_future_resolve!(duplicated_subtree_all_succeeded:
state([success(1), success(2)]),
all_completed!(all_succeeded_or_first_failed!(1, 2), all_succeeded_or_first_failed!(1, 2))
=> AnyCompleted);
test_try_future_resolve!(duplicated_subtree_with_failure:
state([failure(1)]),
all_completed!(all_succeeded_or_first_failed!(1, 2), all_succeeded_or_first_failed!(1, 2))
=> AnyCompleted);
test_try_future_resolve!(duplicated_leaf_with_unknown_in_all_completed:
state([success(1)]),
all_completed!(1, unknown!(1, 2))
=> AnyCompleted);
test_try_future_resolve!(duplicated_leaf_partial_resolution:
state([success(1)]), all_completed!(1, 2, 1)
=> WaitExternalInput(all_completed!(2)));
test_convert_unresolved_future!(single_completion:
handles([(1, NotificationId::CompletionId(1))]),
1
=> pat!(Future {
waiting_completions: eq(&[1]),
waiting_signals: empty(),
waiting_named_signals: empty(),
nested_futures: empty(),
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(single_signal:
handles([(1, NotificationId::SignalId(17))]),
1
=> pat!(Future {
waiting_completions: empty(),
waiting_signals: eq(&[17]),
waiting_named_signals: empty(),
nested_futures: empty(),
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(single_named_signal:
handles([(1, NotificationId::SignalName("foo".to_string()))]),
1
=> pat!(Future {
waiting_completions: empty(),
waiting_signals: empty(),
waiting_named_signals: eq(&["foo".to_string()]),
nested_futures: empty(),
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(first_completed_flat:
handles([(1, NotificationId::CompletionId(1)), (2, NotificationId::CompletionId(2))]),
first_completed!(1, 2)
=> pat!(Future {
waiting_completions: unordered_elements_are![eq(1), eq(2)],
waiting_signals: empty(),
waiting_named_signals: empty(),
nested_futures: empty(),
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(first_completed_mixed:
handles([(1, NotificationId::CompletionId(1)), (2, NotificationId::SignalId(5))]),
first_completed!(1, 2)
=> pat!(Future {
waiting_completions: eq(&[1]),
waiting_signals: eq(&[5]),
waiting_named_signals: empty(),
nested_futures: empty(),
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(all_completed_flat:
handles([(1, NotificationId::CompletionId(1)), (2, NotificationId::CompletionId(2))]),
all_completed!(1, 2)
=> pat!(Future {
waiting_completions: unordered_elements_are![eq(1), eq(2)],
waiting_signals: empty(),
nested_futures: empty(),
combinator_type: eq(CombinatorType::AllCompleted as i32)
})
);
test_convert_unresolved_future!(fsaf_flat:
handles([(1, NotificationId::CompletionId(1)), (2, NotificationId::CompletionId(2))]),
first_succeeded_or_all_failed!(1, 2)
=> pat!(Future {
waiting_completions: unordered_elements_are![eq(1), eq(2)],
waiting_signals: empty(),
nested_futures: empty(),
combinator_type: eq(CombinatorType::FirstSucceededOrAllFailed as i32)
})
);
test_convert_unresolved_future!(asff_flat:
handles([(1, NotificationId::CompletionId(1)), (2, NotificationId::CompletionId(2))]),
all_succeeded_or_first_failed!(1, 2)
=> pat!(Future {
waiting_completions: unordered_elements_are![eq(1), eq(2)],
waiting_signals: empty(),
nested_futures: empty(),
combinator_type: eq(CombinatorType::AllSucceededOrFirstFailed as i32)
})
);
test_convert_unresolved_future!(unknown_flat:
handles([(1, NotificationId::CompletionId(1)), (2, NotificationId::SignalId(5))]),
unknown!(1, 2)
=> pat!(Future {
waiting_completions: eq(&[1]),
waiting_signals: eq(&[5]),
nested_futures: empty(),
combinator_type: eq(CombinatorType::Unknown as i32)
})
);
test_convert_unresolved_future!(nested_combinator:
handles([
(1, NotificationId::CompletionId(1)),
(2, NotificationId::CompletionId(2)),
(3, NotificationId::CompletionId(3))
]),
first_completed!(1, all_completed!(2, 3))
=> pat!(Future {
waiting_completions: eq(&[1]),
waiting_signals: empty(),
nested_futures: elements_are![pat!(Future {
waiting_completions: unordered_elements_are![eq(2), eq(3)],
nested_futures: empty(),
combinator_type: eq(CombinatorType::AllCompleted as i32)
})],
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(unknown_child_nested:
handles([
(1, NotificationId::CompletionId(1)),
(2, NotificationId::CompletionId(2)),
(3, NotificationId::CompletionId(3))
]),
first_completed!(unknown!(1, 2), 3)
=> pat!(Future {
waiting_completions: eq(&[3]),
waiting_signals: empty(),
nested_futures: elements_are![pat!(Future {
waiting_completions: unordered_elements_are![eq(1), eq(2)],
combinator_type: eq(CombinatorType::Unknown as i32)
})],
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(unknown_wrapping_combinator:
handles([
(1, NotificationId::CompletionId(1)),
(2, NotificationId::CompletionId(2)),
(3, NotificationId::CompletionId(3))
]),
first_completed!(unknown!(all_completed!(1, 2)), 3)
=> pat!(Future {
waiting_completions: eq(&[3]),
waiting_signals: empty(),
nested_futures: elements_are![pat!(Future {
waiting_completions: empty(),
nested_futures: elements_are![pat!(Future {
waiting_completions: unordered_elements_are![eq(1), eq(2)],
combinator_type: eq(CombinatorType::AllCompleted as i32)
})],
combinator_type: eq(CombinatorType::Unknown as i32)
})],
combinator_type: eq(CombinatorType::FirstCompleted as i32)
})
);
test_convert_unresolved_future!(unknown_root_with_nested_combinator:
handles([
(1, NotificationId::CompletionId(1)),
(2, NotificationId::CompletionId(2)),
(3, NotificationId::SignalId(5))
]),
unknown!(first_succeeded_or_all_failed!(1, 2), 3)
=> pat!(Future {
waiting_completions: empty(),
waiting_signals: eq(&[5]),
nested_futures: elements_are![pat!(Future {
waiting_completions: unordered_elements_are![eq(1), eq(2)],
combinator_type: eq(CombinatorType::FirstSucceededOrAllFailed as i32)
})],
combinator_type: eq(CombinatorType::Unknown as i32)
})
);
test_convert_unresolved_future!(all_completed_with_unknown_child:
handles([
(1, NotificationId::CompletionId(1)),
(2, NotificationId::SignalId(17))
]),
all_completed!(1, unknown!(2))
=> pat!(Future {
waiting_completions: eq(&[1]),
waiting_signals: empty(),
nested_futures: elements_are![pat!(Future {
waiting_signals: eq(&[17]),
combinator_type: eq(CombinatorType::Unknown as i32)
})],
combinator_type: eq(CombinatorType::AllCompleted as i32)
})
);
}