#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "solaris",
target_os = "illumos",
))]
#[allow(dead_code)]
const F_GETFD: i32 = 1;
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "solaris",
target_os = "illumos",
))]
#[allow(dead_code)]
const F_SETFD: i32 = 2;
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "solaris",
target_os = "illumos",
))]
const F_GETFL: i32 = 3;
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "solaris",
target_os = "illumos",
))]
const F_SETFL: i32 = 4;
#[cfg(not(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "solaris",
target_os = "illumos",
)))]
compile_error!(
"fcntl F_GETFD/F_SETFD/F_GETFL/F_SETFL values are unknown for this target \
— add it to the cfg gates in nonblock_fd.rs after verifying against the \
platform's <fcntl.h>"
);
#[cfg(target_os = "linux")]
const O_NONBLOCK_FCNTL: i32 = 0x800;
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
))]
const O_NONBLOCK_FCNTL: i32 = 0x0004;
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
const O_NONBLOCK_FCNTL: i32 = 0x80;
#[cfg(not(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "solaris",
target_os = "illumos",
)))]
compile_error!(
"O_NONBLOCK_FCNTL value is unknown for this target — add it to the cfg \
gates in nonblock_fd.rs after verifying against the platform's <fcntl.h>"
);
const _F_GETFL_NEQ_F_SETFL: () = assert!(F_GETFL != F_SETFL);
const _F_GETFL_NEQ_F_SETFD: () = assert!(F_GETFL != F_SETFD);
const _F_GETFL_NEQ_F_GETFD: () = assert!(F_GETFL != F_GETFD);
const _F_SETFL_NEQ_F_SETFD: () = assert!(F_SETFL != F_SETFD);
const _F_SETFL_NEQ_F_GETFD: () = assert!(F_SETFL != F_GETFD);
const _F_GETFD_NEQ_F_SETFD: () = assert!(F_GETFD != F_SETFD);
const _O_NONBLOCK_NONZERO: () = assert!(O_NONBLOCK_FCNTL != 0);
extern "C" {
fn fcntl(fd: i32, cmd: i32, ...) -> i32;
}
pub(crate) fn set_nonblocking_fd(fd: i32) -> bool {
let flags = unsafe { fcntl(fd, F_GETFL) };
if flags < 0 {
return false;
}
let rc = unsafe { fcntl(fd, F_SETFL, flags | O_NONBLOCK_FCNTL) };
rc >= 0
}
#[cfg(test)]
mod tests {
use super::*;
extern "C" {
fn pipe(fds: *mut [i32; 2]) -> i32;
fn close(fd: i32) -> i32;
fn read(fd: i32, buf: *mut u8, count: usize) -> isize;
}
fn make_pipe() -> (i32, i32) {
let mut fds: [i32; 2] = [-1, -1];
let rc = unsafe { pipe(&mut fds as *mut [i32; 2]) };
assert_eq!(rc, 0, "pipe(2) failed in test harness");
(fds[0], fds[1])
}
fn close_fd(fd: i32) {
unsafe {
let _ = close(fd);
}
}
#[test]
fn set_nonblocking_fd_sets_o_nonblock_bit_on_pipe() {
let (rfd, wfd) = make_pipe();
let initial = unsafe { fcntl(rfd, F_GETFL) };
assert!(initial >= 0, "F_GETFL before set failed: {initial}");
assert_eq!(
initial & O_NONBLOCK_FCNTL,
0,
"pipe(2) read end should start in blocking mode"
);
assert!(set_nonblocking_fd(rfd), "set_nonblocking_fd returned false");
let after = unsafe { fcntl(rfd, F_GETFL) };
assert!(after >= 0, "F_GETFL after set failed: {after}");
assert_ne!(
after & O_NONBLOCK_FCNTL,
0,
"O_NONBLOCK bit should be set after set_nonblocking_fd"
);
close_fd(rfd);
close_fd(wfd);
}
#[test]
fn pipe_read_after_set_nonblocking_returns_wouldblock() {
let (rfd, wfd) = make_pipe();
assert!(set_nonblocking_fd(rfd));
let mut buf = [0u8; 16];
let n = unsafe { read(rfd, buf.as_mut_ptr(), buf.len()) };
assert_eq!(n, -1, "non-blocking read of empty pipe should fail");
let err = std::io::Error::last_os_error();
assert_eq!(
err.kind(),
std::io::ErrorKind::WouldBlock,
"expected WouldBlock, got {err:?}",
);
close_fd(rfd);
close_fd(wfd);
}
}