Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasi-threads: an initial implementation #5484

Merged
merged 33 commits into from
Feb 7, 2023

Conversation

abrown
Copy link
Contributor

@abrown abrown commented Dec 20, 2022

This PR builds on several other under-review PRs to demonstrate a working wasi-threads implementation in Wasmtime. The first three commits are cherry-picked from the following PRs:

The last two commits build on these to expose the wasi-threads API to WebAssembly modules. Upon building and running Wasmtime with the right flags, one can then run multi-threaded WebAssembly programs:

$ cargo run --features wasi-threads -- \
    --wasm-features threads --wasi-modules experimental-wasi-threads \
    <a threads-enabled module>

Due to Cargo feature issues in 05020d4, this PR is not yet expected to pass all CI tasks but, perhaps more interestingly, one can use these changes to run some simple pthread tests over in the wasi-libc repository: WebAssembly/wasi-libc#369.

@github-actions github-actions bot added the wasi Issues pertaining to WASI label Dec 21, 2022
@github-actions
Copy link

Subscribe to Label Action

cc @kubkon

This issue or pull request has been labeled: "wasi"

Thus the following users have been cc'd because of the following labels:

  • kubkon: wasi

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

crates/wasi-common/src/snapshots/preview_1.rs Outdated Show resolved Hide resolved
crates/wasi-threads/README.md Show resolved Hide resolved
crates/wasi-threads/LICENSE Outdated Show resolved Hide resolved
tests/all/cli_tests/threads.wat Outdated Show resolved Hide resolved
crates/wasi-threads/src/lib.rs Outdated Show resolved Hide resolved
crates/wasi-threads/src/lib.rs Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
crates/wasi-common/cap-std-sync/src/file.rs Outdated Show resolved Hide resolved
crates/wasi-common/cap-std-sync/src/sched/unix.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/file.rs Outdated Show resolved Hide resolved
crates/wasi-common/src/ctx.rs Show resolved Hide resolved
crates/wasi-threads/src/lib.rs Outdated Show resolved Hide resolved
crates/wasi-threads/src/lib.rs Outdated Show resolved Hide resolved
@abrown abrown marked this pull request as ready for review February 1, 2023 16:35
src/commands/run.rs Outdated Show resolved Hide resolved
src/commands/run.rs Outdated Show resolved Hide resolved
@alexcrichton
Copy link
Member

Oh one thing as well, it looks like this test may not be running on CI since it requires explicit opt-in to run. That being said including the threads feature in the build by default I think is reasonable since it doesn't pull in any extra build requirements and still requires a flag to enable at runtime.

@abrown
Copy link
Contributor Author

abrown commented Feb 1, 2023

Oh one thing as well, it looks like this test may not be running on CI since it requires explicit opt-in to run. That being said including the threads feature in the build by default I think is reasonable since it doesn't pull in any extra build requirements and still requires a flag to enable at runtime.

That sounds good, but out of an abundance of caution, can we merge as-is, then make the feature default? I want to get a few days of no-issue CI builds and general usage. Also, I want to take another pass on the testing side, improving the one existing test and possibly including some other tests that others have been working on.

@alexcrichton
Copy link
Member

Sure yeah, would you prefer to not test at all on CI while manual testing is done?

@abrown
Copy link
Contributor Author

abrown commented Feb 1, 2023

I'm fine with manual testing for now (like, a week?) of the wasi-threads parts--they are pretty straight-forward. The parts I'm concerned about getting some usage of are the changes to wasi-common.

crates/wasi-common/src/snapshots/preview_1.rs Show resolved Hide resolved
crates/wasi-common/src/snapshots/preview_1.rs Show resolved Hide resolved
crates/wasi/src/lib.rs Outdated Show resolved Hide resolved
crates/wasi-threads/src/lib.rs Show resolved Hide resolved
Copy link
Member

@tschneidereit tschneidereit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this PR during last week's Wasmtime meeting, and I had a call with @abrown about it on Friday. Based on these conversations, I support merging (almost, see below) as-is.

The feature is disabled by default, and needs the --experimental-wasi-threads flag to be passed to be enabled. Additionally, it implements an active proposal in the WASI standardization process. This combination clears the bar for Wasmtime's criteria for shipping content-visible features.

As for the changes themselves, the one request I have is to change the documentation to include wasi-threads in the list of supported proposals.


I do want to emphasize that merging this PR doesn't represent commitment by the Wasmtime project to ship wasi-threads to production, enabled by default. As documented, that will require the wasi-threads proposal itself reaching stage 4 in the standardization process.

I'm highlighting this because there are open questions around how to specify and implement wasi-threads in terms of the Component Model. More specifically, the Component Model requires all APIs to be fully virtualizable, and to uphold full encapsulation between separate components. Based on conversations with @abrown, @alexcrichton, @sunfishcode, and @lukewagner, I understand that there are candidate solutions for these problems, but none that are known to work as of yet.

Vetting one of these candidate solutions to a point where there is sufficient shared optimism about its viability would be an important next step.

haraldh and others added 7 commits February 7, 2023 11:19
This patch adds interior mutability to the WasiCtx Table and the Table elements.

Major pain points:
* `File` only needs `RwLock<cap_std::fs::File>` to implement
  `File::set_fdflags()` on Windows, because of [1]
* Because `File` needs a `RwLock` and `RwLock*Guard` cannot
  be hold across an `.await`, The `async` from
  `async fn num_ready_bytes(&self)` had to be removed
* Because `File` needs a `RwLock` and `RwLock*Guard` cannot
  be dereferenced in `pollable`, the signature of
  `fn pollable(&self) -> Option<rustix::fd::BorrowedFd>`
  changed to `fn pollable(&self) -> Option<Arc<dyn AsFd + '_>>`

[1] https://github.com/bytecodealliance/system-interface/blob/da238e324e752033f315f09c082ad9ce35d42696/src/fs/fd_flags.rs#L210-L217

Signed-off-by: Harald Hoyer <[email protected]>
This change is a first step toward implementing `wasi-threads` in
Wasmtime. We may find that it has some missing pieces, but the core
functionality is there: when `wasi::thread_spawn` is called by a running
WebAssembly module, a function named `wasi_thread_start` is found in the
module's exports and called in a new instance. The shared memory of the
original instance is reused in the new instance.

This new WASI proposal is in its early stages and details are still
being hashed out in the [spec] and [wasi-libc] repositories. Due to its
experimental state, the `wasi-threads` functionality is hidden behind
both a compile-time and runtime flag: one must build with `--features
wasi-threads` but also run the Wasmtime CLI with `--wasm-features
threads` and `--wasi-modules experimental-wasi-threads`. One can
experiment with `wasi-threads` by running:

```console
$ cargo run --features wasi-threads -- \
    --wasm-features threads --wasi-modules experimental-wasi-threads \
    <a threads-enabled module>
```

Threads-enabled Wasm modules are not yet easy to build. Hopefully this
is resolved soon, but in the meantime see the use of
`THREAD_MODEL=posix` in the [wasi-libc] repository for some clues on
what is necessary. Wiggle complicates things by requiring the Wasm
memory to be exported with a certain name and `wasi-threads` also
expects that memory to be imported; this build-time obstacle can be
overcome with the `--import-memory --export-memory` flags only available
in the latest Clang tree. Due to all of this, the included tests are
written directly in WAT--run these with:

```console
$ cargo test --features wasi-threads -p wasmtime-cli -- cli_tests
```

[spec]: https://github.com/WebAssembly/wasi-threads
[wasi-libc]: https://github.com/WebAssembly/wasi-libc

This change does not protect the WASI implementations themselves from
concurrent access. This is already complete in previous commits or left
for future commits in certain cases (e.g., wasi-nn).
As is being discussed [elsewhere], either calling `proc_exit` or
trapping in any thread should halt execution of all threads. The
Wasmtime CLI already has logic for adapting a WebAssembly error code to
a code expected in each OS. This change factors out this logic to a new
function, `maybe_exit_on_error`, for use within the `wasi-threads`
implementation.

This will work reasonably well for CLI users of Wasmtime +
`wasi-threads`, but embedders will want something better in the future:
when a `wasi-threads` threads fails, they may not want their application
to exit. Handling this is tricky, because it will require cancelling the
threads spawned by the `wasi-threads` implementation, something that is
not trivial to do in Rust. With this change, we defer that work until
later in order to provide a working implementation of `wasi-threads` for
experimentation.

[elsewhere]: WebAssembly/wasi-threads#17
In order to make progress with wasi-threads, this change temporarily
works around limitations induced by `wasi-common`'s
`fd_fdstat_set_flags` to allow `&mut self` use in the implementation.
Eventual resolution is tracked in
bytecodealliance#5643. This change
makes several related helper functions (e.g., `set_fdflags`) take `&mut
self` as well.

Co-authored-by: Alex Crichton <[email protected]>
@abrown
Copy link
Contributor Author

abrown commented Feb 7, 2023

Rebased to handle conflicts due to version bumps and #5683.

@abrown abrown merged commit edfa10d into bytecodealliance:main Feb 7, 2023
@abrown abrown deleted the wasi-threads-revisited branch February 7, 2023 21:43
@TerrorJack
Copy link
Contributor

Note that the thread-spawn name in wasi-libc isn't coherent with the thread_spawn name used in wasmtime.

abrown added a commit to abrown/wasmtime that referenced this pull request Feb 8, 2023
As @TerrorJack pointed out in bytecodealliance#5484, that PR implements an older
name--`thread_spawn`. This change uses the now-official name from the
specification--`thread-spawn`.
@abrown
Copy link
Contributor Author

abrown commented Feb 8, 2023

Note that the thread-spawn name in wasi-libc isn't coherent with the thread_spawn name used in wasmtime.

@TerrorJack, thanks for the heads up. Yes, there are a few details that still need to be cleaned up, like this one. I opened #5748 to resolve this.

alexcrichton pushed a commit that referenced this pull request Feb 8, 2023
* wasi-threads: fix import name

As @TerrorJack pointed out in #5484, that PR implements an older
name--`thread_spawn`. This change uses the now-official name from the
specification--`thread-spawn`.

* fix: update name in test
tests/all/cli_tests/threads.wat Show resolved Hide resolved
tests/all/cli_tests/threads.wat Show resolved Hide resolved
;; A helper function for printing ptr-len strings.
(func $print (param $ptr i32) (param $len i32)
(i32.store (i32.const 8) (local.get $len))
(i32.store (i32.const 4) (local.get $ptr))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function doesn't seem thread-safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking about atomic stores or a lock around the $__wasi_fd_write call? (Recall that wasi-common has some internal locking).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. it's about the linear memory used for iovec.

abrown added a commit to abrown/wasmtime that referenced this pull request Feb 23, 2023
As @yamt points out [here], the `wait`/`notify` pairing used in this
manual WAT test was not effective. The `wait` always immediately
returned, meaning that the main thread essentially spins until a counter
is atomically incremented. This is fine for test correctness, but was
not the original intent, which was lost in a refactoring. This change
uses the `$i` local to keep track of the counter value we expect to see
for the `wait`, so that the `wait`/`notify` pair actually waits as
expected.

[here]: bytecodealliance#5484 (comment)
abrown added a commit to abrown/wasi-threads that referenced this pull request Feb 23, 2023
Now that Wasmtime has merged
bytecodealliance/wasmtime#5484 the language used
in the README can be updated.
loganek pushed a commit to WebAssembly/wasi-threads that referenced this pull request Feb 23, 2023
Now that Wasmtime has merged
bytecodealliance/wasmtime#5484 the language used
in the README can be updated.
alexcrichton pushed a commit that referenced this pull request Feb 23, 2023
As @yamt points out [here], the `wait`/`notify` pairing used in this
manual WAT test was not effective. The `wait` always immediately
returned, meaning that the main thread essentially spins until a counter
is atomically incremented. This is fine for test correctness, but was
not the original intent, which was lost in a refactoring. This change
uses the `$i` local to keep track of the counter value we expect to see
for the `wait`, so that the `wait`/`notify` pair actually waits as
expected.

[here]: #5484 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wasi Issues pertaining to WASI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants