use anyhow::Result;
use futures::future::{Either, Ready, ready};
use std::{
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use velo::{Event, EventAwaiter, EventManager};
pub enum TransferAwaiter {
Local(EventAwaiter),
}
impl std::future::Future for TransferAwaiter {
type Output = Result<()>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.get_mut() {
Self::Local(waiter) => Pin::new(waiter).poll(cx),
}
}
}
pub struct TransferCompleteNotification {
awaiter: Either<Ready<Result<()>>, TransferAwaiter>,
}
impl TransferCompleteNotification {
pub fn completed() -> Self {
Self {
awaiter: Either::Left(ready(Ok(()))),
}
}
pub fn from_awaiter(awaiter: EventAwaiter) -> Self {
Self {
awaiter: Either::Right(TransferAwaiter::Local(awaiter)),
}
}
pub fn could_yield(&self) -> bool {
matches!(self.awaiter, Either::Right(_))
}
pub fn aggregate(
notifications: Vec<Self>,
events: &Arc<EventManager>,
runtime: &tokio::runtime::Handle,
) -> Result<Self> {
if notifications.is_empty() {
return Ok(Self::completed());
}
if notifications.len() == 1 {
return Ok(notifications.into_iter().next().unwrap());
}
if notifications.iter().all(|n| !n.could_yield()) {
return Ok(Self::completed());
}
let event = events.new_event()?;
let awaiter = events.awaiter(event.handle())?;
runtime.spawn(await_all_notifications(notifications, event));
Ok(Self::from_awaiter(awaiter))
}
}
async fn await_all_notifications(
notifications: Vec<TransferCompleteNotification>,
local_event: Event,
) {
let results: Vec<Result<()>> =
futures::future::join_all(notifications.into_iter().map(|n| n.into_future())).await;
let errors: Vec<_> = results.into_iter().filter_map(|r| r.err()).collect();
if errors.is_empty() {
let _ = local_event.trigger();
} else {
let error_msg = errors
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("; ");
let _ = local_event.poison(error_msg);
}
}
impl std::future::IntoFuture for TransferCompleteNotification {
type Output = Result<()>;
type IntoFuture = Either<Ready<Result<()>>, TransferAwaiter>;
fn into_future(self) -> Self::IntoFuture {
self.awaiter
}
}