use std::{
sync::{Arc, Mutex},
task::{Poll, Waker},
};
pub fn spawn<F, T>(f: F) -> Handle<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
spawn_with_builder(std::thread::Builder::new(), f).expect("failed to spawn thread")
}
pub fn spawn_with_builder<F, T>(builder: std::thread::Builder, f: F) -> std::io::Result<Handle<T>>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let state = Arc::new(Mutex::new(State::Running(Waker::noop().clone())));
let join_handle = builder.spawn({
let state = state.clone();
move || {
let val = f();
let State::Running(waker) =
std::mem::replace(&mut *state.lock().unwrap(), State::Complete)
else {
panic!("thread finished but state was already Complete");
};
waker.wake();
val
}
})?;
Ok(Handle {
state,
join_handle: Some(join_handle),
})
}
pub struct Handle<T> {
state: Arc<Mutex<State>>,
join_handle: Option<std::thread::JoinHandle<T>>,
}
enum State {
Running(Waker),
Complete,
}
impl<T> Handle<T> {
#[must_use]
pub fn is_finished(&self) -> bool {
self.join_handle.as_ref().unwrap().is_finished()
}
pub const fn join(self) -> JoinFuture<T> {
JoinFuture { handle: self }
}
#[must_use]
pub fn thread(&self) -> &std::thread::Thread {
self.join_handle.as_ref().unwrap().thread()
}
}
#[must_use]
pub struct JoinFuture<T> {
handle: Handle<T>,
}
impl<T> Future for JoinFuture<T> {
type Output = std::thread::Result<T>;
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Self::Output> {
let handle = &mut self.handle;
match &mut *handle.state.lock().unwrap() {
State::Running(waker) => {
waker.clone_from(cx.waker());
Poll::Pending
}
State::Complete => Poll::Ready(
handle
.join_handle
.take()
.expect("future polled after completion")
.join(),
),
}
}
}