#![allow(deprecated)]
use crate::Promise;
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::{Cell, RefCell};
use core::future::Future;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use wasm_bindgen::prelude::*;
#[wasm_bindgen(raw_module = "__wbindgen_placeholder__")]
extern "C" {
#[wasm_bindgen(catch, suspending)]
fn __wbindgen_jspi_suspend(promise: &Promise) -> Result<JsValue, JsValue>;
fn __wbindgen_jspi_in_context() -> u32;
fn __wbindgen_jspi_spawn_poll(task: u32);
fn __wbindgen_jspi_spawn_first(task: u32);
}
pub(crate) fn in_context() -> bool {
__wbindgen_jspi_in_context() != 0
}
#[inline(never)]
pub(crate) fn suspend(promise: &Promise) -> Result<JsValue, JsValue> {
__wbindgen_jspi_suspend(promise)
}
#[derive(Clone, Copy, PartialEq)]
enum State {
Idle,
Scheduled,
Running,
RunningWoken,
}
struct SpawnedTask {
future: RefCell<Option<Pin<Box<dyn Future<Output = ()>>>>>,
state: Cell<State>,
}
impl SpawnedTask {
fn schedule(this: Rc<SpawnedTask>) {
this.state.set(State::Scheduled);
__wbindgen_jspi_spawn_poll(Rc::into_raw(this) as u32);
}
fn wake(this: &Rc<SpawnedTask>) {
match this.state.get() {
State::Running => this.state.set(State::RunningWoken),
State::RunningWoken | State::Scheduled => {}
State::Idle => {
if this.future.borrow().is_some() {
SpawnedTask::schedule(Rc::clone(this));
}
}
}
}
fn poll(this: &Rc<SpawnedTask>) {
loop {
this.state.set(State::Running);
let waker = task_waker(Rc::clone(this));
let mut cx = Context::from_waker(&waker);
let done = {
let mut slot = this.future.borrow_mut();
let Some(future) = slot.as_mut() else { return };
match future.as_mut().poll(&mut cx) {
Poll::Ready(()) => {
*slot = None;
true
}
Poll::Pending => false,
}
};
if done {
this.state.set(State::Idle);
return;
}
match this.state.replace(State::Idle) {
State::RunningWoken => continue,
_ => return,
}
}
}
}
fn task_waker(task: Rc<SpawnedTask>) -> Waker {
unsafe fn raw_clone(ptr: *const ()) -> RawWaker {
let rc = ManuallyDrop::new(Rc::from_raw(ptr as *const SpawnedTask));
RawWaker::new(Rc::into_raw(Rc::clone(&rc)) as *const (), &VTABLE)
}
unsafe fn raw_wake(ptr: *const ()) {
let rc = Rc::from_raw(ptr as *const SpawnedTask);
SpawnedTask::wake(&rc);
}
unsafe fn raw_wake_by_ref(ptr: *const ()) {
let rc = ManuallyDrop::new(Rc::from_raw(ptr as *const SpawnedTask));
SpawnedTask::wake(&rc);
}
unsafe fn raw_drop(ptr: *const ()) {
drop(Rc::from_raw(ptr as *const SpawnedTask));
}
static VTABLE: RawWakerVTable =
RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);
unsafe { Waker::from_raw(RawWaker::new(Rc::into_raw(task) as *const (), &VTABLE)) }
}
#[no_mangle]
pub extern "C-unwind" fn __wbg_jspi_task_poll(task: u32) {
let task = unsafe { Rc::from_raw(task as *const SpawnedTask) };
SpawnedTask::poll(&task);
}
pub(crate) fn spawn_promising<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
let task = Rc::new(SpawnedTask {
future: RefCell::new(Some(Box::pin(future))),
state: Cell::new(State::Scheduled),
});
__wbindgen_jspi_spawn_first(Rc::into_raw(task) as u32);
}