Repository: firecracker-microvm/firecracker
File: src/utils/src/time.rs (Rust)

Code before:
            },
            it_interval: if let Some(interval) = interval {
                libc::timespec {
                    tv_sec: interval.as_secs() as i64,
                    tv_nsec: interval.subsec_nanos() as i64,
                }
            } else {
                libc::timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                }
            },
        };
        // SAFETY: Safe because this doesn't modify any memory and we check the return value.
        let ret = unsafe { libc::timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
        if ret < 0 {
            return Err(std::io::Error::last_os_error());
        }
        Ok(())
    }

    /// Read the value from the timerfd. Since it is always created with NONBLOCK flag,
    /// this function does not block.
    pub fn read(&mut self) -> Result<u64, std::io::Error> {
        let mut buf = [0u8; size_of::<u64>()];
        match self.0.read(buf.as_mut_slice()) {
            Ok(_) => Ok(u64::from_ne_bytes(buf)),
            Err(inner) if inner.kind() == ErrorKind::WouldBlock => Ok(0),
            Err(err) => Err(err),
        }
    }

    /// Tell if the timer is currently armed.
    pub fn is_armed(&self) -> Result<bool, std::io::Error> {
        // SAFETY: Zero init of a PDO type.
        let mut spec: libc::itimerspec = unsafe { std::mem::zeroed() };
        // SAFETY: Safe because timerfd_gettime is trusted to only modify `spec`.
        let ret = unsafe { libc::timerfd_gettime(self.as_raw_fd(), &mut spec) };
        if ret < 0 {
            return Err(std::io::Error::last_os_error());
        }
        Ok(spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0)
    }
}

impl AsRawFd for TimerFd {
    fn as_raw_fd(&self) -> RawFd {
        self.0.as_raw_fd()
    }
}


Review comment (bug):
do we need to handle the EINTR case? The following is how the `timerfd` crate does it:
```
pub fn read(&self) -> u64 {
        let mut buffer = [0_u8; 8];
        loop {
            match rustix::io::read(&self.0, &mut buffer) {
                Ok(8) => {
                    let value = u64::from_ne_bytes(buffer);
                    assert_ne!(value, 0);
                    return value;
                }
                Err(rustix::io::Errno::WOULDBLOCK) => return 0,
                Err(rustix::io::Errno::INTR) => (),
                Err(e) => panic!("Unexpected read error: {}", e),
                _ => unreachable!(),
            }
        }
    }
```

Handling these edge cases is why I'm wondering whether it's really worth for us maintaining a donwstream implementation


Code after:
            it_interval: if let Some(interval) = interval {
                libc::timespec {
                    tv_sec: interval.as_secs() as i64,
                    tv_nsec: interval.subsec_nanos() as i64,
                }
            } else {
                libc::timespec {
                    tv_sec: 0,
                    tv_nsec: 0,
                }
            },
        };
        // SAFETY: Safe because this doesn't modify any memory and we check the return value.
        let ret = unsafe { libc::timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
        assert!(
            0 <= ret,
            "TimerFd arm failed: {:#}",
            std::io::Error::last_os_error()
        );
    }

    /// Read the value from the timerfd. Since it is always created with NONBLOCK flag,
    /// this function does not block.
    pub fn read(&mut self) -> u64 {
        let mut buf = [0u8; size_of::<u64>()];
        match self.0.read(buf.as_mut_slice()) {
            Ok(_) => u64::from_ne_bytes(buf),
            Err(inner) if inner.kind() == ErrorKind::WouldBlock => 0,
            Err(err) => panic!("TimerFd read failed: {err:#}"),
        }
    }

    /// Tell if the timer is currently armed.
    pub fn is_armed(&self) -> bool {
        // SAFETY: Zero init of a PDO type.
        let mut spec: libc::itimerspec = unsafe { std::mem::zeroed() };
        // SAFETY: Safe because timerfd_gettime is trusted to only modify `spec`.
        let ret = unsafe { libc::timerfd_gettime(self.as_raw_fd(), &mut spec) };
        assert!(
            0 <= ret,
            "TimerFd arm failed: {:#}",
            std::io::Error::last_os_error()
        );
        spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0
    }
}

impl AsRawFd for TimerFd {
    fn as_raw_fd(&self) -> RawFd {
        self.0.as_raw_fd()
    }