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

Wasm32-wasi target support #4716

Merged
merged 8 commits into from Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 17 additions & 3 deletions .github/workflows/ci.yml
Expand Up @@ -489,9 +489,23 @@ jobs:
- name: Install cargo-wasi
run: cargo install cargo-wasi

# TODO: Expand this when full WASI support lands.
# Currently, this is a bare bones regression test
# for features that work today with wasi.
- name: WASI test tokio full
run: cargo test -p tokio --target wasm32-wasi --features full
env:
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings

- name: WASI test tokio-util full
run: cargo test -p tokio-util --target wasm32-wasi --features full
env:
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings

- name: WASI test tokio-stream
run: cargo test -p tokio-stream --target wasm32-wasi --features time,net,io-util,sync
env:
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
RUSTFLAGS: --cfg tokio_unstable -Dwarnings

- name: test tests-integration --features wasi-rt
# TODO: this should become: `cargo hack wasi test --each-feature`
Expand Down
6 changes: 5 additions & 1 deletion tests-integration/tests/macros_main.rs
@@ -1,4 +1,8 @@
#![cfg(all(feature = "macros", feature = "rt-multi-thread"))]
#![cfg(all(
feature = "macros",
feature = "rt-multi-thread",
not(target_os = "wasi")
))]

#[tokio::main]
async fn basic_main() -> usize {
Expand Down
1 change: 1 addition & 0 deletions tests-integration/tests/macros_select.rs
Expand Up @@ -4,6 +4,7 @@ use futures::channel::oneshot;
use futures::executor::block_on;
use std::thread;

#[cfg_attr(target_os = "wasi", ignore = "WASI: std::thread::spawn not supported")]
Copy link
Member

Choose a reason for hiding this comment

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

I'm OK w/ this for now, but maybe we can open a tracking issue to remove this? The test itself doesn't look like it needs thread spawning. I'm not exactly sure why it is the way it is.

#[test]
fn join_with_select() {
block_on(async {
Expand Down
2 changes: 1 addition & 1 deletion tests-integration/tests/process_stdio.rs
@@ -1,5 +1,5 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
#![cfg(all(feature = "full", not(target_os = "wasi")))]

use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::join;
Expand Down
1 change: 1 addition & 0 deletions tokio-stream/Cargo.toml
Expand Up @@ -38,6 +38,7 @@ parking_lot = "0.12.0"
tokio-test = { path = "../tokio-test" }
futures = { version = "0.3", default-features = false }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
proptest = "1"

[package.metadata.docs.rs]
Expand Down
2 changes: 1 addition & 1 deletion tokio-stream/tests/stream_panic.rs
@@ -1,5 +1,5 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "time")]
#![cfg(all(feature = "time", not(target_os = "wasi")))] // Wasi does not support panic recovery

use parking_lot::{const_mutex, Mutex};
use std::error::Error;
Expand Down
1 change: 1 addition & 0 deletions tokio-stream/tests/stream_stream_map.rs
Expand Up @@ -325,6 +325,7 @@ fn one_ready_many_none() {
}
}

#[cfg(not(target_os = "wasi"))]
proptest::proptest! {
#[test]
fn fuzz_pending_complete_mix(kinds: Vec<bool>) {
Expand Down
1 change: 1 addition & 0 deletions tokio-util/src/lib.rs
Expand Up @@ -29,6 +29,7 @@ cfg_codec! {
}

cfg_net! {
#[cfg(not(target_arch = "wasm32"))]
pub mod udp;
pub mod net;
}
Expand Down
2 changes: 2 additions & 0 deletions tokio-util/src/task/mod.rs
Expand Up @@ -2,7 +2,9 @@

#[cfg(tokio_unstable)]
mod join_map;
#[cfg(not(target_os = "wasi"))]
mod spawn_pinned;
#[cfg(not(target_os = "wasi"))]
pub use spawn_pinned::LocalPoolHandle;

#[cfg(tokio_unstable)]
Expand Down
1 change: 1 addition & 0 deletions tokio-util/tests/context.rs
@@ -1,4 +1,5 @@
#![cfg(feature = "rt")]
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads
#![warn(rust_2018_idioms)]

use tokio::runtime::Builder;
Expand Down
1 change: 1 addition & 0 deletions tokio-util/tests/io_sync_bridge.rs
@@ -1,4 +1,5 @@
#![cfg(feature = "io-util")]
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads

use std::error::Error;
use std::io::{Cursor, Read, Result as IoResult};
Expand Down
2 changes: 1 addition & 1 deletion tokio-util/tests/panic.rs
@@ -1,5 +1,5 @@
#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]
#![cfg(all(feature = "full", not(target_os = "wasi")))] // Wasi doesn't support panic recovery

use parking_lot::{const_mutex, Mutex};
use std::error::Error;
Expand Down
1 change: 1 addition & 0 deletions tokio-util/tests/spawn_pinned.rs
@@ -1,4 +1,5 @@
#![warn(rust_2018_idioms)]
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support threads

use std::rc::Rc;
use std::sync::Arc;
Expand Down
2 changes: 2 additions & 0 deletions tokio-util/tests/time_delay_queue.rs
Expand Up @@ -778,6 +778,7 @@ async fn compact_change_deadline() {
assert!(entry.is_none());
}

#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")]
#[tokio::test(start_paused = true)]
async fn remove_after_compact() {
let now = Instant::now();
Comment on lines +781 to 784
Copy link
Contributor

Choose a reason for hiding this comment

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

That's weird. Do we know why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because it uses std::panic::catch_unwind(), which isn't supported by Wasi.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay. In that case, ignoring this test is fine, but the ignore label should explain that this is due to catching panics.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Many of the tests disabled for Wasi just have #[cfg(not(target_os = 'wasi'))]. Should the disabled tests have that, or have an ignore message? Just thinking about consistency.

Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer using ignore, but that doesn't always work because it might call methods that are missing. In that case just use #[cfg(not(...))]. I would prefer a comment explaining why in both cases.

Expand All @@ -794,6 +795,7 @@ async fn remove_after_compact() {
assert!(panic.is_err());
}

#[cfg_attr(target_os = "wasi", ignore = "FIXME: Does not seem to work with WASI")]
#[tokio::test(start_paused = true)]
async fn remove_after_compact_poll() {
let now = Instant::now();
Expand Down
1 change: 1 addition & 0 deletions tokio-util/tests/udp.rs
@@ -1,4 +1,5 @@
#![warn(rust_2018_idioms)]
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support UDP

use tokio::net::UdpSocket;
use tokio_stream::StreamExt;
Expand Down
13 changes: 8 additions & 5 deletions tokio/Cargo.toml
Expand Up @@ -51,7 +51,6 @@ net = [
"mio/os-poll",
"mio/os-ext",
"mio/net",
"socket2",
"winapi/fileapi",
"winapi/handleapi",
"winapi/namedpipeapi",
Expand Down Expand Up @@ -112,11 +111,13 @@ pin-project-lite = "0.2.0"
bytes = { version = "1.0.0", optional = true }
once_cell = { version = "1.5.2", optional = true }
memchr = { version = "2.2", optional = true }
mio = { version = "0.8.1", optional = true }
socket2 = { version = "0.4.4", optional = true, features = [ "all" ] }
mio = { version = "0.8.4", optional = true }
num_cpus = { version = "1.8.0", optional = true }
parking_lot = { version = "0.12.0", optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
socket2 = { version = "0.4.4", features = [ "all" ] }

# Currently unstable. The API exposed by these features may be broken at any time.
# Requires `--cfg tokio_unstable` to enable.
[target.'cfg(tokio_unstable)'.dependencies]
Expand Down Expand Up @@ -149,10 +150,12 @@ async-stream = "0.3"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
proptest = "1"
rand = "0.8.0"
socket2 = "0.4"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
rand = "0.8.0"

[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies]
wasm-bindgen-test = "0.3.0"

[target.'cfg(target_os = "freebsd")'.dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion tokio/src/coop.rs
Expand Up @@ -207,7 +207,7 @@ cfg_coop! {
mod test {
use super::*;

#[cfg(target_arch = "wasm32")]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test as test;

fn get() -> Budget {
Expand Down
10 changes: 10 additions & 0 deletions tokio/src/io/driver/mod.rs
Expand Up @@ -72,6 +72,8 @@ pub(super) struct Inner {
io_dispatch: RwLock<IoDispatcher>,

/// Used to wake up the reactor from a call to `turn`.
/// Not supported on Wasi due to lack of threading support.
#[cfg(not(target_os = "wasi"))]
waker: mio::Waker,

metrics: IoDriverMetrics,
Expand Down Expand Up @@ -115,6 +117,7 @@ impl Driver {
/// creation.
pub(crate) fn new() -> io::Result<Driver> {
let poll = mio::Poll::new()?;
#[cfg(not(target_os = "wasi"))]
let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?;
let registry = poll.registry().try_clone()?;

Expand All @@ -129,6 +132,7 @@ impl Driver {
inner: Arc::new(Inner {
registry,
io_dispatch: RwLock::new(IoDispatcher::new(allocator)),
#[cfg(not(target_os = "wasi"))]
waker,
metrics: IoDriverMetrics::default(),
}),
Expand Down Expand Up @@ -164,6 +168,11 @@ impl Driver {
match self.poll.poll(&mut events, max_wait) {
Ok(_) => {}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
#[cfg(target_os = "wasi")]
Err(e) if e.kind() == io::ErrorKind::InvalidInput => {
// In case of wasm32_wasi this error happens, when trying to poll without subscriptions
// just return from the park, as there would be nothing, which wakes us up.
}
Err(e) => return Err(e),
}

Expand Down Expand Up @@ -300,6 +309,7 @@ impl Handle {
/// blocked in `turn`, then the next call to `turn` will not block and
/// return immediately.
fn wakeup(&self) {
#[cfg(not(target_os = "wasi"))]
self.inner.waker.wake().expect("failed to wake I/O driver");
}
}
Expand Down
2 changes: 2 additions & 0 deletions tokio/src/io/mod.rs
Expand Up @@ -211,9 +211,11 @@ cfg_io_driver_impl! {
pub use driver::{Interest, Ready};
}

#[cfg_attr(target_os = "wasi", allow(unused_imports))]
mod poll_evented;

#[cfg(not(loom))]
#[cfg_attr(target_os = "wasi", allow(unused_imports))]
pub(crate) use poll_evented::PollEvented;
}

Expand Down
14 changes: 14 additions & 0 deletions tokio/src/lib.rs
Expand Up @@ -393,6 +393,20 @@ compile_error! {
"Tokio requires the platform pointer width to be 32, 64, or 128 bits"
}

#[cfg(all(
not(tokio_unstable),
target_arch = "wasm32",
any(
feature = "fs",
feature = "io-std",
feature = "net",
feature = "process",
feature = "rt-multi-thread",
feature = "signal"
)
))]
compile_error!("Only features sync,macros,io-util,rt are supported on wasm.");

// Includes re-exports used by macros.
//
// This module is not intended to be part of the public API. In general, any
Expand Down
29 changes: 26 additions & 3 deletions tokio/src/macros/cfg.rs
Expand Up @@ -61,6 +61,7 @@ macro_rules! cfg_fs {
($($item:item)*) => {
$(
#[cfg(feature = "fs")]
#[cfg(not(target_os = "wasi"))]
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
$item
)*
Expand Down Expand Up @@ -247,6 +248,7 @@ macro_rules! cfg_process {
#[cfg(feature = "process")]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
#[cfg(not(loom))]
#[cfg(not(target_os = "wasi"))]
$item
)*
}
Expand Down Expand Up @@ -275,6 +277,7 @@ macro_rules! cfg_signal {
#[cfg(feature = "signal")]
#[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
#[cfg(not(loom))]
#[cfg(not(target_os = "wasi"))]
$item
)*
}
Expand Down Expand Up @@ -334,7 +337,7 @@ macro_rules! cfg_not_rt {
macro_rules! cfg_rt_multi_thread {
($($item:item)*) => {
$(
#[cfg(feature = "rt-multi-thread")]
#[cfg(all(feature = "rt-multi-thread", not(target_os = "wasi")))]
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
$item
)*
Expand Down Expand Up @@ -451,7 +454,8 @@ macro_rules! cfg_has_atomic_u64 {
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "riscv32"
target_arch = "riscv32",
target_arch = "wasm32"
)))]
$item
)*
Expand All @@ -465,9 +469,28 @@ macro_rules! cfg_not_has_atomic_u64 {
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "riscv32"
target_arch = "riscv32",
target_arch = "wasm32"
))]
$item
)*
}
}

macro_rules! cfg_not_wasi {
($($item:item)*) => {
$(
#[cfg(not(target_os = "wasi"))]
$item
)*
}
}

macro_rules! cfg_is_wasm_not_wasi {
($($item:item)*) => {
$(
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
$item
)*
}
}
14 changes: 9 additions & 5 deletions tokio/src/net/mod.rs
Expand Up @@ -23,8 +23,10 @@
//! [`UnixDatagram`]: UnixDatagram

mod addr;
#[cfg(feature = "net")]
pub(crate) use addr::to_socket_addrs;
cfg_not_wasi! {
#[cfg(feature = "net")]
pub(crate) use addr::to_socket_addrs;
}
pub use addr::ToSocketAddrs;

cfg_net! {
Expand All @@ -33,11 +35,13 @@ cfg_net! {

pub mod tcp;
pub use tcp::listener::TcpListener;
pub use tcp::socket::TcpSocket;
pub use tcp::stream::TcpStream;
cfg_not_wasi! {
pub use tcp::socket::TcpSocket;

mod udp;
pub use udp::UdpSocket;
mod udp;
pub use udp::UdpSocket;
}
}

cfg_net_unix! {
Expand Down