Skip to content

Commit 7b27712

Browse files
committed
Various fixes for Linux, Windows, and macOS.
1 parent 7ce5bdf commit 7b27712

File tree

3 files changed

+61
-20
lines changed

3 files changed

+61
-20
lines changed

src/event/select.rs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
//! `select` is unsafe due to I/O safety.
66
#![allow(unsafe_code)]
77

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

44+
/// Storage element type for use with [`select`].
45+
#[cfg(linux_like)]
46+
#[repr(transparent)]
47+
#[derive(Copy, Clone, Default)]
48+
pub struct FdSetElement(pub(crate) c::c_ulong);
49+
4250
/// Storage element type for use with [`select`].
4351
#[cfg(not(any(
52+
linux_like,
4453
windows,
4554
target_os = "wasi",
4655
all(
@@ -185,11 +194,16 @@ pub fn fd_set_bound(fds: &[FdSetElement]) -> RawFd {
185194

186195
#[cfg(any(windows, target_os = "wasi"))]
187196
{
188-
assert!(cfg!(target_endian = "little"), "what");
189-
190197
let set = unsafe { &*fds.as_ptr().cast::<FD_SET>() };
191198
let fd_count = set.fd_count;
192-
fd_count as RawFd
199+
let fd_array = unsafe { slice::from_raw_parts(set.fd_array.as_ptr(), fd_count as usize) };
200+
let mut max = 0;
201+
for fd in fd_array {
202+
if *fd >= max {
203+
max = *fd + 1;
204+
}
205+
}
206+
max as RawFd
193207
}
194208
}
195209

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

303317
fn next(&mut self) -> Option<Self::Item> {
304-
assert!(cfg!(target_endian = "little"), "what");
305-
306318
let current = self.current;
307319

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

326338
#[test]
327-
#[cfg(windows)]
339+
#[cfg(any(windows, target_os = "wasi"))]
328340
fn layouts() {
329-
use windows_sys::Win32::Networking::WinSock::FD_SET;
330-
331-
// The first element of the `FdSetElement` array corresponds to the
332-
// `fd_count` field.
333-
assert_eq!(memoffset::offset_of!(FD_SET, fd_count), 0);
334-
335-
// The following elements of the `FdSetElement` array correspond to the
336-
// `fd_array` field.
337-
let array = [FdSetElement::default()];
338-
assert_eq!(memoffset::offset_of!(FD_SET, fd_array), unsafe {
339-
array[1..1].as_ptr().offset_from(array[0..0].as_ptr()) as usize
340-
});
341-
342341
// The `FdSetElement` array should be suitably aligned.
343342
assert_eq!(align_of::<FdSetElement>(), align_of::<FD_SET>());
344343
}

tests/event/main.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,32 @@ mod eventfd;
1212
mod poll;
1313
#[cfg(any(bsd, linux_kernel, windows, target_os = "wasi"))]
1414
mod select;
15+
16+
#[cfg(windows)]
17+
mod windows {
18+
use std::sync::OnceLock;
19+
20+
pub struct Thing;
21+
22+
impl Thing {
23+
pub fn new() -> Self {
24+
let _ = rustix::net::wsa_startup().unwrap();
25+
Self
26+
}
27+
}
28+
29+
impl Drop for Thing {
30+
fn drop(&mut self) {
31+
rustix::net::wsa_cleanup().unwrap();
32+
}
33+
}
34+
35+
pub static CLEANUP: OnceLock<Thing> = OnceLock::new();
36+
}
37+
38+
/// Checks whether the Windows socket interface has been started already, and
39+
/// if not, starts it.
40+
pub fn init() {
41+
#[cfg(windows)]
42+
let _ = windows::CLEANUP.get_or_init(|| windows::Thing::new());
43+
}

tests/event/select.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,13 @@ fn test_select_with_great_fds() {
178178

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

186+
crate::init();
187+
185188
// Create a socket pair (but don't use `socketpair` because we want this
186189
// to work on Windows too).
187190

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

283+
crate::init();
284+
280285
let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST);
281286
let addr = SocketAddr::new(localhost, 0);
282287
let listener = rustix::net::socket(AddressFamily::INET, SocketType::STREAM, None).unwrap();
@@ -299,6 +304,14 @@ fn test_select_with_maxfd_sockets() {
299304

300305
// Renumber the fds to the maximum possible values.
301306
let great_fd = unsafe { libc::dup2(reader.as_raw_fd(), fd_limit as RawFd - 1) };
307+
308+
// On old versions of macOS, the above `dup2` call fails with `EBADF`. Just
309+
// skip the rest of this test in that case.
310+
#[cfg(apple)]
311+
if great_fd == -1 && libc_errno::errno().0 == libc::EBADF {
312+
return;
313+
}
314+
302315
let reader = unsafe { OwnedFd::from_raw_fd(great_fd) };
303316
let great_fd = unsafe { libc::dup2(writer.as_raw_fd(), fd_limit as RawFd - 2) };
304317
let writer = unsafe { OwnedFd::from_raw_fd(great_fd) };

0 commit comments

Comments
 (0)