Skip to content

Commit 14a69d6

Browse files
committed
add wasm32-wasip2 networking support
This adds networking support for the `wasm32-wasip2` target platform, which includes more extensive support for sockets than `wasm32-wasip1`. The bulk of the changes are in tokio-rs/mio#1931. This patch mainly tweaks a few `cfg` directives to indicate `wasm32-wasip2`'s additional capabilities. Note that this is a draft PR until until tokio-rs/mio#1931 and rust-lang/socket2#639 have been include in stable releases of their respective projects. Also note that I've added a `test-wasi` target to CI and triaged each test which was previously disabled for WASI into one of three categories: - Disabled on both WASIp1 and p2 due to not-yet-supported features such as multithreading - Disabled on p1 but enabled on p2 - Disabled on p1 and _temporarily_ disabled on p2 due to `wasi-libc` bugfixes which have been merged but not yet included in a Rust release. I'll open an issue to re-enable them when the fixes land in Rust. In the future, we could consider adding support for `tokio::net::lookup_host`. WASIp2 natively supports asynchronous DNS lookups and is single threaded, whereas Tokio currently assumes DNS lookups are blocking and require multithreading to emulate async lookups. A WASIp2-specific implementation could do the lookup directly without multithreading. WASIp2 also supports single-threaded, asynchronous file I/O, timers, etc. We could either support those directly or wait for WASIp3's multithreading support, in which case most of `tokio::fs` (as well as `tokio::net::lookup_host`, etc.) _should_ work unchanged via `wasi-libc` and worker threads. Currently, building for WASIp2 requires both `nightly` Rust and `RUSTFLAGS="--cfg tokio_unstable"`. The `nightly` requirement could be removed once either the `feature(wasip2)` is stabilized or `rustix` is updated not to use it. It would be nice to remove the `--cfg tokio_unstable` requirement as well, assuming we have a solid maintainance plan.
1 parent 03fe44c commit 14a69d6

25 files changed

+355
-66
lines changed

.github/workflows/ci.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ env:
3232
# This excludes unstable features like io_uring,
3333
# which require '--cfg tokio_unstable'.
3434
TOKIO_STABLE_FEATURES: "full,test-util"
35+
WASMTIME_VERSION: 41.0.3
3536

3637
defaults:
3738
run:
@@ -334,6 +335,34 @@ jobs:
334335
# the unstable cfg to RustDoc
335336
RUSTDOCFLAGS: --cfg tokio_unstable
336337

338+
test-wasi:
339+
runs-on: ubuntu-latest
340+
timeout-minutes: 10
341+
steps:
342+
- uses: actions/checkout@v4
343+
- uses: dtolnay/rust-toolchain@nightly
344+
- name: Install cargo-nextest
345+
uses: taiki-e/install-action@v2
346+
with:
347+
tool: cargo-nextest
348+
349+
- name: Setup `wasmtime`
350+
run: |
351+
curl -OL https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/wasmtime-v${WASMTIME_VERSION}-x86_64-linux.tar.xz
352+
tar xf wasmtime-v${WASMTIME_VERSION}-x86_64-linux.tar.xz
353+
echo CARGO_TARGET_WASM32_WASIP2_RUNNER="$(pwd)/wasmtime-v${WASMTIME_VERSION}-x86_64-linux/wasmtime run -Sinherit-network" >> $GITHUB_ENV
354+
355+
- name: Install target
356+
run: rustup target add wasm32-wasip2
357+
358+
- name: test tokio --target wasm32-wasip2
359+
# We currently need `+nightly` here due to some dependencies
360+
# (e.g. `rustix`) which require `#![feature(wasip2)]`:
361+
run: cargo +nightly nextest run --target wasm32-wasip2 --features net,macros,rt,io-util
362+
working-directory: tokio
363+
env:
364+
RUSTFLAGS: --cfg tokio_unstable
365+
337366
check-unstable-mt-counters:
338367
name: check tokio full --internal-mt-counters
339368
needs: basics

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ members = [
1717

1818
[patch.crates-io]
1919
tokio = { path = "tokio" }
20+
# TODO: Remove these once new releases are available:
21+
mio = { git = "https://github.com/tokio-rs/mio", rev = "9a387a507a32f3bc29bd3b9c0b59edaa95ed7bd0" }
22+
socket2 = { git = "https://github.com/rust-lang/socket2", rev = "e47157c29446856d87370b49194ce2f998337bfa" }
2023

2124
[workspace.metadata.spellcheck]
2225
config = "spellcheck.toml"

tokio/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ bytes = { version = "1.2.1", optional = true }
9999
mio = { version = "1.0.1", optional = true, default-features = false }
100100
parking_lot = { version = "0.12.0", optional = true }
101101

102-
[target.'cfg(not(target_family = "wasm"))'.dependencies]
102+
[target.'cfg(any(not(target_family = "wasm"), all(target_os = "wasi", not(target_env = "p1"))))'.dependencies]
103103
socket2 = { version = "0.6.0", optional = true, features = ["all"] }
104104

105105
# Currently unstable. The API exposed by these features may be broken at any time.
@@ -120,6 +120,9 @@ backtrace = { version = "0.3.58", optional = true }
120120
libc = { version = "0.2.168", optional = true }
121121
signal-hook-registry = { version = "1.1.1", optional = true }
122122

123+
[target.'cfg(target_os = "wasi")'.dependencies]
124+
libc = { version = "0.2.168", optional = true }
125+
123126
[target.'cfg(unix)'.dev-dependencies]
124127
libc = { version = "0.2.168" }
125128
nix = { version = "0.29.0", default-features = false, features = ["aio", "fs", "socket"] }

tokio/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#![cfg_attr(docsrs, allow(unused_attributes))]
2020
#![cfg_attr(loom, allow(dead_code, unreachable_pub))]
2121
#![cfg_attr(windows, allow(rustdoc::broken_intra_doc_links))]
22+
// As of this writing, `rustix` requires `feature(wasip2)` when built for
23+
// `wasm32-wasip2`:
24+
#![cfg_attr(all(target_os = "wasi", target_env = "p2"), feature(wasip2))]
2225

2326
//! A runtime for writing reliable network applications without compromising speed.
2427
//!

tokio/src/macros/cfg.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ macro_rules! cfg_unix {
5858
}
5959
}
6060

61+
/// Enables Unix-specific code, including WASI.
62+
/// Use this macro instead of `cfg(any(unix, target_os = "wasi"))` to generate docs properly.
63+
macro_rules! cfg_unix_or_wasi {
64+
($($item:item)*) => {
65+
$(
66+
#[cfg(any(all(doc, docsrs), unix, target_os = "wasi"))]
67+
#[cfg_attr(docsrs, doc(cfg(any(unix, target_os = "wasi"))))]
68+
$item
69+
)*
70+
}
71+
}
72+
6173
/// Enables unstable Windows-specific code.
6274
/// Use this macro instead of `cfg(windows)` to generate docs properly.
6375
macro_rules! cfg_unstable_windows {
@@ -674,6 +686,15 @@ macro_rules! cfg_not_wasi {
674686
}
675687
}
676688

689+
macro_rules! cfg_not_wasip1 {
690+
($($item:item)*) => {
691+
$(
692+
#[cfg(not(all(target_os = "wasi", target_env = "p1")))]
693+
$item
694+
)*
695+
}
696+
}
697+
677698
macro_rules! cfg_is_wasm_not_wasi {
678699
($($item:item)*) => {
679700
$(

tokio/src/net/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
//! [`AsyncFd`]: crate::io::unix::AsyncFd
3030
3131
mod addr;
32-
cfg_not_wasi! {
32+
cfg_not_wasip1! {
3333
#[cfg(feature = "net")]
3434
pub(crate) use addr::to_socket_addrs;
3535
}
@@ -42,7 +42,7 @@ cfg_net! {
4242
pub mod tcp;
4343
pub use tcp::listener::TcpListener;
4444
pub use tcp::stream::TcpStream;
45-
cfg_not_wasi! {
45+
cfg_not_wasip1! {
4646
pub use tcp::socket::TcpSocket;
4747

4848
mod udp;

tokio/src/net/tcp/listener.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::io::{Interest, PollEvented};
22
use crate::net::tcp::TcpStream;
33
use crate::util::check_socket_for_blocking;
44

5-
cfg_not_wasi! {
5+
cfg_not_wasip1! {
66
use crate::net::{to_socket_addrs, ToSocketAddrs};
77
}
88

@@ -60,7 +60,7 @@ cfg_net! {
6060
}
6161

6262
impl TcpListener {
63-
cfg_not_wasi! {
63+
cfg_not_wasip1! {
6464
/// Creates a new `TcpListener`, which will be bound to the specified address.
6565
///
6666
/// The returned listener is ready for accepting connections.
@@ -300,7 +300,7 @@ impl TcpListener {
300300
}
301301
}
302302

303-
cfg_not_wasi! {
303+
cfg_not_wasip1! {
304304
pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result<TcpListener> {
305305
let io = PollEvented::new(listener)?;
306306
Ok(TcpListener { io })

tokio/src/net/tcp/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
pub(crate) mod listener;
44

5-
cfg_not_wasi! {
5+
cfg_not_wasip1! {
66
pub(crate) mod socket;
77
}
88

tokio/src/net/tcp/socket.rs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::fmt;
44
use std::io;
55
use std::net::SocketAddr;
66

7+
#[cfg(not(any(unix, windows)))]
8+
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
79
#[cfg(unix)]
810
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
911
use std::time::Duration;
@@ -170,7 +172,8 @@ impl TcpSocket {
170172
target_os = "illumos",
171173
target_os = "linux",
172174
target_os = "netbsd",
173-
target_os = "openbsd"
175+
target_os = "openbsd",
176+
target_os = "wasi",
174177
))]
175178
let ty = ty.nonblocking();
176179
let inner = socket2::Socket::new(domain, ty, Some(socket2::Protocol::TCP))?;
@@ -182,7 +185,8 @@ impl TcpSocket {
182185
target_os = "illumos",
183186
target_os = "linux",
184187
target_os = "netbsd",
185-
target_os = "openbsd"
188+
target_os = "openbsd",
189+
target_os = "wasi",
186190
)))]
187191
inner.set_nonblocking(true)?;
188192
Ok(TcpSocket { inner })
@@ -597,7 +601,8 @@ impl TcpSocket {
597601
target_os = "redox",
598602
target_os = "solaris",
599603
target_os = "illumos",
600-
target_os = "haiku"
604+
target_os = "haiku",
605+
target_os = "wasi",
601606
)))]
602607
#[cfg_attr(
603608
docsrs,
@@ -606,7 +611,8 @@ impl TcpSocket {
606611
target_os = "redox",
607612
target_os = "solaris",
608613
target_os = "illumos",
609-
target_os = "haiku"
614+
target_os = "haiku",
615+
target_os = "wasi",
610616
))))
611617
)]
612618
pub fn tos_v4(&self) -> io::Result<u32> {
@@ -625,7 +631,8 @@ impl TcpSocket {
625631
target_os = "redox",
626632
target_os = "solaris",
627633
target_os = "illumos",
628-
target_os = "haiku"
634+
target_os = "haiku",
635+
target_os = "wasi",
629636
)))]
630637
#[cfg_attr(
631638
docsrs,
@@ -634,7 +641,8 @@ impl TcpSocket {
634641
target_os = "redox",
635642
target_os = "solaris",
636643
target_os = "illumos",
637-
target_os = "haiku"
644+
target_os = "haiku",
645+
target_os = "wasi",
638646
))))
639647
)]
640648
pub fn tos(&self) -> io::Result<u32> {
@@ -657,7 +665,8 @@ impl TcpSocket {
657665
target_os = "redox",
658666
target_os = "solaris",
659667
target_os = "illumos",
660-
target_os = "haiku"
668+
target_os = "haiku",
669+
target_os = "wasi",
661670
)))]
662671
#[cfg_attr(
663672
docsrs,
@@ -666,7 +675,8 @@ impl TcpSocket {
666675
target_os = "redox",
667676
target_os = "solaris",
668677
target_os = "illumos",
669-
target_os = "haiku"
678+
target_os = "haiku",
679+
target_os = "wasi",
670680
))))
671681
)]
672682
pub fn set_tos_v4(&self, tos: u32) -> io::Result<()> {
@@ -685,7 +695,8 @@ impl TcpSocket {
685695
target_os = "redox",
686696
target_os = "solaris",
687697
target_os = "illumos",
688-
target_os = "haiku"
698+
target_os = "haiku",
699+
target_os = "wasi",
689700
)))]
690701
#[cfg_attr(
691702
docsrs,
@@ -694,7 +705,8 @@ impl TcpSocket {
694705
target_os = "redox",
695706
target_os = "solaris",
696707
target_os = "illumos",
697-
target_os = "haiku"
708+
target_os = "haiku",
709+
target_os = "wasi",
698710
))))
699711
)]
700712
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
@@ -826,7 +838,7 @@ impl TcpSocket {
826838
/// ```
827839
pub async fn connect(self, addr: SocketAddr) -> io::Result<TcpStream> {
828840
if let Err(err) = self.inner.connect(&addr.into()) {
829-
#[cfg(unix)]
841+
#[cfg(any(unix, target_os = "wasi"))]
830842
if err.raw_os_error() != Some(libc::EINPROGRESS) {
831843
return Err(err);
832844
}
@@ -851,6 +863,14 @@ impl TcpSocket {
851863
unsafe { mio::net::TcpStream::from_raw_socket(raw_socket) }
852864
};
853865

866+
#[cfg(not(any(unix, windows)))]
867+
let mio = {
868+
use std::os::fd::{FromRawFd, IntoRawFd};
869+
870+
let raw_fd = self.inner.into_raw_fd();
871+
unsafe { mio::net::TcpStream::from_raw_fd(raw_fd) }
872+
};
873+
854874
TcpStream::connect_mio(mio).await
855875
}
856876

@@ -907,6 +927,14 @@ impl TcpSocket {
907927
unsafe { mio::net::TcpListener::from_raw_socket(raw_socket) }
908928
};
909929

930+
#[cfg(not(any(unix, windows)))]
931+
let mio = {
932+
use std::os::fd::{FromRawFd, IntoRawFd};
933+
934+
let raw_fd = self.inner.into_raw_fd();
935+
unsafe { mio::net::TcpListener::from_raw_fd(raw_fd) }
936+
};
937+
910938
TcpListener::new(mio)
911939
}
912940

@@ -960,6 +988,14 @@ impl TcpSocket {
960988
let raw_socket = std_stream.into_raw_socket();
961989
unsafe { TcpSocket::from_raw_socket(raw_socket) }
962990
}
991+
992+
#[cfg(not(any(unix, windows)))]
993+
{
994+
use std::os::fd::{FromRawFd, IntoRawFd};
995+
996+
let raw_fd = std_stream.into_raw_fd();
997+
unsafe { TcpSocket::from_raw_fd(raw_fd) }
998+
}
963999
}
9641000
}
9651001

@@ -981,8 +1017,8 @@ impl fmt::Debug for TcpSocket {
9811017

9821018
// These trait implementations can't be build on Windows, so we completely
9831019
// ignore them, even when building documentation.
984-
#[cfg(unix)]
985-
cfg_unix! {
1020+
#[cfg(any(unix, target_os = "wasi"))]
1021+
cfg_unix_or_wasi! {
9861022
impl AsRawFd for TcpSocket {
9871023
fn as_raw_fd(&self) -> RawFd {
9881024
self.inner.as_raw_fd()

tokio/src/net/tcp/stream.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
cfg_not_wasi! {
2+
use std::time::Duration;
3+
}
4+
5+
cfg_not_wasip1! {
26
use crate::net::{to_socket_addrs, ToSocketAddrs};
37
use std::future::poll_fn;
4-
use std::time::Duration;
58
}
69

710
use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
@@ -73,7 +76,7 @@ cfg_net! {
7376
}
7477

7578
impl TcpStream {
76-
cfg_not_wasi! {
79+
cfg_not_wasip1! {
7780
/// Opens a TCP connection to a remote host.
7881
///
7982
/// `addr` is an address of the remote host. Anything which implements the

0 commit comments

Comments
 (0)