#![allow(static_mut_refs)]
use super::cabi;
use crate::rt::async_support::BoxFuture;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::future::Future;
use core::pin::Pin;
use core::ptr;
use core::task::{Context, Poll};
use futures::channel::oneshot;
use futures::future::{AbortHandle, Abortable, Aborted};
use futures::stream::{FuturesUnordered, StreamExt};
static mut SPAWNED: Vec<BoxFuture> = Vec::new();
#[derive(Default)]
pub struct Tasks<'a> {
tasks: FuturesUnordered<BoxFuture<'a>>,
root: Option<AbortHandle>,
}
impl<'a> Tasks<'a> {
pub fn new(root: impl Future<Output = ()> + 'a) -> Tasks<'a> {
let (abort, registration) = AbortHandle::new_pair();
let root: BoxFuture<'a> = Box::pin(async move {
let _ = Abortable::new(root, registration).await;
});
Tasks {
tasks: [root].into_iter().collect(),
root: Some(abort),
}
}
pub fn cancel_root(&mut self) {
if let Some(root) = self.root.take() {
root.abort();
}
}
pub fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<()> {
loop {
let poll = self.tasks.poll_next_unpin(cx);
let spawned = unsafe {
if SPAWNED.is_empty() {
false
} else {
self.tasks.extend(SPAWNED.drain(..));
true
}
};
match poll {
Poll::Pending => {
if !spawned {
return Poll::Pending;
}
}
Poll::Ready(None) => {
assert!(!spawned);
return Poll::Ready(());
}
Poll::Ready(Some(())) => {}
}
}
}
pub fn is_empty(&self) -> bool {
self.tasks.is_empty()
}
}
pub(super) fn push(task: BoxFuture<'static>) {
unsafe {
SPAWNED.push(task);
}
}
pub fn spawn_local<T: 'static>(future: impl Future<Output = T> + 'static) -> JoinHandle<T> {
let (sender, receiver) = oneshot::channel();
let (abort, registration) = AbortHandle::new_pair();
let future = Box::new(async move {
let _ = sender.send(Abortable::new(future, registration).await);
});
let ok = unsafe {
let task = cabi::wasip3_task_set(ptr::null_mut());
assert!(!task.is_null());
assert!((*task).version >= cabi::WASIP3_TASK_V1);
let ok = if (*task).version >= cabi::WASIP3_TASK_V3 {
let task = task.cast::<cabi::wasip3_task_v3>();
match (*task).vtable.rust_spawn {
Some(f) => {
f((*task).v1.ptr, future);
true
}
None => false,
}
} else {
false
};
cabi::wasip3_task_set(task);
ok
};
if !ok {
panic!(
"failed to `spawn_local` because there's a different version of \
the `wit-bindgen` crate running the export than this \
`wit-bindgen` crate and that one is either too old \
or has the `async-spawn` feature disabled"
);
}
JoinHandle { receiver, abort }
}
pub struct JoinHandle<T> {
receiver: oneshot::Receiver<Result<T, Aborted>>,
abort: AbortHandle,
}
impl<T> JoinHandle<T> {
pub fn cancel(&mut self) {
self.abort.abort();
}
}
impl<T> Future for JoinHandle<T> {
type Output = Option<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
match Pin::new(&mut self.receiver).poll(cx) {
Poll::Ready(Ok(Ok(result))) => Poll::Ready(Some(result)),
Poll::Ready(Ok(Err(_)) | Err(_)) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}