Skip to content

Commit

Permalink
Support WASI.
Browse files Browse the repository at this point in the history
  • Loading branch information
sunfishcode committed Sep 23, 2024
1 parent 52381f0 commit d3714c9
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 19 deletions.
61 changes: 58 additions & 3 deletions src/backend/libc/event/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::event::port::Event;
target_os = "espidf"
))]
use crate::event::EventfdFlags;
#[cfg(any(bsd, linux_kernel))]
#[cfg(any(bsd, linux_kernel, target_os = "wasi"))]
use crate::event::FdSetElement;
use crate::event::PollFd;
use crate::io;
Expand All @@ -30,9 +30,9 @@ use crate::utils::as_ptr;
all(feature = "alloc", any(linux_kernel, target_os = "redox")),
))]
use core::mem::MaybeUninit;
#[cfg(any(bsd, linux_kernel))]
#[cfg(any(bsd, linux_kernel, target_os = "wasi"))]
use core::ptr::null;
#[cfg(any(bsd, linux_kernel, solarish, target_os = "redox"))]
#[cfg(any(bsd, linux_kernel, solarish, target_os = "redox", target_os = "wasi"))]
use core::ptr::null_mut;
#[cfg(any(
linux_kernel,
Expand Down Expand Up @@ -205,6 +205,61 @@ pub(crate) unsafe fn select(
}
}

// WASI uses a count + array instead of a bitvector.
#[cfg(target_os = "wasi")]
pub(crate) unsafe fn select(
nfds: i32,
readfds: Option<&mut [FdSetElement]>,
writefds: Option<&mut [FdSetElement]>,
exceptfds: Option<&mut [FdSetElement]>,
timeout: Option<&crate::timespec::Timespec>,
) -> io::Result<i32> {
let len = crate::event::fd_set_num_elements_for_fd_array(nfds as usize);

let readfds = match readfds {
Some(readfds) => {
assert!(readfds.len() >= len);
readfds.as_mut_ptr()
}
None => null_mut(),
};
let writefds = match writefds {
Some(writefds) => {
assert!(writefds.len() >= len);
writefds.as_mut_ptr()
}
None => null_mut(),
};
let exceptfds = match exceptfds {
Some(exceptfds) => {
assert!(exceptfds.len() >= len);
exceptfds.as_mut_ptr()
}
None => null_mut(),
};

let timeout_data;
let timeout_ptr = match timeout {
Some(timeout) => {
// Convert from `Timespec` to `c::timeval`.
timeout_data = c::timeval {
tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
tv_usec: ((timeout.tv_nsec + 999) / 1000) as _,
};
&timeout_data
}
None => null(),
};

ret_c_int(c::select(
nfds,
readfds.cast(),
writefds.cast(),
exceptfds.cast(),
timeout_ptr as *mut c::timeval,
))
}

#[cfg(solarish)]
pub(crate) fn port_create() -> io::Result<OwnedFd> {
unsafe { ret_owned_fd(c::port_create()) }
Expand Down
96 changes: 80 additions & 16 deletions src/event/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,28 @@
//! `select` is unsafe due to I/O safety.
#![allow(unsafe_code)]

use crate::fd::RawFd;
use crate::{backend, io};
#[cfg(any(windows, target_os = "wasi"))]
use core::slice;

pub use crate::timespec::{Nsecs, Secs, Timespec};

/// wasi-libc's `fd_set` type. The libc bindings for it have private fields,
/// so we redeclare it for ourselves so that we can access the fields. They're
/// publicly exposed in wasi-libc.
#[cfg(target_os = "wasi")]
#[repr(C)]
struct FD_SET {
/// The wasi-libc headers call this `__nfds`.
fd_count: usize,
/// The wasi-libc headers call this `__fds`.
fd_array: [i32; libc::FD_SETSIZE],
}

#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock::FD_SET;

/// Storage element type for use with [`select`].
#[cfg(any(
windows,
Expand All @@ -24,6 +42,7 @@ pub struct FdSetElement(pub(crate) u64);
/// Storage element type for use with [`select`].
#[cfg(not(any(
windows,
target_os = "wasi",
all(
target_pointer_width = "64",
any(target_os = "freebsd", target_os = "dragonfly")
Expand All @@ -33,6 +52,12 @@ pub struct FdSetElement(pub(crate) u64);
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) u32);

/// Storage element type for use with [`select`].
#[cfg(target_os = "wasi")]
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) usize);

/// `select(nfds, readfds, writefds, exceptfds, timeout)`—Wait for events on
/// sets of file descriptors.
///
Expand Down Expand Up @@ -92,37 +117,62 @@ pub unsafe fn select(
backend::event::syscalls::select(nfds, readfds, writefds, exceptfds, timeout)
}

#[cfg(not(any(windows, target_os = "wasi")))]
const BITS: usize = core::mem::size_of::<FdSetElement>() * 8;
use crate::fd::RawFd;

/// Set `fd` in the set pointed to by `fds`.
#[doc(alias = "FD_SET")]
#[inline]
pub fn fd_set_insert(fds: &mut [FdSetElement], fd: RawFd) {
let fd = fd as usize;
fds[fd / BITS].0 |= 1 << (fd % BITS);
#[cfg(not(any(windows, target_os = "wasi")))]
{
let fd = fd as usize;
fds[fd / BITS].0 |= 1 << (fd % BITS);
}

#[cfg(any(windows, target_os = "wasi"))]
{
let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };

if !fd_array.iter().any(|p| *p as RawFd == fd) {
let fd_array = unsafe {
slice::from_raw_parts_mut(set.fd_array.as_mut_ptr(), fd_count as usize + 1)
};
set.fd_count = fd_count + 1;
fd_array[fd_count as usize] = fd as _;
}
}
}

/// Clear `fd` in the set pointed to by `fds`.
#[doc(alias = "FD_CLR")]
#[inline]
pub fn fd_set_remove(fds: &mut [FdSetElement], fd: RawFd) {
let fd = fd as usize;
fds[fd / BITS].0 &= !(1 << (fd % BITS));
}
#[cfg(not(any(windows, target_os = "wasi")))]
{
let fd = fd as usize;
fds[fd / BITS].0 &= !(1 << (fd % BITS));
}

/// Compute the minimum `nfds` value needed for the set pointed to by
/// `fds`.
#[inline]
pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {
#[cfg(any(windows, target_os = "wasi"))]
{
assert!(cfg!(target_endian = "little"), "what");
let set = unsafe { &mut *fds.as_mut_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };

let fd_count = fds[0].0 as usize;
(fd_count + 1) as RawFd
if let Some(pos) = fd_array.iter().position(|p| *p as RawFd == fd) {
set.fd_count = fd_count - 1;
set.fd_array[pos] = *set.fd_array.last().unwrap();
}
}
}

/// Compute the minimum `nfds` value needed for the set pointed to by
/// `fds`.
#[inline]
pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {
#[cfg(not(any(windows, target_os = "wasi")))]
{
if let Some(position) = fds.iter().rposition(|element| element.0 != 0) {
Expand All @@ -132,6 +182,15 @@ pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {
0
}
}

#[cfg(any(windows, target_os = "wasi"))]
{
assert!(cfg!(target_endian = "little"), "what");

let set = unsafe { &*fds.as_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
fd_count as RawFd
}
}

/// Compute the number of `FdSetElement`s needed to hold a set which can
Expand Down Expand Up @@ -245,12 +304,17 @@ impl<'a> Iterator for FdSetIter<'a> {
assert!(cfg!(target_endian = "little"), "what");

let current = self.current;
if current as u64 == self.fds[0].0 {

let set = unsafe { &*self.fds.as_ptr().cast::<FD_SET>() };
let fd_count = set.fd_count;
let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };

if current == fd_count as usize {
return None;
}
let fd = self.fds[current as usize + 1].0;
let fd = fd_array[current as usize];
self.current = current + 1;
Some(fd)
Some(fd as RawFd)
}
}

Expand Down

0 comments on commit d3714c9

Please sign in to comment.