use std::{future::Future, future::poll_fn, pin, pin::Pin, task::Poll};
use crate::{Ctx, Service, readiness::Generation};
pub(crate) type BoxFuture<'a, R> = Pin<Box<dyn Future<Output = R> + 'a>>;
pub(crate) async fn shutdown<S, St, A, B, RA, RB>(svc1: &A, svc2: &B, ctx: Ctx<'_, S, St>)
where
A: Service<St, RA>,
B: Service<St, RB>,
{
join(ctx.shutdown(svc1), ctx.shutdown(svc2)).await;
}
pub(crate) async fn ready<S, St, A, B, RA, RB>(
svc1: &A,
svc2: &B,
ctx: Ctx<'_, S, St>,
) -> Result<(), A::Error>
where
A: Service<St, RA>,
B: Service<St, RB, Error = A::Error>,
{
ready_join(
ctx,
|| ctx.ready_generation(svc1),
|| ctx.ready_generation(svc2),
)
.await
.map(|_| ())
}
pub async fn join<A, B>(fut1: A, fut2: B)
where
A: Future<Output = ()>,
B: Future<Output = ()>,
{
let mut fut1 = pin::pin!(fut1);
let mut fut2 = pin::pin!(fut2);
let mut ready1 = false;
let mut ready2 = false;
poll_fn(move |cx| {
if !ready1 && Pin::new(&mut fut1).poll(cx).is_ready() {
ready1 = true;
}
if !ready2 && Pin::new(&mut fut2).poll(cx).is_ready() {
ready2 = true;
}
if ready1 && ready2 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
}
pub async fn ready_join<S, St, E, F1, Fut1, F2, Fut2>(
ctx: Ctx<'_, S, St>,
f1: F1,
f2: F2,
) -> Result<Generation, E>
where
F1: Fn() -> Fut1,
Fut1: Future<Output = Result<Generation, E>>,
F2: Fn() -> Fut2,
Fut2: Future<Output = Result<Generation, E>>,
{
let mut fut1 = pin::pin!(Some(f1()));
let mut fut2 = pin::pin!(Some(f2()));
let scope = ctx.scope();
let mut generation1 = scope.generation();
let mut generation2 = scope.generation();
poll_fn(|cx| {
if is_idle(&mut fut1) && !scope.is_current(generation1) {
fut1.set(Some(f1()));
}
if let Some(fut) = fut1.as_mut().as_pin_mut()
&& let Poll::Ready(generation) = fut.poll(cx)?
{
generation1 = generation;
fut1.set(None);
}
if is_idle(&mut fut2) && !scope.is_current(generation2) {
fut2.set(Some(f2()));
}
if let Some(fut) = fut2.as_mut().as_pin_mut()
&& let Poll::Ready(generation) = fut.poll(cx)?
{
generation2 = generation;
fut2.set(None);
}
let current1 = scope.is_current(generation1);
let current2 = scope.is_current(generation2);
if is_idle(&mut fut1) && is_idle(&mut fut2) && current1 && current2 {
Poll::Ready(Ok(generation1))
} else {
if (is_idle(&mut fut1) && !current1) || (is_idle(&mut fut2) && !current2) {
cx.waker().wake_by_ref();
}
Poll::Pending
}
})
.await
}
pub async fn ready_field<'a, S, St, T, Req, E>(
ctx: Ctx<'a, S, St>,
svc: &'a T,
map_err: impl FnOnce(T::Error) -> E,
) -> Result<Generation, E>
where
T: Service<St, Req>,
{
ctx.ready_generation(svc).await.map_err(map_err)
}
fn is_idle<F>(fut: &mut Pin<&mut Option<F>>) -> bool {
fut.as_mut().as_pin_mut().is_none()
}
#[cfg(test)]
#[path = "../tests/unit/util.rs"]
pub(crate) mod tests;