use crate::sys::time::timer::TimerSpec;
pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
use crate::unistd::read;
use crate::{errno::Errno, Result};
use libc::c_int;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
#[cfg_attr(linux_android, doc = "[`epoll`](crate::sys::epoll)")]
#[cfg_attr(target_os = "freebsd", doc = "[`kqueue`](crate::sys::event)")]
#[derive(Debug)]
pub struct TimerFd {
fd: OwnedFd,
}
impl AsFd for TimerFd {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl FromRawFd for TimerFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
TimerFd {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
}
}
}
impl From<TimerFd> for OwnedFd {
fn from(value: TimerFd) -> Self {
value.fd
}
}
libc_enum! {
#[repr(i32)]
#[non_exhaustive]
pub enum ClockId {
CLOCK_REALTIME,
CLOCK_MONOTONIC,
CLOCK_BOOTTIME,
#[cfg(linux_android)]
CLOCK_REALTIME_ALARM,
#[cfg(linux_android)]
CLOCK_BOOTTIME_ALARM,
}
}
libc_bitflags! {
pub struct TimerFlags: c_int {
TFD_NONBLOCK;
TFD_CLOEXEC;
}
}
impl TimerFd {
#[doc(alias("timerfd_create"))]
pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
Errno::result(unsafe {
libc::timerfd_create(clockid as i32, flags.bits())
})
.map(|fd| Self {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
})
}
#[doc(alias("timerfd_settime"))]
pub fn set(
&self,
expiration: Expiration,
flags: TimerSetTimeFlags,
) -> Result<()> {
let timerspec: TimerSpec = expiration.into();
Errno::result(unsafe {
libc::timerfd_settime(
self.fd.as_fd().as_raw_fd(),
flags.bits(),
timerspec.as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
#[doc(alias("timerfd_gettime"))]
pub fn get(&self) -> Result<Option<Expiration>> {
let mut timerspec = TimerSpec::none();
Errno::result(unsafe {
libc::timerfd_gettime(
self.fd.as_fd().as_raw_fd(),
timerspec.as_mut(),
)
})
.map(|_| {
if timerspec.as_ref().it_interval.tv_sec == 0
&& timerspec.as_ref().it_interval.tv_nsec == 0
&& timerspec.as_ref().it_value.tv_sec == 0
&& timerspec.as_ref().it_value.tv_nsec == 0
{
None
} else {
Some(timerspec.into())
}
})
}
#[doc(alias("timerfd_settime"))]
pub fn unset(&self) -> Result<()> {
Errno::result(unsafe {
libc::timerfd_settime(
self.fd.as_fd().as_raw_fd(),
TimerSetTimeFlags::empty().bits(),
TimerSpec::none().as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
pub fn wait(&self) -> Result<()> {
while let Err(e) = read(&self.fd, &mut [0u8; 8]) {
if e == Errno::ECANCELED {
break;
}
if e != Errno::EINTR {
return Err(e);
}
}
Ok(())
}
pub unsafe fn from_owned_fd(fd: OwnedFd) -> Self {
Self {
fd
}
}
}