use std::any::Any;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use crate::util::{Value, ValueReceiver, ValueReceiverAsync, WorkerPanic, WorkerResult};
pub struct JoinHandle<T: Send + 'static> {
id: usize,
recv: ValueReceiver,
_marker: PhantomData<T>,
}
impl<T: Send + 'static> JoinHandle<T> {
pub(crate) fn new(id: usize, recv: ValueReceiver) -> Self {
Self {
id,
recv,
_marker: PhantomData,
}
}
pub fn join(self) -> Result<T, Box<dyn Any + Send + 'static>> {
handle_join_result(self.id, self.recv.recv())
}
pub fn is_finished(&self) -> bool {
self.recv.has_message() || self.recv.is_closed()
}
}
pub struct AsyncJoinHandle<T: Send + 'static> {
id: usize,
recv: ValueReceiverAsync,
_marker: PhantomData<T>,
}
impl<T: Send + 'static> IntoFuture for JoinHandle<T> {
type Output = Result<T, Box<dyn Any + Send + 'static>>;
type IntoFuture = AsyncJoinHandle<T>;
fn into_future(self) -> Self::IntoFuture {
AsyncJoinHandle {
id: self.id,
recv: self.recv.into_future(),
_marker: PhantomData,
}
}
}
impl<T: Send + 'static> Future for AsyncJoinHandle<T> {
type Output = Result<T, Box<dyn Any + Send + 'static>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let id = self.id;
let recv = unsafe { self.map_unchecked_mut(|s| &mut s.recv) };
match recv.poll(cx) {
Poll::Ready(x) => Poll::Ready(handle_join_result(id, x)),
Poll::Pending => Poll::Pending,
}
}
}
fn handle_join_result<T>(
id: usize,
result: Result<WorkerResult, oneshot::RecvError>,
) -> Result<T, Box<dyn Any + Send + 'static>> {
let result = match result {
Ok(x) => x,
Err(_) => return Err(Box::new(format!("thread {id} is disconnected"))),
};
let value: Value = match result {
Ok(x) => x,
Err(WorkerPanic { payload: Some(e) }) => {
return Err(e);
}
Err(WorkerPanic { payload: None }) => {
if cfg!(panic = "unwind") {
return Err(Box::new(format!(
"thread {id} encountered a non-recoverable hard abort!",
)));
}
return Err(Box::new(format!("thread {id} panicked or aborted!")));
}
};
let value: Box<T> = unsafe { value.into_box_unchecked() };
Ok(*value)
}