use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use std::io;
use crate::channel::oneshot;
use crate::sys::blocking;
mod join_set;
pub use join_set::{JoinError, JoinSet};
type BlockingOutcome<R> = Result<R, ()>;
type BlockingResultFuture<R> =
Pin<Box<dyn Future<Output = Result<BlockingOutcome<R>, oneshot::RecvError>> + Send + 'static>>;
pub struct BlockingJoinHandle<R: Send + 'static> {
inner: BlockingResultFuture<R>,
}
impl<R: Send + 'static> Future for BlockingJoinHandle<R> {
type Output = Result<R, JoinError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
match this.inner.as_mut().poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(Ok(Ok(value))) => Poll::Ready(Ok(value)),
Poll::Ready(Ok(Err(()))) => Poll::Ready(Err(JoinError::Panicked)),
Poll::Ready(Err(_)) => Poll::Ready(Err(JoinError::Cancelled)),
}
}
}
pub fn spawn_blocking<F, R>(f: F) -> io::Result<BlockingJoinHandle<R>>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let (sender, mut receiver) = oneshot::channel::<Result<R, ()>>();
blocking::spawn_blocking(move || {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).map_err(|_| ());
let _ = sender.send(result);
})?;
let inner: BlockingResultFuture<R> = Box::pin(async move { receiver.recv().await });
Ok(BlockingJoinHandle { inner })
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{run, run_until_stalled, spawn};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn spawn_blocking_returns_value() {
let result = Arc::new(AtomicUsize::new(0));
let result_clone = Arc::clone(&result);
std::thread::spawn(move || {
spawn(async move {
let handle = spawn_blocking(|| 7usize + 35).expect("spawn_blocking");
let value = handle.await.expect("join");
result_clone.store(value, Ordering::SeqCst);
});
run();
})
.join()
.expect("runtime thread should join");
assert_eq!(result.load(Ordering::SeqCst), 42);
}
#[test]
fn spawn_blocking_returns_complex_value() {
let result = Arc::new(std::sync::Mutex::new(String::new()));
let result_clone = Arc::clone(&result);
spawn(async move {
let handle =
spawn_blocking(|| "hello blocking world".to_string()).expect("spawn_blocking");
let value = handle.await.expect("join");
*result_clone.lock().unwrap() = value;
});
for _ in 0..200 {
run_until_stalled();
if !result.lock().unwrap().is_empty() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(5));
}
assert_eq!(*result.lock().unwrap(), "hello blocking world");
}
}