|
5 | 5 | [online]: https://wasm-bindgen.netlify.app/exbuild/raytrace-parallel/
|
6 | 6 | [code]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/raytrace-parallel
|
7 | 7 |
|
8 |
| -**This is an unstable and experimental example** of using threads with |
9 |
| -WebAssembly and Rust, culminating in a parallel raytracer demo. The browser requirements are: |
10 |
| - |
11 |
| -* Firefox Nightly |
12 |
| - - `SharedArrayBuffer` is enabled in `about:config` in Firefox |
13 |
| -* Google Chrome |
14 |
| - - No flags required on recent versions of Chrome |
15 |
| -* other browsers haven't implemented the proposed WebAssembly features yet. |
16 |
| - |
17 |
| -Locally to build this demo you'll need `xargo` and the `rust-src` rustup |
18 |
| -component, and afterwards `./build.sh` like other examples should build the |
19 |
| -example. |
20 |
| - |
21 |
| -Again, to reiterate, this is all experimental and we're working through various |
22 |
| -issues as we're working on this. If you're curious to see how this works it's |
23 |
| -best to explore via the source code right now! More info will be available here |
24 |
| -once WebAssembly threads are closer to stabilization. |
| 8 | +This is an of using threads with WebAssembly, Rust, and `wasm-bindgen`, |
| 9 | +culminating in a parallel raytracer demo. There's a number of moving pieces to |
| 10 | +this demo and it's unfortunately not the easiest thing to wrangle, but it's |
| 11 | +hoped that this'll give you a bit of a taste of what it's like to use threads |
| 12 | +and wasm with Rust on the web. |
| 13 | + |
| 14 | +### Building the demo |
| 15 | + |
| 16 | +One of the major gotchas with threaded WebAssembly is that Rust does not ship a |
| 17 | +precompiled target (e.g. standard library) which has threading support enabled. |
| 18 | +This means that you'll need to recompile the standard library with the |
| 19 | +appropriate rustc flags, namely `-C target-feature=+atomics,+bulk-memory`. |
| 20 | + |
| 21 | +To do this you can use the `RUSTFLAGS` environment variable that Cargo reads: |
| 22 | + |
| 23 | +```sh |
| 24 | +export RUSTFLAGS='-C target-feature=+atomics,+bulk-memory' |
| 25 | +``` |
| 26 | + |
| 27 | +To recompile the standard library it's recommended to use Cargo's |
| 28 | +[`-Zbuild-std`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std) |
| 29 | +feature: |
| 30 | + |
| 31 | +```sh |
| 32 | +cargo build --target wasm32-unknown-unknown -Z build-std=panic_abort,std |
| 33 | +``` |
| 34 | + |
| 35 | +Note that you can also configure this via `.cargo/config.toml`: |
| 36 | + |
| 37 | +```toml |
| 38 | +[unstable] |
| 39 | +build-std = ['std', 'panic_abort'] |
| 40 | + |
| 41 | +[build] |
| 42 | +target = "wasm32-unknown-unknown" |
| 43 | +rustflags = '-Ctarget-feature=+atomics,+bulk-memory' |
| 44 | +``` |
| 45 | + |
| 46 | +After this `cargo build` should produce a WebAssembly file with threading |
| 47 | +enabled, and the standard library will be appropriately compiled as well. |
| 48 | + |
| 49 | +The final step in this is to run `wasm-bindgen` as usual, and `wasm-bindgen` |
| 50 | +needs no extra configuration to work with threads. You can continue to run it |
| 51 | +through `wasm-pack`, for example. |
| 52 | + |
| 53 | +### Running the demo |
| 54 | + |
| 55 | +Currently it's required to use the `--target no-modules` flag with |
| 56 | +`wasm-bindgen` to run threaded code. This is because the WebAssembly file |
| 57 | +imports memory instead of exporting it, so we need to hook initialization of the |
| 58 | +wasm module at this time to provide the appropriate memory object. |
| 59 | + |
| 60 | +With `--target no-modules` you'll be able to use `importScripts` inside of each |
| 61 | +web worker to import the shim JS generated by `wasm-bindgen` as well as calling |
| 62 | +the `wasm_bindgen` initialization function with the shared memory instance from |
| 63 | +the main thread. The expected usage is that WebAssembly on the main thread will |
| 64 | +post its memory object to all other threads to get instantiated with. |
| 65 | + |
| 66 | +### Caveats |
| 67 | + |
| 68 | +Unfortunately at this time running wasm on the web with threads has a number of |
| 69 | +caveats, although some are specific to just `wasm-bindgen`. These are some |
| 70 | +pieces to consider and watch out for, although we're always looking for |
| 71 | +improvements to be made so if you have an idea please file an issue! |
| 72 | + |
| 73 | +* The main thread in a browser cannot block. This means that if you run |
| 74 | + WebAssembly code on the main thread you can *never* block, meaning you can't |
| 75 | + do so much as acquire a mutex. This is an extremely difficult limitation to |
| 76 | + work with on the web, although one workaround is to run wasm exclusively in |
| 77 | + web workers and run JS on the main thread. It is possible to run the same wasm |
| 78 | + across all threads, but you need to be extremely vigilant about |
| 79 | + synchronization with the main thread. |
| 80 | + |
| 81 | +* Setting up a threaded environment is a bit wonky and doesn't feel smooth |
| 82 | + today. For example `--target no-modules` is required with `wasm-bindgen` and |
| 83 | + very specific shims are required on both the main thread and worker threads. |
| 84 | + These are possible to work with but are somewhat brittle since there's no |
| 85 | + standard way to spin up web workers as wasm threads. |
| 86 | + |
| 87 | +* There is no standard notion of a "thread". For example the standard library |
| 88 | + has no viable route to implement the `std::thread` module. As a consequence |
| 89 | + there is no concept of thread exit and TLS destructors will never run. With no |
| 90 | + concept of a thread exit thread stacks will also never be deallocated. |
| 91 | + Currently the intention is that with threaded wasm a pool of threads will be |
| 92 | + used but that pool is initialized once and never changes over time, since |
| 93 | + resources are never reclaimed from it. Much of this has to do with the |
| 94 | + `#[wasm_bindgen]`-specific handling of threads. You can get more advanced, but |
| 95 | + at that point you may have to not use `wasm-bindgen` as well. |
| 96 | + |
| 97 | +* Web Workers executing WebAssembly code cannot receive events from JS. A Web |
| 98 | + Worker has to fully return back to the browser (and ideally should do so |
| 99 | + occasionally) to receive JS messages and such. This means that common |
| 100 | + paradigms like a rayon thread pool do not apply straightforward-ly to the web. |
| 101 | + The intention of the web is that all long-term blocking happens in the browser |
| 102 | + itself, not in each thread, but many crates in the ecosystem leveraging |
| 103 | + threading are not necessarily engineered this way. |
| 104 | + |
| 105 | +These caveats are all largely inherited from the web platform itself, and |
| 106 | +they're important to consider when designing an application for threading. It's |
| 107 | +highly unlikely that you can pull a crate off the shelf and "just use it" due to |
| 108 | +these limitations. You'll need to be sure to carefully plan ahead and ensure |
| 109 | +that gotchas such as these don't cause issues in the future. As mentioned before |
| 110 | +though we're always trying to actively develop this support so if folks have |
| 111 | +ideas about how to improve, or if web standards change, we'll try to update this |
| 112 | +documentation! |
| 113 | + |
| 114 | +### Browser Requirements |
| 115 | + |
| 116 | +This demo should work in the latest Firefox and Chrome versions at this time, |
| 117 | +and other browsers are likely to follow suit. Note that threads and |
| 118 | +`SharedArrayBuffer` require HTTP headers to be set to work correctly. For more |
| 119 | +information see the [documentation on |
| 120 | +MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) |
| 121 | +under "Security requirements" as well as [Firefox's rollout blog |
| 122 | +post](https://hacks.mozilla.org/2020/07/safely-reviving-shared-memory/). This |
| 123 | +means that during local development you'll need to configure your web server |
| 124 | +appropriately or enable a workaround in your browser. |
0 commit comments