#![cfg(target_has_atomic = "ptr")] #![warn(missing_docs)]
use crate::api::EventLoopError;
use crate::SlintContext;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
use alloc::task::Wake;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::future::Future;
use core::ops::DerefMut;
use core::pin::Pin;
use core::task::Poll;
use portable_atomic as atomic;
enum FutureState<T> {
Running(Pin<Box<dyn Future<Output = T>>>),
Finished(Option<T>),
}
struct FutureRunnerInner<T> {
fut: FutureState<T>,
wakers: Vec<core::task::Waker>,
}
struct FutureRunner<T> {
#[cfg(not(feature = "std"))]
inner: core::cell::RefCell<FutureRunnerInner<T>>,
#[cfg(feature = "std")]
inner: std::sync::Mutex<FutureRunnerInner<T>>,
aborted: atomic::AtomicBool,
proxy: Box<dyn crate::platform::EventLoopProxy>,
#[cfg(feature = "std")]
thread: std::thread::ThreadId,
}
impl<T> FutureRunner<T> {
fn inner(&self) -> impl DerefMut<Target = FutureRunnerInner<T>> + '_ {
#[cfg(feature = "std")]
return self.inner.lock().unwrap();
#[cfg(not(feature = "std"))]
return self.inner.borrow_mut();
}
}
#[allow(unsafe_code)]
unsafe impl<T> Send for FutureRunner<T> {}
#[allow(unsafe_code)]
unsafe impl<T> Sync for FutureRunner<T> {}
impl<T: 'static> Wake for FutureRunner<T> {
fn wake(self: alloc::sync::Arc<Self>) {
self.clone().proxy.invoke_from_event_loop(Box::new(move || {
#[cfg(feature = "std")]
assert_eq!(self.thread, std::thread::current().id(), "the future was moved to a thread despite we checked it was created in the event loop thread");
let waker = self.clone().into();
let mut inner = self.inner();
let mut cx = core::task::Context::from_waker(&waker);
if let FutureState::Running(fut) = &mut inner.fut {
if self.aborted.load(atomic::Ordering::Relaxed) {
inner.fut = FutureState::Finished(None);
} else {
match fut.as_mut().poll(&mut cx) {
Poll::Ready(val) => {
inner.fut = FutureState::Finished(Some(val));
for w in core::mem::take(&mut inner.wakers) {
w.wake();
}
}
Poll::Pending => {}
}
}
}
}))
.expect("No event loop despite we checked");
}
}
pub struct JoinHandle<T>(alloc::sync::Arc<FutureRunner<T>>);
impl<T> Future for JoinHandle<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll<Self::Output> {
let mut inner = self.0.inner();
match &mut inner.fut {
FutureState::Running(_) => {
let waker = cx.waker();
if !inner.wakers.iter().any(|w| w.will_wake(waker)) {
inner.wakers.push(waker.clone());
}
Poll::Pending
}
FutureState::Finished(x) => {
Poll::Ready(x.take().expect("Polling completed or aborted JoinHandle"))
}
}
}
}
impl<T> JoinHandle<T> {
pub fn abort(self) {
self.0.aborted.store(true, atomic::Ordering::Relaxed);
}
pub fn is_finished(&self) -> bool {
matches!(self.0.inner().fut, FutureState::Finished(_))
}
}
#[cfg(feature = "std")]
#[allow(unsafe_code)]
unsafe impl<T: Send> Send for JoinHandle<T> {}
pub(crate) fn spawn_local_with_ctx<F: Future + 'static>(
ctx: &SlintContext,
fut: F,
) -> Result<JoinHandle<F::Output>, EventLoopError> {
let arc = alloc::sync::Arc::new(FutureRunner {
#[cfg(feature = "std")]
thread: std::thread::current().id(),
inner: FutureRunnerInner { fut: FutureState::Running(Box::pin(fut)), wakers: Vec::new() }
.into(),
aborted: Default::default(),
proxy: ctx.event_loop_proxy().ok_or(EventLoopError::NoEventLoopProvider)?,
});
arc.wake_by_ref();
Ok(JoinHandle(arc))
}