#![cfg(target_arch = "wasm32")]
use core::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime};
use futures::channel::oneshot;
use futures::future::{AbortHandle, Abortable};
use futures::FutureExt;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::spawn_local;
use super::{BoxFuture, Runtime, SpawnHandle, SpawnHandleInner};
#[derive(Clone, Copy, Default)]
pub struct WasmRuntime;
impl Runtime for WasmRuntime {
fn spawn(&self, fut: BoxFuture<()>) -> SpawnHandle {
let (abort_handle, abort_reg) = AbortHandle::new_pair();
let finished = Arc::new(AtomicBool::new(false));
let finished_for_task = finished.clone();
let wrapped = Abortable::new(fut, abort_reg);
spawn_local(async move {
let _ = wrapped.await;
finished_for_task.store(true, Ordering::SeqCst);
});
SpawnHandle::from_inner(WasmSpawnHandle {
abort_handle,
finished,
})
}
fn sleep(&self, duration: Duration) -> BoxFuture<()> {
let (tx, rx) = oneshot::channel::<()>();
let closure = Closure::once(Box::new(move || {
let _ = tx.send(());
}) as Box<dyn FnOnce()>);
let ms: i32 = duration.as_millis().try_into().unwrap_or(i32::MAX).max(0);
let global = js_sys::global();
let set_timeout = js_sys::Reflect::get(&global, &JsValue::from_str("setTimeout"))
.expect("global.setTimeout missing — not a browser/worker context");
let set_timeout: js_sys::Function = set_timeout
.dyn_into()
.expect("global.setTimeout is not a function");
let _ = set_timeout
.call2(
&global,
closure.as_ref().unchecked_ref(),
&JsValue::from_f64(ms as f64),
)
.expect("setTimeout invocation failed");
closure.forget();
rx.map(|_| ()).boxed()
}
fn now_monotonic(&self) -> Instant {
Instant::now()
}
fn now_wall_clock(&self) -> SystemTime {
SystemTime::UNIX_EPOCH + Duration::from_millis(js_sys::Date::now() as u64)
}
}
pub(super) struct WasmSpawnHandle {
abort_handle: AbortHandle,
finished: Arc<AtomicBool>,
}
impl SpawnHandleInner for WasmSpawnHandle {
fn abort(&self) {
self.abort_handle.abort();
}
fn is_finished(&self) -> bool {
self.finished.load(Ordering::SeqCst)
}
}