Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions crates/host_env/src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,62 @@ pub mod platform {
#[allow(non_snake_case)]
#[cfg(windows)]
pub mod platform {
pub use WinSock::{FD_SET as fd_set, FD_SETSIZE, SOCKET as RawFd, TIMEVAL as timeval, select};
pub use WinSock::{SOCKET as RawFd, TIMEVAL as timeval};
use std::io;
use windows_sys::Win32::Networking::WinSock;

// Match CPython's Windows capacity. WinSock uses fd_count to determine
// the array length; the windows-sys declaration has the SDK default of 64.
pub const FD_SETSIZE: u32 = 512;

#[allow(non_camel_case_types)]
#[repr(C)]
pub struct fd_set {
fd_count: u32,
fd_array: [RawFd; FD_SETSIZE as usize],
}

const _: () = {
assert!(core::mem::align_of::<fd_set>() == core::mem::align_of::<WinSock::FD_SET>());
assert!(
core::mem::offset_of!(fd_set, fd_count)
== core::mem::offset_of!(WinSock::FD_SET, fd_count)
);
assert!(
core::mem::offset_of!(fd_set, fd_array)
== core::mem::offset_of!(WinSock::FD_SET, fd_array)
);
assert!(
core::mem::size_of::<fd_set>()
== core::mem::offset_of!(fd_set, fd_array)
+ FD_SETSIZE as usize * core::mem::size_of::<RawFd>()
);
};

/// # Safety
///
/// Every non-null set must point to an initialized fd_set, and timeout
/// must be null or point to an initialized timeval.
pub unsafe fn select(
nfds: i32,
readfds: *mut fd_set,
writefds: *mut fd_set,
errorfds: *mut fd_set,
timeout: *const timeval,
) -> i32 {
// fd_set is a count followed by a caller-sized SOCKET array. Both
// layouts have the same alignment and offset of the array.
unsafe {
WinSock::select(
nfds,
readfds.cast(),
writefds.cast(),
errorfds.cast(),
timeout,
)
}
}

/// # Safety
///
/// `set` must be a valid mutable pointer to an initialized WinSock fd_set.
Expand Down Expand Up @@ -71,8 +123,8 @@ pub mod platform {
///
/// `set` must be a valid mutable pointer to an initialized WinSock fd_set.
pub unsafe fn FD_ISSET(fd: RawFd, set: *mut fd_set) -> bool {
use WinSock::__WSAFDIsSet;
unsafe { __WSAFDIsSet(fd as _, set) != 0 }
let set = unsafe { &*set };
set.fd_array[..set.fd_count as usize].contains(&fd)
}

#[must_use]
Expand Down
19 changes: 19 additions & 0 deletions extra_tests/snippets/stdlib_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,25 @@ def fileno(self):
b.close()


# Windows supports up to 512 entries, above the Windows SDK default of 64.
for count in (65, 512) if sys.platform == "win32" else (65,):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the disallowed test-file changes

This newly added loop introduces test data, control flow, and assertions in a test file, but the root AGENTS.md limits test-file modifications to adding or removing expected-failure markers. Remove this regression block from the test file (or update the repository policy before landing it).

AGENTS.md reference: AGENTS.md:L275-L279

Useful? React with 👍 / 👎.

pairs = []
try:
for _ in range(count):
pairs.append(socket.socketpair())
readers = [reader for reader, writer in pairs]
assert select.select(readers, [], [], 0) == ([], [], [])
for reader, writer in pairs:
writer.send(b"x")
assert select.select(readers, [], [], 0) == (readers, [], [])
if sys.platform == "win32":
assert_raises(ValueError, select.select, [readers[0]] * 513, [], [], 0)
finally:
for pair in pairs:
for sock in pair:
sock.close()


# fileno() runs while the sequence is being read, and it can mutate the very
# list it was handed.
mutable_pair, other_end = socket.socketpair()
Expand Down
Loading