#![allow(clippy::doc_markdown)]
use std::io;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::time::Duration;
use iceoryx2_bb_posix::file_descriptor::{FileDescriptor, FileDescriptorBased};
use iceoryx2_bb_posix::file_descriptor_set::SynchronousMultiplexing;
#[allow(clippy::redundant_pub_crate)]
pub(crate) struct TimerFd {
owned: OwnedFd,
descriptor: FileDescriptor,
}
impl TimerFd {
#[allow(unsafe_code, clippy::borrow_as_ptr)]
pub(crate) fn new(period: Duration) -> io::Result<Self> {
let raw = unsafe {
libc::timerfd_create(
libc::CLOCK_MONOTONIC,
libc::TFD_CLOEXEC | libc::TFD_NONBLOCK,
)
};
if raw < 0 {
return Err(io::Error::last_os_error());
}
let owned = unsafe { OwnedFd::from_raw_fd(raw) };
let period_ns = i128::try_from(period.as_nanos()).unwrap_or(i128::MAX);
let mut now = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
let rc = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now) };
if rc < 0 {
return Err(io::Error::last_os_error());
}
let first_ns = i128::from(now.tv_sec) * 1_000_000_000 + i128::from(now.tv_nsec) + period_ns;
let spec = libc::itimerspec {
it_value: ns_to_timespec(first_ns),
it_interval: ns_to_timespec(period_ns),
};
let rc = unsafe {
libc::timerfd_settime(raw, libc::TFD_TIMER_ABSTIME, &spec, std::ptr::null_mut())
};
if rc < 0 {
return Err(io::Error::last_os_error());
}
let descriptor = FileDescriptor::non_owning_new(raw)
.ok_or_else(|| io::Error::other("timerfd: kernel returned an invalid fd"))?;
Ok(Self { owned, descriptor })
}
#[allow(unsafe_code)]
pub(crate) fn drain(&self) -> u64 {
let mut buf = [0u8; 8];
let n = unsafe { libc::read(self.owned.as_raw_fd(), buf.as_mut_ptr().cast(), buf.len()) };
if n == 8 { u64::from_ne_bytes(buf) } else { 0 }
}
}
impl FileDescriptorBased for TimerFd {
fn file_descriptor(&self) -> &FileDescriptor {
&self.descriptor
}
}
impl SynchronousMultiplexing for TimerFd {}
impl std::fmt::Debug for TimerFd {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TimerFd")
.field("fd", &self.owned.as_raw_fd())
.finish_non_exhaustive()
}
}
fn ns_to_timespec(ns: i128) -> libc::timespec {
let secs = ns / 1_000_000_000;
let nsec = ns % 1_000_000_000;
libc::timespec {
tv_sec: libc::time_t::try_from(secs).unwrap_or(libc::time_t::MAX),
tv_nsec: nsec as libc::c_long,
}
}