Skip to content

Commit

Permalink
Allow usage on the main web thread with wasm-sync
Browse files Browse the repository at this point in the history
One of the most common complaints I've been receiving in [wasm-bindgen-rayon](https://github.com/RReverser/wasm-bindgen-rayon) that prevents people from using Rayon on the Web is the complexity of manually splitting up the code that uses Rayon into a Web Worker from code that drives the UI.

It requires custom message passing for proxying between two threads (Workers), which, admittedly, feels particularly silly when using a tool that is meant to simplify working with threads for you.

This all stems from a [Wasm limitation](WebAssembly/threads#177) that disallows `atomic.wait` on the main browser thread. In theory, it's a reasonable limitation, since blocking main thread on the web is more problematic than on other platforms as it blocks web app's UI from being responsive altogether, and because there is no limit on how long atomic wait can block. In practice, however, it causes enough issues for users that various toolchains - even Emscripten - work around this issue by spin-locking when on the main thread.

Rust / wasm-bindgen decided not to adopt the same workaround, following general Wasm limitation, which is also a fair stance for general implementation of `Mutex` and other blocking primitives, but I believe Rayon usecase is quite different. Code using parallel iterators is almost always guaranteed to run for less or, worst-case, ~same time as code using regular iterators, so it doesn't make sense to "punish" Rayon users and prevent them from being able to use parallel iterators on the main thread when it will lead to a _more_ responsive UI than using regular iterators.

This PR adds a `cfg`-conditional dependency on [wasm_sync](https://docs.rs/wasm_sync/latest/wasm_sync/) that automatically switches to allowed spin-based `Mutex` and `Condvar` when it detects it's running on the main thread, and to regular `std::sync` based implementation otherwise, thus avoiding the `atomics.wait` error. This dependency will only be added when building for `wasm32-unknown-unknown` - that is, not affecting WASI and Emscripten users - and only when building with `-C target-feature=+atomics`, so not affecting users who rely on Rayon's single-threaded fallback mode either. I hope this kind of very limited override will be acceptable as it makes it much easier to use Rayon on the web.

When this is merged, I'll be able to leverage it in wasm-bindgen-rayon and [significantly simplify](https://github.com/RReverser/wasm-bindgen-rayon/compare/main...RReverser:wasm-bindgen-rayon:wasm-sync?expand=1) demos, tests and docs by avoiding that extra Worker machinery (e.g. see `demo/wasm-worker.js` and `demo/index.js` merged into single simple JS file in the linked diff).
  • Loading branch information
RReverser committed Jan 10, 2024
1 parent b80b406 commit c9628ca
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 5 deletions.
3 changes: 3 additions & 0 deletions rayon-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ categories = ["concurrency"]
crossbeam-deque = "0.8.1"
crossbeam-utils = "0.8.0"

[target.'cfg(all(target_arch = "wasm32", target_os = "unknown", target_feature = "atomics"))'.dependencies]
wasm_sync = "0.1.0"

[dev-dependencies]
rand = "0.8"
rand_xorshift = "0.3"
Expand Down
3 changes: 2 additions & 1 deletion rayon-core/src/latch.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use std::sync::Arc;
use std::usize;

use crate::registry::{Registry, WorkerThread};
use crate::sync::{Condvar, Mutex};

/// We define various kinds of latches, which are all a primitive signaling
/// mechanism. A latch starts as false. Eventually someone calls `set()` and
Expand Down
14 changes: 14 additions & 0 deletions rayon-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,20 @@ pub use self::thread_pool::current_thread_index;
pub use self::thread_pool::ThreadPool;
pub use self::thread_pool::{yield_local, yield_now, Yield};

#[cfg(not(all(
target_arch = "wasm32",
target_os = "unknown",
target_feature = "atomics"
)))]
pub use std::sync;

#[cfg(all(
target_arch = "wasm32",
target_os = "unknown",
target_feature = "atomics"
))]
pub use wasm_sync as sync;

use self::registry::{CustomSpawn, DefaultSpawn, ThreadSpawn};

/// Returns the maximum number of threads that Rayon supports in a single thread-pool.
Expand Down
3 changes: 2 additions & 1 deletion rayon-core/src/registry.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::job::{JobFifo, JobRef, StackJob};
use crate::latch::{AsCoreLatch, CoreLatch, Latch, LatchRef, LockLatch, OnceLatch, SpinLatch};
use crate::sleep::Sleep;
use crate::sync::Mutex;
use crate::unwind;
use crate::{
ErrorKind, ExitHandler, PanicHandler, StartHandler, ThreadPoolBuildError, ThreadPoolBuilder,
Expand All @@ -15,7 +16,7 @@ use std::io;
use std::mem;
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, Once};
use std::sync::{Arc, Once};
use std::thread;
use std::usize;

Expand Down
2 changes: 1 addition & 1 deletion rayon-core/src/sleep/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//! for an overview.

use crate::latch::CoreLatch;
use crate::sync::{Condvar, Mutex};
use crossbeam_utils::CachePadded;
use std::sync::atomic::Ordering;
use std::sync::{Condvar, Mutex};
use std::thread;
use std::usize;

Expand Down
2 changes: 1 addition & 1 deletion src/iter/par_bridge.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rayon_core::sync::Mutex;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Mutex;

use crate::iter::plumbing::{bridge_unindexed, Folder, UnindexedConsumer, UnindexedProducer};
use crate::iter::ParallelIterator;
Expand Down
2 changes: 1 addition & 1 deletion src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use crate::iter::plumbing::*;
use crate::iter::*;
use std::sync::Mutex;
use rayon_core::sync::Mutex;

use crate::option;

Expand Down

0 comments on commit c9628ca

Please sign in to comment.