diff --git a/filedescriptor/Cargo.toml b/filedescriptor/Cargo.toml index 133e49382..415c31005 100644 --- a/filedescriptor/Cargo.toml +++ b/filedescriptor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "filedescriptor" -version = "0.6.0" +version = "0.7.0" authors = ["Wez Furlong"] edition = "2018" repository = "https://github.com/wez/wzsh" diff --git a/filedescriptor/src/lib.rs b/filedescriptor/src/lib.rs index 5fce1f4f2..a2da16369 100644 --- a/filedescriptor/src/lib.rs +++ b/filedescriptor/src/lib.rs @@ -247,6 +247,16 @@ impl FileDescriptor { pub fn as_stdio(&self) -> anyhow::Result { self.as_stdio_impl() } + + /// Attempt to change the non-blocking IO mode of the file descriptor. + /// Not all kinds of file descriptor can be placed in non-blocking mode + /// on all systems, and some file descriptors will claim to be in + /// non-blocking mode but it will have no effect. + /// File descriptors based on sockets are the most portable type + /// that can be successfully made non-blocking. + pub fn set_non_blocking(&mut self, non_blocking: bool) -> anyhow::Result<()> { + self.set_non_blocking_impl(non_blocking) + } } /// Represents the readable and writable ends of a pair of descriptors diff --git a/filedescriptor/src/unix.rs b/filedescriptor/src/unix.rs index af613ae4d..b97dee4ca 100644 --- a/filedescriptor/src/unix.rs +++ b/filedescriptor/src/unix.rs @@ -204,6 +204,19 @@ impl FileDescriptor { let stdio = unsafe { std::process::Stdio::from_raw_fd(fd) }; Ok(stdio) } + + #[inline] + pub(crate) fn set_non_blocking_impl(&mut self, non_blocking: bool) -> anyhow::Result<()> { + let on = if non_blocking { 1 } else { 0 }; + let res = unsafe { libc::ioctl(self.handle.as_raw_file_descriptor(), libc::FIONBIO, &on) }; + if res != 0 { + bail!( + "failed to change non-blocking mode: {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } } impl Pipe { diff --git a/filedescriptor/src/windows.rs b/filedescriptor/src/windows.rs index 3261770fb..3022185c6 100644 --- a/filedescriptor/src/windows.rs +++ b/filedescriptor/src/windows.rs @@ -19,8 +19,9 @@ use winapi::um::processthreadsapi::*; use winapi::um::winbase::{FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE}; use winapi::um::winnt::HANDLE; use winapi::um::winsock2::{ - accept, bind, closesocket, connect, getsockname, htonl, listen, WSAPoll, WSASocketW, - WSAStartup, INVALID_SOCKET, SOCKET, SOCK_STREAM, WSADATA, WSA_FLAG_NO_HANDLE_INHERIT, + accept, bind, closesocket, connect, getsockname, htonl, ioctlsocket, listen, WSAPoll, + WSASocketW, WSAStartup, INVALID_SOCKET, SOCKET, SOCK_STREAM, WSADATA, + WSA_FLAG_NO_HANDLE_INHERIT, }; pub use winapi::um::winsock2::{POLLERR, POLLHUP, POLLIN, POLLOUT, WSAPOLLFD as pollfd}; @@ -88,14 +89,15 @@ impl FromRawSocketDescriptor for T { unsafe impl Send for OwnedHandle {} impl OwnedHandle { - fn probe_handle_type_if_unknown(handle: HANDLE, handle_type: HandleType) -> HandleType { + fn probe_handle_type_if_unknown(handle: RawHandle, handle_type: HandleType) -> HandleType { match handle_type { HandleType::Unknown => Self::probe_handle_type(handle), t => t, } } - pub(crate) fn probe_handle_type(handle: HANDLE) -> HandleType { + pub(crate) fn probe_handle_type(handle: RawHandle) -> HandleType { + let handle = handle as HANDLE; match unsafe { GetFileType(handle) } { FILE_TYPE_CHAR => HandleType::Char, FILE_TYPE_DISK => HandleType::Disk, @@ -212,6 +214,29 @@ impl FileDescriptor { let stdio = unsafe { std::process::Stdio::from_raw_handle(handle) }; Ok(stdio) } + + #[inline] + pub(crate) fn set_non_blocking_impl(&mut self, non_blocking: bool) -> anyhow::Result<()> { + if !self.handle.is_socket_handle() { + bail!("only socket descriptors can change their non-blocking mode on windows"); + } + + let mut on = if non_blocking { 1 } else { 0 }; + let res = unsafe { + ioctlsocket( + self.as_raw_socket() as SOCKET, + winapi::um::winsock2::FIONBIO, + &mut on, + ) + }; + if res != 0 { + bail!( + "failed to change non-blocking mode: {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } } impl IntoRawHandle for FileDescriptor {