Skip to content

Commit

Permalink
Add polling and networking for target_os = "wasi"
Browse files Browse the repository at this point in the history
Based on tokio-rs#1395

And with
* bytecodealliance/wasmtime#3711
* rust-lang/rust#93158
merged, mio can have limited support for networking for the
`wasm32-wasi` target.

Co-authored-by: Thomas de Zeeuw <thomasdezeeuw@gmail.com>
Signed-off-by: Harald Hoyer <harald@profian.com>
  • Loading branch information
haraldh and Thomasdezeeuw committed Mar 8, 2022
1 parent 9f8ed3d commit 2628793
Show file tree
Hide file tree
Showing 13 changed files with 503 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ miow = "0.3.6"
winapi = { version = "0.3", features = ["winsock2", "mswsock"] }
ntapi = "0.3"

[target.'cfg(target_os = "wasi")'.dependencies]
wasi = "0.11.0"
libc = "0.2.86"

[dev-dependencies]
env_logger = { version = "0.8.4", default-features = false }
rand = "0.8"
Expand Down
40 changes: 40 additions & 0 deletions src/io_source.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::ops::{Deref, DerefMut};
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
#[cfg(target_os = "wasi")]
use std::os::wasi::io::AsRawFd;
#[cfg(windows)]
use std::os::windows::io::AsRawSocket;
#[cfg(debug_assertions)]
Expand Down Expand Up @@ -200,6 +202,44 @@ where
}
}

#[cfg(target_os = "wasi")]
impl<T> event::Source for IoSource<T>
where
T: AsRawFd,
{
fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.associate(registry)?;
registry
.selector()
.register(self.inner.as_raw_fd() as _, token, interests)
}

fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.check_association(registry)?;
registry
.selector()
.reregister(self.inner.as_raw_fd() as _, token, interests)
}

fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.remove_association(registry)?;
registry.selector().deregister(self.inner.as_raw_fd() as _)
}
}

impl<T> fmt::Debug for IoSource<T>
where
T: fmt::Debug,
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ mod interest;
mod poll;
mod sys;
mod token;
#[cfg(not(target_os = "wasi"))]
mod waker;

pub mod event;
Expand All @@ -65,6 +66,7 @@ pub use event::Events;
pub use interest::Interest;
pub use poll::{Poll, Registry};
pub use token::Token;
#[cfg(not(target_os = "wasi"))]
pub use waker::Waker;

#[cfg(all(unix, feature = "os-ext"))]
Expand Down
2 changes: 2 additions & 0 deletions src/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
mod tcp;
pub use self::tcp::{TcpListener, TcpStream};

#[cfg(not(target_os = "wasi"))]
mod udp;
#[cfg(not(target_os = "wasi"))]
pub use self::udp::UdpSocket;

#[cfg(unix)]
Expand Down
31 changes: 31 additions & 0 deletions src/net/tcp/listener.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::net::{self, SocketAddr};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::{fmt, io};
Expand All @@ -9,6 +11,7 @@ use crate::io_source::IoSource;
use crate::net::TcpStream;
#[cfg(unix)]
use crate::sys::tcp::set_reuseaddr;
#[cfg(not(target_os = "wasi"))]
use crate::sys::tcp::{bind, listen, new_for_addr};
use crate::{event, sys, Interest, Registry, Token};

Expand Down Expand Up @@ -52,6 +55,7 @@ impl TcpListener {
/// 2. Set the `SO_REUSEADDR` option on the socket on Unix.
/// 3. Bind the socket to the specified address.
/// 4. Calls `listen` on the socket to prepare it to receive new connections.
#[cfg(not(target_os = "wasi"))]
pub fn bind(addr: SocketAddr) -> io::Result<TcpListener> {
let socket = new_for_addr(addr)?;
#[cfg(unix)]
Expand Down Expand Up @@ -215,3 +219,30 @@ impl FromRawSocket for TcpListener {
TcpListener::from_std(FromRawSocket::from_raw_socket(socket))
}
}

#[cfg(target_os = "wasi")]
impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}

#[cfg(target_os = "wasi")]
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}

#[cfg(target_os = "wasi")]
impl FromRawFd for TcpListener {
/// Converts a `RawFd` to a `TcpListener`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
TcpListener::from_std(FromRawFd::from_raw_fd(fd))
}
}
31 changes: 31 additions & 0 deletions src/net/tcp/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::net::{self, Shutdown, SocketAddr};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};

use crate::io_source::IoSource;
#[cfg(not(target_os = "wasi"))]
use crate::sys::tcp::{connect, new_for_addr};
use crate::{event, Interest, Registry, Token};

Expand Down Expand Up @@ -73,6 +76,7 @@ impl TcpStream {
/// 5. Now the stream can be used.
///
/// [read interest]: Interest::READABLE
#[cfg(not(target_os = "wasi"))]
pub fn connect(addr: SocketAddr) -> io::Result<TcpStream> {
let socket = new_for_addr(addr)?;
#[cfg(unix)]
Expand Down Expand Up @@ -332,3 +336,30 @@ impl FromRawSocket for TcpStream {
TcpStream::from_std(FromRawSocket::from_raw_socket(socket))
}
}

#[cfg(target_os = "wasi")]
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.inner.into_inner().into_raw_fd()
}
}

#[cfg(target_os = "wasi")]
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}

#[cfg(target_os = "wasi")]
impl FromRawFd for TcpStream {
/// Converts a `RawFd` to a `TcpStream`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
TcpStream::from_std(FromRawFd::from_raw_fd(fd))
}
}
4 changes: 3 additions & 1 deletion src/poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ impl Registry {
///
/// Event sources registered with this `Registry` will be registered with
/// the original `Registry` and `Poll` instance.
#[cfg(not(target_os = "wasi"))]
pub fn try_clone(&self) -> io::Result<Registry> {
self.selector
.try_clone()
Expand All @@ -643,7 +644,7 @@ impl Registry {

/// Internal check to ensure only a single `Waker` is active per [`Poll`]
/// instance.
#[cfg(debug_assertions)]
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
pub(crate) fn register_waker(&self) {
assert!(
!self.selector.register_waker(),
Expand All @@ -652,6 +653,7 @@ impl Registry {
}

/// Get access to the `sys::Selector`.
#[cfg(any(not(target_os = "wasi"), feature = "net"))]
pub(crate) fn selector(&self) -> &sys::Selector {
&self.selector
}
Expand Down
6 changes: 6 additions & 0 deletions src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ cfg_os_poll! {
pub use self::windows::*;
}

#[cfg(target_os = "wasi")]
cfg_os_poll! {
mod wasi;
pub(crate) use self::wasi::*;
}

cfg_not_os_poll! {
mod shell;
pub(crate) use self::shell::*;
Expand Down
2 changes: 2 additions & 0 deletions src/sys/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ macro_rules! os_required {
mod selector;
pub(crate) use self::selector::{event, Event, Events, Selector};

#[cfg(not(target_os = "wasi"))]
mod waker;
#[cfg(not(target_os = "wasi"))]
pub(crate) use self::waker::Waker;

cfg_net! {
Expand Down
22 changes: 21 additions & 1 deletion src/sys/shell/selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub type Events = Vec<Event>;
pub struct Selector {}

impl Selector {
#[cfg(not(target_os = "wasi"))]
pub fn try_clone(&self) -> io::Result<Selector> {
os_required!();
}
Expand All @@ -19,7 +20,7 @@ impl Selector {
os_required!();
}

#[cfg(debug_assertions)]
#[cfg(all(debug_assertions, not(target_os = "wasi")))]
pub fn register_waker(&self) -> bool {
os_required!();
}
Expand All @@ -44,6 +45,25 @@ cfg_any_os_ext! {
}
}

#[cfg(target_os = "wasi")]
cfg_any_os_ext! {
use crate::{Interest, Token};

impl Selector {
pub fn register(&self, _: wasi::Fd, _: Token, _: Interest) -> io::Result<()> {
os_required!();
}

pub fn reregister(&self, _: wasi::Fd, _: Token, _: Interest) -> io::Result<()> {
os_required!();
}

pub fn deregister(&self, _: wasi::Fd) -> io::Result<()> {
os_required!();
}
}
}

cfg_io_source! {
#[cfg(debug_assertions)]
impl Selector {
Expand Down
4 changes: 4 additions & 0 deletions src/sys/shell/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
use std::io;
use std::net::{self, SocketAddr};

#[cfg(not(target_os = "wasi"))]
pub(crate) fn new_for_addr(_: SocketAddr) -> io::Result<i32> {
os_required!();
}

#[cfg(not(target_os = "wasi"))]
pub(crate) fn bind(_: &net::TcpListener, _: SocketAddr) -> io::Result<()> {
os_required!();
}

#[cfg(not(target_os = "wasi"))]
pub(crate) fn connect(_: &net::TcpStream, _: SocketAddr) -> io::Result<()> {
os_required!();
}

#[cfg(not(target_os = "wasi"))]
pub(crate) fn listen(_: &net::TcpListener, _: u32) -> io::Result<()> {
os_required!();
}
Expand Down
1 change: 1 addition & 0 deletions src/sys/shell/udp.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg(not(target_os = "wasi"))]
use std::io;
use std::net::{self, SocketAddr};

Expand Down
Loading

0 comments on commit 2628793

Please sign in to comment.