Skip to content

Commit 2a08ec8

Browse files
committed
add wasm32-wasip2 networking support
Motivation This adds networking support for the `wasm32-wasip2` target platform, which includes more extensive support for sockets than `wasm32-wasip1`. Solution 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 `wasm32-wasip2` 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. Future Work 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 RUSTFLAGS="--cfg tokio_unstable"`. Once we have a solid maintenance plan, we can remove that requirement.
1 parent 03fe44c commit 2a08ec8

25 files changed

+322
-83
lines changed

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,32 @@ jobs:
11121112
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
11131113
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
11141114

1115+
wasm32-wasip2:
1116+
name: test tokio for wasm32-wasip2
1117+
runs-on: ubuntu-latest
1118+
timeout-minutes: 10
1119+
steps:
1120+
- uses: actions/checkout@v4
1121+
- name: Install Rust ${{ env.rust_stable }}
1122+
uses: dtolnay/rust-toolchain@stable
1123+
with:
1124+
toolchain: ${{ env.rust_stable }}
1125+
targets: wasm32-wasip2
1126+
1127+
- name: Install cargo-nextest, wasmtime
1128+
uses: taiki-e/install-action@v2
1129+
with:
1130+
tool: cargo-nextest,wasmtime-cli
1131+
1132+
- uses: Swatinem/rust-cache@v2
1133+
1134+
- name: test tokio --target wasm32-wasip2
1135+
run: cargo nextest run --target wasm32-wasip2 --features net,macros,rt,io-util
1136+
working-directory: tokio
1137+
env:
1138+
RUSTFLAGS: --cfg tokio_unstable
1139+
CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime run -Sinherit-network
1140+
11151141
check-external-types:
11161142
name: check-external-types (${{ matrix.os }})
11171143
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"

spellcheck.dic

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
312
1+
313
22
&
33
+
44
<
@@ -309,5 +309,6 @@ wakers
309309
Wakers
310310
wakeup
311311
wakeups
312+
WASI
312313
workstealing
313314
ZST

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/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: 5 additions & 5 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.
@@ -292,15 +292,15 @@ impl TcpListener {
292292

293293
#[cfg(target_os = "wasi")]
294294
{
295-
use std::os::wasi::io::{FromRawFd, IntoRawFd};
295+
use std::os::fd::{FromRawFd, IntoRawFd};
296296
self.io
297297
.into_inner()
298298
.map(|io| io.into_raw_fd())
299299
.map(|raw_fd| unsafe { std::net::TcpListener::from_raw_fd(raw_fd) })
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 })
@@ -427,7 +427,7 @@ cfg_unstable! {
427427
#[cfg(target_os = "wasi")]
428428
mod sys {
429429
use super::TcpListener;
430-
use std::os::wasi::prelude::*;
430+
use std::os::fd::{RawFd, BorrowedFd, AsRawFd, AsFd};
431431

432432
impl AsRawFd for TcpListener {
433433
fn as_raw_fd(&self) -> RawFd {

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: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use std::fmt;
44
use std::io;
55
use std::net::SocketAddr;
66

7-
#[cfg(unix)]
8-
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
7+
#[cfg(not(windows))]
8+
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
99
use std::time::Duration;
1010

1111
cfg_windows! {
@@ -170,7 +170,8 @@ impl TcpSocket {
170170
target_os = "illumos",
171171
target_os = "linux",
172172
target_os = "netbsd",
173-
target_os = "openbsd"
173+
target_os = "openbsd",
174+
target_os = "wasi",
174175
))]
175176
let ty = ty.nonblocking();
176177
let inner = socket2::Socket::new(domain, ty, Some(socket2::Protocol::TCP))?;
@@ -182,7 +183,8 @@ impl TcpSocket {
182183
target_os = "illumos",
183184
target_os = "linux",
184185
target_os = "netbsd",
185-
target_os = "openbsd"
186+
target_os = "openbsd",
187+
target_os = "wasi",
186188
)))]
187189
inner.set_nonblocking(true)?;
188190
Ok(TcpSocket { inner })
@@ -597,7 +599,8 @@ impl TcpSocket {
597599
target_os = "redox",
598600
target_os = "solaris",
599601
target_os = "illumos",
600-
target_os = "haiku"
602+
target_os = "haiku",
603+
target_os = "wasi",
601604
)))]
602605
#[cfg_attr(
603606
docsrs,
@@ -606,7 +609,8 @@ impl TcpSocket {
606609
target_os = "redox",
607610
target_os = "solaris",
608611
target_os = "illumos",
609-
target_os = "haiku"
612+
target_os = "haiku",
613+
target_os = "wasi",
610614
))))
611615
)]
612616
pub fn tos_v4(&self) -> io::Result<u32> {
@@ -625,7 +629,8 @@ impl TcpSocket {
625629
target_os = "redox",
626630
target_os = "solaris",
627631
target_os = "illumos",
628-
target_os = "haiku"
632+
target_os = "haiku",
633+
target_os = "wasi",
629634
)))]
630635
#[cfg_attr(
631636
docsrs,
@@ -634,7 +639,8 @@ impl TcpSocket {
634639
target_os = "redox",
635640
target_os = "solaris",
636641
target_os = "illumos",
637-
target_os = "haiku"
642+
target_os = "haiku",
643+
target_os = "wasi",
638644
))))
639645
)]
640646
pub fn tos(&self) -> io::Result<u32> {
@@ -657,7 +663,8 @@ impl TcpSocket {
657663
target_os = "redox",
658664
target_os = "solaris",
659665
target_os = "illumos",
660-
target_os = "haiku"
666+
target_os = "haiku",
667+
target_os = "wasi",
661668
)))]
662669
#[cfg_attr(
663670
docsrs,
@@ -666,7 +673,8 @@ impl TcpSocket {
666673
target_os = "redox",
667674
target_os = "solaris",
668675
target_os = "illumos",
669-
target_os = "haiku"
676+
target_os = "haiku",
677+
target_os = "wasi",
670678
))))
671679
)]
672680
pub fn set_tos_v4(&self, tos: u32) -> io::Result<()> {
@@ -685,7 +693,8 @@ impl TcpSocket {
685693
target_os = "redox",
686694
target_os = "solaris",
687695
target_os = "illumos",
688-
target_os = "haiku"
696+
target_os = "haiku",
697+
target_os = "wasi",
689698
)))]
690699
#[cfg_attr(
691700
docsrs,
@@ -694,7 +703,8 @@ impl TcpSocket {
694703
target_os = "redox",
695704
target_os = "solaris",
696705
target_os = "illumos",
697-
target_os = "haiku"
706+
target_os = "haiku",
707+
target_os = "wasi",
698708
))))
699709
)]
700710
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
@@ -826,7 +836,7 @@ impl TcpSocket {
826836
/// ```
827837
pub async fn connect(self, addr: SocketAddr) -> io::Result<TcpStream> {
828838
if let Err(err) = self.inner.connect(&addr.into()) {
829-
#[cfg(unix)]
839+
#[cfg(not(windows))]
830840
if err.raw_os_error() != Some(libc::EINPROGRESS) {
831841
return Err(err);
832842
}
@@ -835,9 +845,9 @@ impl TcpSocket {
835845
return Err(err);
836846
}
837847
}
838-
#[cfg(unix)]
848+
#[cfg(not(windows))]
839849
let mio = {
840-
use std::os::unix::io::{FromRawFd, IntoRawFd};
850+
use std::os::fd::{FromRawFd, IntoRawFd};
841851

842852
let raw_fd = self.inner.into_raw_fd();
843853
unsafe { mio::net::TcpStream::from_raw_fd(raw_fd) }
@@ -891,9 +901,9 @@ impl TcpSocket {
891901
/// ```
892902
pub fn listen(self, backlog: u32) -> io::Result<TcpListener> {
893903
self.inner.listen(backlog as i32)?;
894-
#[cfg(unix)]
904+
#[cfg(not(windows))]
895905
let mio = {
896-
use std::os::unix::io::{FromRawFd, IntoRawFd};
906+
use std::os::fd::{FromRawFd, IntoRawFd};
897907

898908
let raw_fd = self.inner.into_raw_fd();
899909
unsafe { mio::net::TcpListener::from_raw_fd(raw_fd) }
@@ -945,9 +955,9 @@ impl TcpSocket {
945955
/// }
946956
/// ```
947957
pub fn from_std_stream(std_stream: std::net::TcpStream) -> TcpSocket {
948-
#[cfg(unix)]
958+
#[cfg(not(windows))]
949959
{
950-
use std::os::unix::io::{FromRawFd, IntoRawFd};
960+
use std::os::fd::{FromRawFd, IntoRawFd};
951961

952962
let raw_fd = std_stream.into_raw_fd();
953963
unsafe { TcpSocket::from_raw_fd(raw_fd) }
@@ -981,8 +991,8 @@ impl fmt::Debug for TcpSocket {
981991

982992
// These trait implementations can't be build on Windows, so we completely
983993
// ignore them, even when building documentation.
984-
#[cfg(unix)]
985-
cfg_unix! {
994+
#[cfg(any(unix, target_os = "wasi"))]
995+
cfg_unix_or_wasi! {
986996
impl AsRawFd for TcpSocket {
987997
fn as_raw_fd(&self) -> RawFd {
988998
self.inner.as_raw_fd()

tokio/src/net/tcp/stream.rs

Lines changed: 7 additions & 4 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
@@ -272,7 +275,7 @@ impl TcpStream {
272275

273276
#[cfg(target_os = "wasi")]
274277
{
275-
use std::os::wasi::io::{FromRawFd, IntoRawFd};
278+
use std::os::fd::{FromRawFd, IntoRawFd};
276279
self.io
277280
.into_inner()
278281
.map(|io| io.into_raw_fd())
@@ -1564,7 +1567,7 @@ cfg_windows! {
15641567
#[cfg(all(tokio_unstable, target_os = "wasi"))]
15651568
mod sys {
15661569
use super::TcpStream;
1567-
use std::os::wasi::prelude::*;
1570+
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd};
15681571

15691572
impl AsRawFd for TcpStream {
15701573
fn as_raw_fd(&self) -> RawFd {

0 commit comments

Comments
 (0)