use crate::sys::time::TimeSpec;
use crate::unistd::read;
use crate::{errno::Errno, Error, Result};
use bitflags::bitflags;
use libc::c_int;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
#[derive(Debug, Clone, Copy)]
pub struct TimerFd {
fd: RawFd,
}
impl AsRawFd for TimerFd {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
impl FromRawFd for TimerFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
TimerFd { fd }
}
}
libc_enum! {
#[repr(i32)]
pub enum ClockId {
CLOCK_REALTIME,
CLOCK_MONOTONIC,
CLOCK_BOOTTIME,
CLOCK_REALTIME_ALARM,
CLOCK_BOOTTIME_ALARM,
}
}
libc_bitflags! {
pub struct TimerFlags: c_int {
TFD_NONBLOCK;
TFD_CLOEXEC;
}
}
bitflags! {
pub struct TimerSetTimeFlags: libc::c_int {
const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
}
}
#[derive(Debug, Clone, Copy)]
struct TimerSpec(libc::itimerspec);
impl TimerSpec {
pub fn none() -> Self {
Self(libc::itimerspec {
it_interval: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
it_value: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
})
}
}
impl AsRef<libc::itimerspec> for TimerSpec {
fn as_ref(&self) -> &libc::itimerspec {
&self.0
}
}
impl From<Expiration> for TimerSpec {
fn from(expiration: Expiration) -> TimerSpec {
match expiration {
Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
it_interval: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
it_value: *t.as_ref(),
}),
Expiration::IntervalDelayed(start, interval) => TimerSpec(libc::itimerspec {
it_interval: *interval.as_ref(),
it_value: *start.as_ref(),
}),
Expiration::Interval(t) => TimerSpec(libc::itimerspec {
it_interval: *t.as_ref(),
it_value: *t.as_ref(),
}),
}
}
}
impl From<TimerSpec> for Expiration {
fn from(timerspec: TimerSpec) -> Expiration {
match timerspec {
TimerSpec(libc::itimerspec {
it_interval:
libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
it_value: ts,
}) => Expiration::OneShot(ts.into()),
TimerSpec(libc::itimerspec {
it_interval: int_ts,
it_value: val_ts,
}) => {
if (int_ts.tv_sec == val_ts.tv_sec) && (int_ts.tv_nsec == val_ts.tv_nsec) {
Expiration::Interval(int_ts.into())
} else {
Expiration::IntervalDelayed(val_ts.into(), int_ts.into())
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Expiration {
OneShot(TimeSpec),
IntervalDelayed(TimeSpec, TimeSpec),
Interval(TimeSpec),
}
impl TimerFd {
pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
Errno::result(unsafe { libc::timerfd_create(clockid as i32, flags.bits()) })
.map(|fd| Self { fd })
}
pub fn set(&self, expiration: Expiration, flags: TimerSetTimeFlags) -> Result<()> {
let timerspec: TimerSpec = expiration.into();
Errno::result(unsafe {
libc::timerfd_settime(
self.fd,
flags.bits(),
timerspec.as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
pub fn get(&self) -> Result<Option<Expiration>> {
let mut timerspec = TimerSpec::none();
let timerspec_ptr: *mut libc::itimerspec = &mut timerspec.0;
Errno::result(unsafe { libc::timerfd_gettime(self.fd, timerspec_ptr) }).map(|_| {
if timerspec.0.it_interval.tv_sec == 0
&& timerspec.0.it_interval.tv_nsec == 0
&& timerspec.0.it_value.tv_sec == 0
&& timerspec.0.it_value.tv_nsec == 0
{
None
} else {
Some(timerspec.into())
}
})
}
pub fn unset(&self) -> Result<()> {
Errno::result(unsafe {
libc::timerfd_settime(
self.fd,
TimerSetTimeFlags::empty().bits(),
TimerSpec::none().as_ref(),
std::ptr::null_mut(),
)
})
.map(drop)
}
pub fn wait(&self) -> Result<()> {
loop {
if let Err(e) = read(self.fd, &mut [0u8; 8]) {
match e {
Error::Sys(Errno::EINTR) => continue,
_ => return Err(e),
}
} else {
break;
}
}
Ok(())
}
}