From d3714c94fcfa2ac88909a91bf6448162af0c697e Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 23 Sep 2024 16:45:21 -0700 Subject: [PATCH] Support WASI. --- src/backend/libc/event/syscalls.rs | 61 ++++++++++++++++++- src/event/select.rs | 96 +++++++++++++++++++++++++----- 2 files changed, 138 insertions(+), 19 deletions(-) diff --git a/src/backend/libc/event/syscalls.rs b/src/backend/libc/event/syscalls.rs index 244fb6b36..763f2e2c0 100644 --- a/src/backend/libc/event/syscalls.rs +++ b/src/backend/libc/event/syscalls.rs @@ -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; @@ -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, @@ -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 { + 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 { unsafe { ret_owned_fd(c::port_create()) } diff --git a/src/event/select.rs b/src/event/select.rs index 8100157d6..521d1fd06 100644 --- a/src/event/select.rs +++ b/src/event/select.rs @@ -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, @@ -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") @@ -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. /// @@ -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::() * 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::() }; + 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::() }; + 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) { @@ -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::() }; + let fd_count = set.fd_count; + fd_count as RawFd + } } /// Compute the number of `FdSetElement`s needed to hold a set which can @@ -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::() }; + 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) } }