| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| use anyhow::Result; |
| use nix::libc::c_int; |
| use nix::poll; |
| use std::convert::TryInto; |
| use std::io; |
| use std::os::unix::io::{AsRawFd, RawFd}; |
| use std::time::Duration; |
| |
| /// Waits for an event on `fd` or for `timeout` to expire. |
| pub fn wait_timeout(fd: RawFd, events: poll::PollFlags, timeout: Duration) -> Result<()> { |
| let timeout = timeout.as_millis().try_into().unwrap_or(c_int::MAX); |
| let mut pfd = [poll::PollFd::new(fd, events)]; |
| match poll::poll(&mut pfd, timeout)? { |
| 0 => Err(io::Error::new( |
| io::ErrorKind::TimedOut, |
| "timed out waiting for fd to be ready", |
| ) |
| .into()), |
| _ => Ok(()), |
| } |
| } |
| |
| /// Waits for `fd` to become ready to read or `timeout` to expire. |
| pub fn wait_read_timeout(fd: &impl AsRawFd, timeout: Duration) -> Result<()> { |
| wait_timeout(fd.as_raw_fd(), poll::PollFlags::POLLIN, timeout) |
| } |
| |
| /// Waits for `fd` to become ready to read or `timeout` to expire. |
| pub fn wait_fd_read_timeout(fd: RawFd, timeout: Duration) -> Result<()> { |
| wait_timeout(fd, poll::PollFlags::POLLIN, timeout) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use anyhow::bail; |
| use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType}; |
| use nix::unistd::{read, write}; |
| |
| #[test] |
| fn test_data_ready() -> Result<()> { |
| let (snd, rcv) = socketpair( |
| AddressFamily::Unix, |
| SockType::Stream, |
| None, |
| SockFlag::empty(), |
| )?; |
| |
| // Send the test data into the socket. |
| let sndbuf = b"abc123"; |
| assert_eq!(write(snd, sndbuf)?, sndbuf.len()); |
| |
| // Wait for it to be ready. |
| wait_read_timeout(&rcv, Duration::from_millis(10))?; |
| |
| // Receive the test data and compare. |
| let mut rcvbuf = [0u8; 6]; |
| assert_eq!(read(rcv, &mut rcvbuf)?, sndbuf.len()); |
| assert_eq!(sndbuf, &rcvbuf); |
| Ok(()) |
| } |
| |
| #[test] |
| fn test_timeout() -> Result<()> { |
| let (_snd, rcv) = socketpair( |
| AddressFamily::Unix, |
| SockType::Stream, |
| None, |
| SockFlag::empty(), |
| )?; |
| // Expect to timeout since there is no data ready on the socket. |
| let result = wait_read_timeout(&rcv, Duration::from_millis(10)); |
| assert!(result.is_err()); |
| let err = result.unwrap_err(); |
| match err.downcast_ref::<io::Error>() { |
| Some(e) => assert_eq!(io::ErrorKind::TimedOut, e.kind()), |
| _ => bail!("Unexpected error result {:?}", err), |
| } |
| Ok(()) |
| } |
| } |