Skip to content

Commit

Permalink
Revert "Use single-threaded impl when MT is impossible"
Browse files Browse the repository at this point in the history
This reverts commit 7ce38b4.
  • Loading branch information
daxpedda committed Feb 20, 2024
1 parent 7ce38b4 commit 6150e21
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 96 deletions.
5 changes: 0 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@
* Add bindings for `CanvasState.reset()`, affecting `CanvasRenderingContext2D` and `OffscreenCanvasRenderingContext2D`.
[#3844](https://github.com/rustwasm/wasm-bindgen/pull/3844)

### Fixed

* Allow `wasm-bindgen-futures` to run correctly when using the atomics target feature in an environment that has no support for `Atomics.waitAsync()` and without cross-origin isolation.
[#3848](https://github.com/rustwasm/wasm-bindgen/pull/3848)

--------------------------------------------------------------------------------

## [0.2.91](https://github.com/rustwasm/wasm-bindgen/compare/0.2.90...0.2.91)
Expand Down
42 changes: 10 additions & 32 deletions crates/futures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,40 +50,18 @@ pub use js_sys;
pub use wasm_bindgen;

mod task {
use std::future::Future;
use std::pin::Pin;

use cfg_if::cfg_if;

#[cfg(target_feature = "atomics")]
mod multithread;
mod singlethread;
#[cfg(target_feature = "atomics")]
mod wait_async_polyfill;

pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()> + 'static>>) {
cfg_if! {
if #[cfg(target_feature = "atomics")] {
#[wasm_bindgen::prelude::wasm_bindgen]
extern "C" {
/// Returns [`crossOriginIsolated`](https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated) global property.
#[wasm_bindgen(js_name = crossOriginIsolated)]
static CROSS_ORIGIN_ISOLATED: bool;
}

if *CROSS_ORIGIN_ISOLATED {
multithread::Task::spawn(future)
} else {
singlethread::Task::spawn(future)
}
} else {
singlethread::Task::spawn(future)
}
}
}
cfg_if! {
if #[cfg(target_feature = "atomics")] {
mod wait_async_polyfill;
mod multithread;
pub(crate) use multithread::*;

pub(crate) trait Task {
fn run(&self);
} else {
mod singlethread;
pub(crate) use singlethread::*;
}
}
}

Expand All @@ -103,7 +81,7 @@ pub fn spawn_local<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
task::spawn(Box::pin(future));
task::Task::spawn(Box::pin(future));
}

struct Inner {
Expand Down
6 changes: 3 additions & 3 deletions crates/futures/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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<dyn crate::task::Task>>>,
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.
Expand Down Expand Up @@ -58,7 +58,7 @@ pub(crate) struct Queue {

impl Queue {
// Schedule a task to run on the next tick
pub(crate) fn schedule_task(&self, task: Rc<dyn crate::task::Task>) {
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
Expand All @@ -71,7 +71,7 @@ impl Queue {
}
}
// Append a task to the currently running queue, or schedule it
pub(crate) fn push_task(&self, task: Rc<dyn crate::task::Task>) {
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)
Expand Down
45 changes: 21 additions & 24 deletions crates/futures/src/task/multithread.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use super::Task as _;
use std::cell::RefCell;
use std::future::Future;
use std::mem::ManuallyDrop;
Expand Down Expand Up @@ -85,8 +84,27 @@ pub(crate) struct Task {
inner: RefCell<Option<Inner>>,
}

impl super::Task for Task {
fn run(&self) {
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
Expand Down Expand Up @@ -144,27 +162,6 @@ impl super::Task for Task {
}
}

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));
}
}

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.
Expand Down
62 changes: 30 additions & 32 deletions crates/futures/src/task/singlethread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,6 @@ pub(crate) struct Task {
is_queued: Cell<bool>,
}

impl super::Task for Task {
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;
}
}
}

impl Task {
pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()> + 'static>>) {
let this = Rc::new(Self {
Expand Down Expand Up @@ -129,4 +97,34 @@ impl Task {

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;
}
}
}

0 comments on commit 6150e21

Please sign in to comment.