Skip to content

Commit

Permalink
Various fixes for Linux, Windows, and macOS.
Browse files Browse the repository at this point in the history
  • Loading branch information
sunfishcode committed Sep 24, 2024
1 parent 7ce5bdf commit 7b27712
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 20 deletions.
37 changes: 18 additions & 19 deletions src/event/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
//! `select` is unsafe due to I/O safety.
#![allow(unsafe_code)]

#[cfg(linux_like)]
use crate::backend::c;
use crate::fd::RawFd;
use crate::{backend, io};
#[cfg(any(windows, target_os = "wasi"))]
Expand Down Expand Up @@ -39,8 +41,15 @@ use windows_sys::Win32::Networking::WinSock::FD_SET;
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) u64);

/// Storage element type for use with [`select`].
#[cfg(linux_like)]
#[repr(transparent)]
#[derive(Copy, Clone, Default)]
pub struct FdSetElement(pub(crate) c::c_ulong);

/// Storage element type for use with [`select`].
#[cfg(not(any(
linux_like,
windows,
target_os = "wasi",
all(
Expand Down Expand Up @@ -185,11 +194,16 @@ pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {

#[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
let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };
let mut max = 0;
for fd in fd_array {
if *fd >= max {
max = *fd + 1;
}
}
max as RawFd
}
}

Expand Down Expand Up @@ -301,8 +315,6 @@ impl<'a> Iterator for FdSetIter<'a> {
type Item = RawFd;

fn next(&mut self) -> Option<Self::Item> {
assert!(cfg!(target_endian = "little"), "what");

let current = self.current;

let set = unsafe { &*self.fds.as_ptr().cast::<FD_SET>() };
Expand All @@ -324,21 +336,8 @@ mod test {
use core::mem::align_of;

#[test]
#[cfg(windows)]
#[cfg(any(windows, target_os = "wasi"))]
fn layouts() {
use windows_sys::Win32::Networking::WinSock::FD_SET;

// The first element of the `FdSetElement` array corresponds to the
// `fd_count` field.
assert_eq!(memoffset::offset_of!(FD_SET, fd_count), 0);

// The following elements of the `FdSetElement` array correspond to the
// `fd_array` field.
let array = [FdSetElement::default()];
assert_eq!(memoffset::offset_of!(FD_SET, fd_array), unsafe {
array[1..1].as_ptr().offset_from(array[0..0].as_ptr()) as usize
});

// The `FdSetElement` array should be suitably aligned.
assert_eq!(align_of::<FdSetElement>(), align_of::<FD_SET>());
}
Expand Down
29 changes: 29 additions & 0 deletions tests/event/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,32 @@ mod eventfd;
mod poll;
#[cfg(any(bsd, linux_kernel, windows, target_os = "wasi"))]
mod select;

#[cfg(windows)]
mod windows {
use std::sync::OnceLock;

pub struct Thing;

impl Thing {
pub fn new() -> Self {
let _ = rustix::net::wsa_startup().unwrap();
Self
}
}

impl Drop for Thing {
fn drop(&mut self) {
rustix::net::wsa_cleanup().unwrap();
}
}

pub static CLEANUP: OnceLock<Thing> = OnceLock::new();
}

/// Checks whether the Windows socket interface has been started already, and
/// if not, starts it.
pub fn init() {
#[cfg(windows)]
let _ = windows::CLEANUP.get_or_init(|| windows::Thing::new());
}
15 changes: 14 additions & 1 deletion tests/event/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,13 @@ fn test_select_with_great_fds() {

#[cfg(feature = "net")]
#[test]
#[serial] // for `crate::init`
fn test_select_with_sockets() {
use rustix::net::{recv, send, AddressFamily, RecvFlags, SendFlags, SocketType};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

crate::init();

// Create a socket pair (but don't use `socketpair` because we want this
// to work on Windows too).

Expand Down Expand Up @@ -271,12 +274,14 @@ fn test_select_with_sockets() {
#[cfg(feature = "net")]
#[cfg(not(windows))] // for `dup2` usage
#[test]
#[serial] // for `setrlimit` usage
#[serial] // for `setrlimit` usage, and `crate::init`
fn test_select_with_maxfd_sockets() {
use rustix::net::{recv, send, AddressFamily, RecvFlags, SendFlags, SocketType};
use rustix::process::{getrlimit, setrlimit, Resource};
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

crate::init();

let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST);
let addr = SocketAddr::new(localhost, 0);
let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap();
Expand All @@ -299,6 +304,14 @@ fn test_select_with_maxfd_sockets() {

// Renumber the fds to the maximum possible values.
let great_fd = unsafe { libc::dup2(reader.as_raw_fd(), fd_limit as RawFd - 1) };

// On old versions of macOS, the above `dup2` call fails with `EBADF`. Just
// skip the rest of this test in that case.
#[cfg(apple)]
if great_fd == -1 && libc_errno::errno().0 == libc::EBADF {
return;
}

let reader = unsafe { OwnedFd::from_raw_fd(great_fd) };
let great_fd = unsafe { libc::dup2(writer.as_raw_fd(), fd_limit as RawFd - 2) };
let writer = unsafe { OwnedFd::from_raw_fd(great_fd) };
Expand Down

0 comments on commit 7b27712

Please sign in to comment.