Skip to content

Commit

Permalink
Modernize some documentation
Browse files Browse the repository at this point in the history
Mostly around the raytrace example but also contributing new web-sys
APIs.
  • Loading branch information
alexcrichton committed Jul 29, 2020
1 parent c6db488 commit b1daf81
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 70 deletions.
2 changes: 1 addition & 1 deletion crates/futures/src/task/multithread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl Task {
// * `AWAKE` - the Promise will immediately be resolved and
// we'll execute the work on the next microtask queue.
Poll::Pending => {
wait_async(&self.atomic.state, SLEEPING).then(&inner.closure);
drop(wait_async(&self.atomic.state, SLEEPING).then(&inner.closure));
}
}
}
Expand Down
15 changes: 7 additions & 8 deletions examples/raytrace-parallel/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ set -ex
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory' \
cargo build --target wasm32-unknown-unknown --release -Z build-std=std,panic_abort

# Note the usage of `--no-modules` here which is used to create an output which
# is usable from Web Workers. We notably can't use `--target bundler` since
# Webpack doesn't have support for atomics yet.
cargo run --manifest-path ../../crates/cli/Cargo.toml \
--bin wasm-bindgen -- \
../../target/wasm32-unknown-unknown/release/raytrace_parallel.wasm --out-dir . \
--no-modules
# Note the usage of `--target no-modules` here which is required for passing
# the memory import to each wasm module.
cargo run -p wasm-bindgen-cli -- \
../../target/wasm32-unknown-unknown/release/raytrace_parallel.wasm \
--out-dir . \
--target no-modules

python3 -m http.server
python3 server.py
3 changes: 1 addition & 2 deletions examples/raytrace-parallel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ concurrency.disabled = true;

// First up, but try to do feature detection to provide better error messages
function loadWasm() {
let msg = 'This demo requires a current version of Firefox (e.g., 70.0) with\n'
msg += 'the `javascript.options.shared_memory` option enabled in `about:config`';
let msg = 'This demo requires a current version of Firefox (e.g., 79.0)';
if (typeof SharedArrayBuffer !== 'function') {
alert('this browser does not have SharedArrayBuffer support enabled' + '\n\n' + msg);
return
Expand Down
47 changes: 5 additions & 42 deletions guide/src/contributing/web-sys/supporting-more-web-apis.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Supporting More Web APIs in `web-sys`

1. <input type="checkbox"/> Ensure that the `.webidl` file describing the
1. Ensure that the `.webidl` file describing the
interface exists somewhere within the `crates/web-sys/webidls/enabled`
directory.

Expand Down Expand Up @@ -28,49 +28,12 @@
git mv webidls/unavailable_enum_ident/MyWebApi.webidl webidls/enabled/MyWebApi.webidl
```

2. <input type="checkbox"/> Verify that the `web-sys` crate still builds and
that its tests still pass with the new `.webidl` file enabled:
2. Regenerate the `web-sys` crate auto-generated bindings, which you can do with
the following commands:

```sh
cd crates/web-sys
cargo build
cargo test
cargo run --release --package wasm-bindgen-webidl webidls src/features
```

3. <input type="checkbox"/> Verify that bindings are being generated for your new
API by generating the documentation and searching for the new API in it:

```sh
cd crates/web-sys
cargo doc --open
# search for the new API in the opened docs
```

* <input type="checkbox"/> If the new API is **not** showing up in the docs,
rebuild the `web-sys` crate [with logging enabled](logging.html)
and look for warning messages that mention your new API. Figure out why
bindings weren't generated and then add support to `wasm_bindgen_webidl` for
whatever is needed to generate your API's bindings.

> You might find it helpful to view the generated rust bindings, to see if
> they are what you would expect. The file will be located at
> `target/wasm32-unknown-unknown/debug/build/web-sys-xxx/out/bindings.rs`,
> where `xxx` is a combinations of numbers and letters that represents your
> build. This file is pretty unintelligable until you run `rustfmt` on it, like
> `rustfmt target/wasm32-unknown-unknown/debug/build/web-sys-xxx/out/bindings.rs`.

> There are commented out lines in `web-sys/build.rs` that run rustfmt as part of
> the build process, and this can be very helpful for debugging as any error
> messages with inline code will display it in a readable format.

4. <input type="checkbox"/> Add tests for as many of the features in the WebIDL file
as possible to `crates/web-sys/tests/all/`. See the
[`web-sys` testing documentation](testing.html) for details.

> __Note__: Start here at __4__ if the WebIDL has already been added but doesn't have
> full test coverage, then go back to __3__ if you find any problems.
5. <input type="checkbox"/> If all entities in the WebIDL file have full test coverage,
mark the WebIDL script in the `README.md` file as complete by changing `[ ]` to `[x]`.
6. <input type="checkbox"/> Send a pull request! 😊
You can then use `git diff` to ensure the bindings look correct.
134 changes: 117 additions & 17 deletions guide/src/examples/raytrace.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,120 @@
[online]: https://wasm-bindgen.netlify.app/exbuild/raytrace-parallel/
[code]: https://github.com/rustwasm/wasm-bindgen/tree/master/examples/raytrace-parallel

**This is an unstable and experimental example** of using threads with
WebAssembly and Rust, culminating in a parallel raytracer demo. The browser requirements are:

* Firefox Nightly
- `SharedArrayBuffer` is enabled in `about:config` in Firefox
* Google Chrome
- No flags required on recent versions of Chrome
* other browsers haven't implemented the proposed WebAssembly features yet.

Locally to build this demo you'll need `xargo` and the `rust-src` rustup
component, and afterwards `./build.sh` like other examples should build the
example.

Again, to reiterate, this is all experimental and we're working through various
issues as we're working on this. If you're curious to see how this works it's
best to explore via the source code right now! More info will be available here
once WebAssembly threads are closer to stabilization.
This is an of using threads with WebAssembly, Rust, and `wasm-bindgen`,

This comment has been minimized.

Copy link
@kettle11

kettle11 Jul 29, 2020

Typo. Should be "This is an example of using".

(Also thanks for updating these docs, this is incredibly helpful!)

culminating in a parallel raytracer demo. There's a number of moving pieces to
this demo and it's unfortunately not the easiest thing to wrangle, but it's
hoped that this'll give you a bit of a taste of what it's like to use threads
and wasm with Rust on the web.

### Building the demo

One of the major gotchas with threaded WebAssembly is that Rust does not ship a
precompiled target (e.g. standard library) which has threading support enabled.
This means that you'll need to recompile the standard library with the
appropriate rustc flags, namely `-C target-feature=+atomics,+bulk-memory`.

To do this you can use the `RUSTFLAGS` environment variable that Cargo reads:

```sh
export RUSTFLAGS='-C target-feature=+atomics,+bulk-memory'
```

To recompile the standard library it's recommended to use Cargo's
[`-Zbuild-std`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std)
feature:

```sh
cargo build --target wasm32-unknown-unknown -Z build-std=panic_abort,std
```

Note that you can also configure this via `.cargo/config.toml`:

```toml
[unstable]
build-std = ['std', 'panic_abort']

[build]
target = "wasm32-unknown-unknown"
rustflags = '-Ctarget-feature=+atomics,+bulk-memory'
```

After this `cargo build` should produce a WebAssembly file with threading
enabled, and the standard library will be appropriately compiled as well.

The final step in this is to run `wasm-bindgen` as usual, and `wasm-bindgen`
needs no extra configuration to work with threads. You can continue to run it
through `wasm-pack`, for example.

### Running the demo

Currently it's required to use the `--target no-modules` flag with
`wasm-bindgen` to run threaded code. This is because the WebAssembly file
imports memory instead of exporting it, so we need to hook initialization of the
wasm module at this time to provide the appropriate memory object.

With `--target no-modules` you'll be able to use `importScripts` inside of each
web worker to import the shim JS generated by `wasm-bindgen` as well as calling
the `wasm_bindgen` initialization function with the shared memory instance from
the main thread. The expected usage is that WebAssembly on the main thread will
post its memory object to all other threads to get instantiated with.

### Caveats

Unfortunately at this time running wasm on the web with threads has a number of
caveats, although some are specific to just `wasm-bindgen`. These are some
pieces to consider and watch out for, although we're always looking for
improvements to be made so if you have an idea please file an issue!

* The main thread in a browser cannot block. This means that if you run
WebAssembly code on the main thread you can *never* block, meaning you can't
do so much as acquire a mutex. This is an extremely difficult limitation to
work with on the web, although one workaround is to run wasm exclusively in
web workers and run JS on the main thread. It is possible to run the same wasm
across all threads, but you need to be extremely vigilant about
synchronization with the main thread.

* Setting up a threaded environment is a bit wonky and doesn't feel smooth
today. For example `--target no-modules` is required with `wasm-bindgen` and
very specific shims are required on both the main thread and worker threads.
These are possible to work with but are somewhat brittle since there's no
standard way to spin up web workers as wasm threads.

* There is no standard notion of a "thread". For example the standard library
has no viable route to implement the `std::thread` module. As a consequence
there is no concept of thread exit and TLS destructors will never run. With no
concept of a thread exit thread stacks will also never be deallocated.
Currently the intention is that with threaded wasm a pool of threads will be
used but that pool is initialized once and never changes over time, since
resources are never reclaimed from it. Much of this has to do with the
`#[wasm_bindgen]`-specific handling of threads. You can get more advanced, but
at that point you may have to not use `wasm-bindgen` as well.

* Web Workers executing WebAssembly code cannot receive events from JS. A Web
Worker has to fully return back to the browser (and ideally should do so
occasionally) to receive JS messages and such. This means that common
paradigms like a rayon thread pool do not apply straightforward-ly to the web.
The intention of the web is that all long-term blocking happens in the browser
itself, not in each thread, but many crates in the ecosystem leveraging
threading are not necessarily engineered this way.

These caveats are all largely inherited from the web platform itself, and
they're important to consider when designing an application for threading. It's
highly unlikely that you can pull a crate off the shelf and "just use it" due to
these limitations. You'll need to be sure to carefully plan ahead and ensure
that gotchas such as these don't cause issues in the future. As mentioned before
though we're always trying to actively develop this support so if folks have
ideas about how to improve, or if web standards change, we'll try to update this
documentation!

### Browser Requirements

This demo should work in the latest Firefox and Chrome versions at this time,
and other browsers are likely to follow suit. Note that threads and
`SharedArrayBuffer` require HTTP headers to be set to work correctly. For more
information see the [documentation on
MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)
under "Security requirements" as well as [Firefox's rollout blog
post](https://hacks.mozilla.org/2020/07/safely-reviving-shared-memory/). This
means that during local development you'll need to configure your web server
appropriately or enable a workaround in your browser.

0 comments on commit b1daf81

Please sign in to comment.