Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

241
vendor/wasm-bindgen-futures/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,241 @@
//! Converting between JavaScript `Promise`s to Rust `Future`s.
//!
//! This crate provides a bridge for working with JavaScript `Promise` types as
//! a Rust `Future`, and similarly contains utilities to turn a rust `Future`
//! into a JavaScript `Promise`. This can be useful when working with
//! asynchronous or otherwise blocking work in Rust (wasm), and provides the
//! ability to interoperate with JavaScript events and JavaScript I/O
//! primitives.
//!
//! There are three main interfaces in this crate currently:
//!
//! 1. [**`JsFuture`**](./struct.JsFuture.html)
//!
//! A type that is constructed with a `Promise` and can then be used as a
//! `Future<Output = Result<JsValue, JsValue>>`. This Rust future will resolve
//! or reject with the value coming out of the `Promise`.
//!
//! 2. [**`future_to_promise`**](./fn.future_to_promise.html)
//!
//! Converts a Rust `Future<Output = Result<JsValue, JsValue>>` into a
//! JavaScript `Promise`. The future's result will translate to either a
//! resolved or rejected `Promise` in JavaScript.
//!
//! 3. [**`spawn_local`**](./fn.spawn_local.html)
//!
//! Spawns a `Future<Output = ()>` on the current thread. This is the
//! best way to run a `Future` in Rust without sending it to JavaScript.
//!
//! These three items should provide enough of a bridge to interoperate the two
//! systems and make sure that Rust/JavaScript can work together with
//! asynchronous and I/O work.
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(
target_feature = "atomics",
feature(thread_local, stdarch_wasm_atomic_wait)
)]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
extern crate alloc;
use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::RefCell;
use core::fmt;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use js_sys::Promise;
use wasm_bindgen::prelude::*;
mod queue;
#[cfg_attr(docsrs, doc(cfg(feature = "futures-core-03-stream")))]
#[cfg(feature = "futures-core-03-stream")]
pub mod stream;
pub use js_sys;
pub use wasm_bindgen;
mod task {
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_feature = "atomics")] {
mod wait_async_polyfill;
mod multithread;
pub(crate) use multithread::*;
} else {
mod singlethread;
pub(crate) use singlethread::*;
}
}
}
/// Runs a Rust `Future` on the current thread.
///
/// The `future` must be `'static` because it will be scheduled
/// to run in the background and cannot contain any stack references.
///
/// The `future` will always be run on the next microtask tick even if it
/// immediately returns `Poll::Ready`.
///
/// # Panics
///
/// This function has the same panic behavior as `future_to_promise`.
#[inline]
pub fn spawn_local<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
task::Task::spawn(Box::pin(future));
}
struct Inner {
result: Option<Result<JsValue, JsValue>>,
task: Option<Waker>,
callbacks: Option<(Closure<dyn FnMut(JsValue)>, Closure<dyn FnMut(JsValue)>)>,
}
/// A Rust `Future` backed by a JavaScript `Promise`.
///
/// This type is constructed with a JavaScript `Promise` object and translates
/// it to a Rust `Future`. This type implements the `Future` trait from the
/// `futures` crate and will either succeed or fail depending on what happens
/// with the JavaScript `Promise`.
///
/// Currently this type is constructed with `JsFuture::from`.
pub struct JsFuture {
inner: Rc<RefCell<Inner>>,
}
impl fmt::Debug for JsFuture {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "JsFuture {{ ... }}")
}
}
impl From<Promise> for JsFuture {
fn from(js: Promise) -> JsFuture {
// Use the `then` method to schedule two callbacks, one for the
// resolved value and one for the rejected value. We're currently
// assuming that JS engines will unconditionally invoke precisely one of
// these callbacks, no matter what.
//
// Ideally we'd have a way to cancel the callbacks getting invoked and
// free up state ourselves when this `JsFuture` is dropped. We don't
// have that, though, and one of the callbacks is likely always going to
// be invoked.
//
// As a result we need to make sure that no matter when the callbacks
// are invoked they are valid to be called at any time, which means they
// have to be self-contained. Through the `Closure::once` and some
// `Rc`-trickery we can arrange for both instances of `Closure`, and the
// `Rc`, to all be destroyed once the first one is called.
let state = Rc::new(RefCell::new(Inner {
result: None,
task: None,
callbacks: None,
}));
fn finish(state: &RefCell<Inner>, val: Result<JsValue, JsValue>) {
let task = {
let mut state = state.borrow_mut();
debug_assert!(state.callbacks.is_some());
debug_assert!(state.result.is_none());
// First up drop our closures as they'll never be invoked again and
// this is our chance to clean up their state.
drop(state.callbacks.take());
// Next, store the value into the internal state.
state.result = Some(val);
state.task.take()
};
// And then finally if any task was waiting on the value wake it up and
// let them know it's there.
if let Some(task) = task {
task.wake()
}
}
let resolve = {
let state = state.clone();
Closure::once(move |val| finish(&state, Ok(val)))
};
let reject = {
let state = state.clone();
Closure::once(move |val| finish(&state, Err(val)))
};
let _ = js.then2(&resolve, &reject);
state.borrow_mut().callbacks = Some((resolve, reject));
JsFuture { inner: state }
}
}
impl Future for JsFuture {
type Output = Result<JsValue, JsValue>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut inner = self.inner.borrow_mut();
// If our value has come in then we return it...
if let Some(val) = inner.result.take() {
return Poll::Ready(val);
}
// ... otherwise we arrange ourselves to get woken up once the value
// does come in
inner.task = Some(cx.waker().clone());
Poll::Pending
}
}
/// Converts a Rust `Future` into a JavaScript `Promise`.
///
/// This function will take any future in Rust and schedule it to be executed,
/// returning a JavaScript `Promise` which can then be passed to JavaScript.
///
/// The `future` must be `'static` because it will be scheduled to run in the
/// background and cannot contain any stack references.
///
/// The returned `Promise` will be resolved or rejected when the future completes,
/// depending on whether it finishes with `Ok` or `Err`.
///
/// # Panics
///
/// Note that in Wasm panics are currently translated to aborts, but "abort" in
/// this case means that a JavaScript exception is thrown. The Wasm module is
/// still usable (likely erroneously) after Rust panics.
///
/// If the `future` provided panics then the returned `Promise` **will not
/// resolve**. Instead it will be a leaked promise. This is an unfortunate
/// limitation of Wasm currently that's hoped to be fixed one day!
pub fn future_to_promise<F>(future: F) -> Promise
where
F: Future<Output = Result<JsValue, JsValue>> + 'static,
{
let mut future = Some(future);
Promise::new(&mut |resolve, reject| {
let future = future.take().unwrap_throw();
spawn_local(async move {
match future.await {
Ok(val) => {
resolve.call1(&JsValue::undefined(), &val).unwrap_throw();
}
Err(val) => {
reject.call1(&JsValue::undefined(), &val).unwrap_throw();
}
}
});
})
}

126
vendor/wasm-bindgen-futures/src/queue.rs vendored Normal file
View File

@@ -0,0 +1,126 @@
use alloc::collections::VecDeque;
use alloc::rc::Rc;
use core::cell::{Cell, RefCell};
use js_sys::Promise;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen]
fn queueMicrotask(closure: &Closure<dyn FnMut(JsValue)>);
type Global;
#[wasm_bindgen(method, getter, js_name = queueMicrotask)]
fn hasQueueMicrotask(this: &Global) -> JsValue;
}
struct QueueState {
// The queue of Tasks which are to be run in order. In practice this is all the
// synchronous work of futures, and each `Task` represents calling `poll` on
// a future "at the right time".
tasks: RefCell<VecDeque<Rc<crate::task::Task>>>,
// This flag indicates whether we've scheduled `run_all` to run in the future.
// This is used to ensure that it's only scheduled once.
is_scheduled: Cell<bool>,
}
impl QueueState {
fn run_all(&self) {
// "consume" the schedule
let _was_scheduled = self.is_scheduled.replace(false);
debug_assert!(_was_scheduled);
// Stop when all tasks that have been scheduled before this tick have been run.
// Tasks that are scheduled while running tasks will run on the next tick.
let mut task_count_left = self.tasks.borrow().len();
while task_count_left > 0 {
task_count_left -= 1;
let task = match self.tasks.borrow_mut().pop_front() {
Some(task) => task,
None => break,
};
task.run();
}
// All of the Tasks have been run, so it's now possible to schedule the
// next tick again
}
}
pub(crate) struct Queue {
state: Rc<QueueState>,
promise: Promise,
closure: Closure<dyn FnMut(JsValue)>,
has_queue_microtask: bool,
}
impl Queue {
// Schedule a task to run on the next tick
pub(crate) fn schedule_task(&self, task: Rc<crate::task::Task>) {
self.state.tasks.borrow_mut().push_back(task);
// Use queueMicrotask to execute as soon as possible. If it does not exist
// fall back to the promise resolution
if !self.state.is_scheduled.replace(true) {
if self.has_queue_microtask {
queueMicrotask(&self.closure);
} else {
let _ = self.promise.then(&self.closure);
}
}
}
// Append a task to the currently running queue, or schedule it
#[cfg(not(target_feature = "atomics"))]
pub(crate) fn push_task(&self, task: Rc<crate::task::Task>) {
// It would make sense to run this task on the same tick. For now, we
// make the simplifying choice of always scheduling tasks for a future tick.
self.schedule_task(task)
}
}
impl Queue {
fn new() -> Self {
let state = Rc::new(QueueState {
is_scheduled: Cell::new(false),
tasks: RefCell::new(VecDeque::new()),
});
let has_queue_microtask = js_sys::global()
.unchecked_into::<Global>()
.hasQueueMicrotask()
.is_function();
Self {
promise: Promise::resolve(&JsValue::undefined()),
closure: {
let state = Rc::clone(&state);
// This closure will only be called on the next microtask event
// tick
Closure::new(move |_| state.run_all())
},
state,
has_queue_microtask,
}
}
pub(crate) fn with<R>(f: impl FnOnce(&Self) -> R) -> R {
use once_cell::unsync::Lazy;
struct Wrapper<T>(Lazy<T>);
#[cfg(not(target_feature = "atomics"))]
unsafe impl<T> Sync for Wrapper<T> {}
#[cfg(not(target_feature = "atomics"))]
unsafe impl<T> Send for Wrapper<T> {}
#[cfg_attr(target_feature = "atomics", thread_local)]
static QUEUE: Wrapper<Queue> = Wrapper(Lazy::new(Queue::new));
f(&QUEUE.0)
}
}

View File

@@ -0,0 +1,81 @@
//! Converting JavaScript `AsyncIterator`s to Rust `Stream`s.
//!
//! Analogous to the promise to future conversion, this module allows
//! turning objects implementing the async iterator protocol into `Stream`s
//! that produce values that can be awaited from.
//!
use crate::JsFuture;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use futures_core::stream::Stream;
use js_sys::{AsyncIterator, IteratorNext};
use wasm_bindgen::prelude::*;
/// A `Stream` that yields values from an underlying `AsyncIterator`.
pub struct JsStream {
iter: AsyncIterator,
next: Option<JsFuture>,
done: bool,
}
impl JsStream {
fn next_future(&self) -> Result<JsFuture, JsValue> {
self.iter.next().map(JsFuture::from)
}
}
impl From<AsyncIterator> for JsStream {
fn from(iter: AsyncIterator) -> Self {
JsStream {
iter,
next: None,
done: false,
}
}
}
impl Stream for JsStream {
type Item = Result<JsValue, JsValue>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
if self.done {
return Poll::Ready(None);
}
let future = match self.next.as_mut() {
Some(val) => val,
None => match self.next_future() {
Ok(val) => {
self.next = Some(val);
self.next.as_mut().unwrap()
}
Err(e) => {
self.done = true;
return Poll::Ready(Some(Err(e)));
}
},
};
match Pin::new(future).poll(cx) {
Poll::Ready(res) => match res {
Ok(iter_next) => {
let next = iter_next.unchecked_into::<IteratorNext>();
if next.done() {
self.done = true;
Poll::Ready(None)
} else {
self.next.take();
Poll::Ready(Some(Ok(next.value())))
}
}
Err(e) => {
self.done = true;
Poll::Ready(Some(Err(e)))
}
},
Poll::Pending => Poll::Pending,
}
}
}

View File

@@ -0,0 +1,204 @@
#![allow(clippy::incompatible_msrv)]
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::sync::Arc;
use core::cell::RefCell;
use core::future::Future;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::sync::atomic::AtomicI32;
use core::sync::atomic::Ordering::SeqCst;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use wasm_bindgen::prelude::*;
const SLEEPING: i32 = 0;
const AWAKE: i32 = 1;
struct AtomicWaker {
state: AtomicI32,
}
impl AtomicWaker {
fn new() -> Arc<Self> {
Arc::new(Self {
state: AtomicI32::new(AWAKE),
})
}
fn wake_by_ref(&self) {
// If we're already AWAKE then we previously notified and there's
// nothing to do...
match self.state.swap(AWAKE, SeqCst) {
AWAKE => return,
other => debug_assert_eq!(other, SLEEPING),
}
// ... otherwise we execute the native `notify` instruction to wake up
// the corresponding `waitAsync` that was waiting for the transition
// from SLEEPING to AWAKE.
unsafe {
core::arch::wasm32::memory_atomic_notify(
self.state.as_ptr(),
1, // Number of threads to notify
);
}
}
/// Same as the singlethread module, this creates a standard library
/// `RawWaker`. We could use `futures_util::task::ArcWake` but it's small
/// enough that we just inline it for now.
unsafe fn into_raw_waker(this: Arc<Self>) -> RawWaker {
unsafe fn raw_clone(ptr: *const ()) -> RawWaker {
let ptr = ManuallyDrop::new(Arc::from_raw(ptr as *const AtomicWaker));
AtomicWaker::into_raw_waker((*ptr).clone())
}
unsafe fn raw_wake(ptr: *const ()) {
let ptr = Arc::from_raw(ptr as *const AtomicWaker);
AtomicWaker::wake_by_ref(&ptr);
}
unsafe fn raw_wake_by_ref(ptr: *const ()) {
let ptr = ManuallyDrop::new(Arc::from_raw(ptr as *const AtomicWaker));
AtomicWaker::wake_by_ref(&ptr);
}
unsafe fn raw_drop(ptr: *const ()) {
drop(Arc::from_raw(ptr as *const AtomicWaker));
}
const VTABLE: RawWakerVTable =
RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);
RawWaker::new(Arc::into_raw(this) as *const (), &VTABLE)
}
}
struct Inner {
future: Pin<Box<dyn Future<Output = ()> + 'static>>,
closure: Closure<dyn FnMut(JsValue)>,
}
pub(crate) struct Task {
atomic: Arc<AtomicWaker>,
waker: Waker,
// See `singlethread.rs` for why this is an internal `Option`.
inner: RefCell<Option<Inner>>,
}
impl Task {
pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()> + 'static>>) {
let atomic = AtomicWaker::new();
let waker = unsafe { Waker::from_raw(AtomicWaker::into_raw_waker(atomic.clone())) };
let this = Rc::new(Task {
atomic,
waker,
inner: RefCell::new(None),
});
let closure = {
let this = Rc::clone(&this);
Closure::new(move |_| this.run())
};
*this.inner.borrow_mut() = Some(Inner { future, closure });
// Queue up the Future's work to happen on the next microtask tick.
crate::queue::Queue::with(move |queue| queue.schedule_task(this));
}
pub(crate) fn run(&self) {
let mut borrow = self.inner.borrow_mut();
// Same as `singlethread.rs`, handle spurious wakeups happening after we
// finished.
let inner = match borrow.as_mut() {
Some(inner) => inner,
None => return,
};
loop {
// Also the same as `singlethread.rs`, flag ourselves as ready to
// receive a notification.
let prev = self.atomic.state.swap(SLEEPING, SeqCst);
debug_assert_eq!(prev, AWAKE);
let poll = {
let mut cx = Context::from_waker(&self.waker);
inner.future.as_mut().poll(&mut cx)
};
match poll {
// Same as `singlethread.rs` (noticing a pattern?) clean up
// resources associated with the future ASAP.
Poll::Ready(()) => {
*borrow = None;
}
// Unlike `singlethread.rs` we are responsible for ensuring there's
// a closure to handle the notification that a Future is ready. In
// the single-threaded case the notification itself enqueues work,
// but in the multithreaded case we don't know what thread a
// notification comes from so we need to ensure the current running
// thread is the one that enqueues the work. To do that we execute
// `Atomics.waitAsync`, creating a local Promise on our own thread
// which will resolve once `Atomics.notify` is called.
//
// We could be in one of two states as we execute this:
//
// * `SLEEPING` - we'll get notified via `Atomics.notify`
// and then this Promise will resolve.
//
// * `AWAKE` - the Promise will immediately be resolved and
// we'll execute the work on the next microtask queue.
Poll::Pending => {
match wait_async(&self.atomic.state, SLEEPING) {
Some(promise) => drop(promise.then(&inner.closure)),
// our state has already changed so we can just do the work
// again inline.
None => continue,
}
}
}
break;
}
}
}
fn wait_async(ptr: &AtomicI32, current_value: i32) -> Option<js_sys::Promise> {
// If `Atomics.waitAsync` isn't defined then we use our fallback, otherwise
// we use the native function.
return if Atomics::get_wait_async().is_undefined() {
Some(crate::task::wait_async_polyfill::wait_async(
ptr,
current_value,
))
} else {
let mem = wasm_bindgen::memory().unchecked_into::<js_sys::WebAssembly::Memory>();
let array = js_sys::Int32Array::new(&mem.buffer());
let result = Atomics::wait_async(&array, ptr.as_ptr() as u32 / 4, current_value);
if result.async_() {
Some(result.value())
} else {
None
}
};
#[wasm_bindgen]
extern "C" {
type Atomics;
type WaitAsyncResult;
#[wasm_bindgen(static_method_of = Atomics, js_name = waitAsync)]
fn wait_async(buf: &js_sys::Int32Array, index: u32, value: i32) -> WaitAsyncResult;
#[wasm_bindgen(static_method_of = Atomics, js_name = waitAsync, getter)]
fn get_wait_async() -> JsValue;
#[wasm_bindgen(method, getter, structural, js_name = async)]
fn async_(this: &WaitAsyncResult) -> bool;
#[wasm_bindgen(method, getter, structural)]
fn value(this: &WaitAsyncResult) -> js_sys::Promise;
}
}

View File

@@ -0,0 +1,131 @@
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, RawWaker, RawWakerVTable, Waker};
struct Inner {
future: Pin<Box<dyn Future<Output = ()> + 'static>>,
waker: Waker,
}
pub(crate) struct Task {
// The actual Future that we're executing as part of this task.
//
// This is an Option so that the Future can be immediately dropped when it's
// finished
inner: RefCell<Option<Inner>>,
// This is used to ensure that the Task will only be queued once
is_queued: Cell<bool>,
}
impl Task {
pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()> + 'static>>) {
let this = Rc::new(Self {
inner: RefCell::new(None),
is_queued: Cell::new(true),
});
let waker = unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) };
*this.inner.borrow_mut() = Some(Inner { future, waker });
crate::queue::Queue::with(|queue| queue.schedule_task(this));
}
fn force_wake(this: Rc<Self>) {
crate::queue::Queue::with(|queue| {
queue.push_task(this);
});
}
fn wake(this: Rc<Self>) {
// If we've already been placed on the run queue then there's no need to
// requeue ourselves since we're going to run at some point in the
// future anyway.
if this.is_queued.replace(true) {
return;
}
Self::force_wake(this);
}
fn wake_by_ref(this: &Rc<Self>) {
// If we've already been placed on the run queue then there's no need to
// requeue ourselves since we're going to run at some point in the
// future anyway.
if this.is_queued.replace(true) {
return;
}
Self::force_wake(Rc::clone(this));
}
/// Creates a standard library `RawWaker` from an `Rc` of ourselves.
///
/// Note that in general this is wildly unsafe because everything with
/// Futures requires `Sync` + `Send` with regard to Wakers. For wasm,
/// however, everything is guaranteed to be singlethreaded (since we're
/// compiled without the `atomics` feature) so we "safely lie" and say our
/// `Rc` pointer is good enough.
///
/// The implementation is based off of futures::task::ArcWake
unsafe fn into_raw_waker(this: Rc<Self>) -> RawWaker {
unsafe fn raw_clone(ptr: *const ()) -> RawWaker {
let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
Task::into_raw_waker(Rc::clone(&ptr))
}
unsafe fn raw_wake(ptr: *const ()) {
let ptr = Rc::from_raw(ptr as *const Task);
Task::wake(ptr);
}
unsafe fn raw_wake_by_ref(ptr: *const ()) {
let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task));
Task::wake_by_ref(&ptr);
}
unsafe fn raw_drop(ptr: *const ()) {
drop(Rc::from_raw(ptr as *const Task));
}
static VTABLE: RawWakerVTable =
RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);
RawWaker::new(Rc::into_raw(this) as *const (), &VTABLE)
}
pub(crate) fn run(&self) {
let mut borrow = self.inner.borrow_mut();
// Wakeups can come in after a Future has finished and been destroyed,
// so handle this gracefully by just ignoring the request to run.
let inner = match borrow.as_mut() {
Some(inner) => inner,
None => return,
};
// Ensure that if poll calls `waker.wake()` we can get enqueued back on
// the run queue.
self.is_queued.set(false);
let poll = {
let mut cx = Context::from_waker(&inner.waker);
inner.future.as_mut().poll(&mut cx)
};
// If a future has finished (`Ready`) then clean up resources associated
// with the future ASAP. This ensures that we don't keep anything extra
// alive in-memory by accident. Our own struct, `Rc<Task>` won't
// actually go away until all wakers referencing us go away, which may
// take quite some time, so ensure that the heaviest of resources are
// released early.
if poll.is_ready() {
*borrow = None;
}
}
}

View File

@@ -0,0 +1,90 @@
//!
//! The polyfill was kindly borrowed from https://github.com/tc39/proposal-atomics-wait-async
//! and ported to Rust
//!
#![allow(clippy::incompatible_msrv)]
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Author: Lars T Hansen, lhansen@mozilla.com
*/
/* Polyfill for Atomics.waitAsync() for web browsers.
*
* Any kind of agent that is able to create a new Worker can use this polyfill.
*
* Load this file in all agents that will use Atomics.waitAsync.
*
* Agents that don't call Atomics.waitAsync need do nothing special.
*
* Any kind of agent can wake another agent that is sleeping in
* Atomics.waitAsync by just calling Atomics.wake for the location being slept
* on, as normal.
*
* The implementation is not completely faithful to the proposed semantics: in
* the case where an agent first asyncWaits and then waits on the same location:
* when it is woken, the two waits will be woken in order, while in the real
* semantics, the sync wait will be woken first.
*
* In this polyfill Atomics.waitAsync is not very fast.
*/
/* Implementation:
*
* For every wait we fork off a Worker to perform the wait. Workers are reused
* when possible. The worker communicates with its parent using postMessage.
*/
use alloc::vec;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::sync::atomic::AtomicI32;
use js_sys::{Array, Promise};
use wasm_bindgen::prelude::*;
use web_sys::{MessageEvent, Worker};
#[thread_local]
static HELPERS: RefCell<Vec<Worker>> = RefCell::new(vec![]);
fn alloc_helper() -> Worker {
if let Some(helper) = HELPERS.borrow_mut().pop() {
return helper;
}
let worker_url = wasm_bindgen::link_to!(module = "/src/task/worker.js");
Worker::new(&worker_url).unwrap_or_else(|js| wasm_bindgen::throw_val(js))
}
fn free_helper(helper: Worker) {
let mut helpers = HELPERS.borrow_mut();
helpers.push(helper.clone());
helpers.truncate(10); // random arbitrary limit chosen here
}
pub fn wait_async(ptr: &AtomicI32, value: i32) -> Promise {
Promise::new(&mut |resolve, _reject| {
let helper = alloc_helper();
let helper_ref = helper.clone();
let onmessage_callback = Closure::once_into_js(move |e: MessageEvent| {
// Our helper is done waiting so it's available to wait on a
// different location, so return it to the free list.
free_helper(helper_ref);
drop(resolve.call1(&JsValue::NULL, &e.data()));
});
helper.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
let data = Array::of3(
&wasm_bindgen::memory(),
&JsValue::from(ptr.as_ptr() as u32 / 4),
&JsValue::from(value),
);
helper
.post_message(&data)
.unwrap_or_else(|js| wasm_bindgen::throw_val(js));
})
}

View File

@@ -0,0 +1,6 @@
onmessage = function (ev) {
let [ia, index, value] = ev.data;
ia = new Int32Array(ia.buffer);
let result = Atomics.wait(ia, index, value);
postMessage(result);
};