use std::pin::Pin;
use std::task::{self, Poll};
use arty_executor::remote::{JoinHandle as RemoteJoinHandle, Pool as RemotePool};
use performables::arc::Arc;
use pin_project::pin_project;
use super::JoinError;
use crate::runtime::blocking_worker::is_current_blocking_pool;
#[derive(derive_more::Debug)]
#[pin_project]
pub struct JoinHandle<R>
where
R: Send + 'static,
{
#[debug(ignore)]
#[pin]
inner: RemoteJoinHandle<R>,
blocking_pool: Option<Arc<()>>,
completed: bool,
}
impl<R> JoinHandle<R>
where
R: Send + 'static,
{
pub(crate) fn new(inner: RemoteJoinHandle<R>) -> Self {
Self {
inner,
blocking_pool: None,
completed: false,
}
}
pub(crate) fn shutdown(pool: &RemotePool) -> Self {
let (sender, inner) = pool.channel();
drop(sender);
Self::new(inner)
}
pub(crate) fn blocking_pool(mut self, pool: Arc<()>) -> Self {
self.blocking_pool = Some(pool);
self
}
#[cfg(test)]
pub(crate) fn join(self) -> Result<R, JoinError> {
futures::executor::block_on(self)
}
}
#[cfg_attr(test, mutants::skip)] fn assert_not_current_blocking_pool(blocking_pool: Option<&Arc<()>>) {
assert!(
!blocking_pool.is_some_and(is_current_blocking_pool),
"blocking JoinHandle cannot be polled while the current blocking pool waits for it"
);
}
impl<R> Future for JoinHandle<R>
where
R: Send + 'static,
{
type Output = Result<R, JoinError>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let this = self.project();
assert!(!*this.completed, "JoinHandle polled after completion");
assert_not_current_blocking_pool(this.blocking_pool.as_ref());
match this.inner.poll(cx) {
Poll::Ready(result) => {
*this.completed = true;
Poll::Ready(result.map_err(JoinError::from_executor))
}
Poll::Pending => Poll::Pending,
}
}
}