extern crate libc;
use std::os::unix::prelude::*;
use std::time::Duration;
use std::io::Result as IoResult;
use std::io::ErrorKind;
extern "C" {
fn timerfd_create(clockid: libc::c_int, flags: libc::c_int) -> RawFd;
fn timerfd_settime(fd: RawFd, flags: libc::c_int,
new_value: *const itimerspec, old_value: *mut itimerspec) -> libc::c_int;
fn timerfd_gettime(fd: RawFd, curr_value: *mut itimerspec) -> libc::c_int;
}
static TFD_CLOEXEC: libc::c_int = 0o2000000;
static TFD_NONBLOCK: libc::c_int = 0o0004000;
mod structs;
use structs::itimerspec;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TimerState {
Disarmed,
Oneshot(Duration),
Periodic {
current: Duration,
interval: Duration,
}
}
pub struct TimerFd(RawFd);
fn neg_is_err(i: libc::c_int) -> IoResult<libc::c_int> {
if i >= 0 {
Ok(i)
} else {
Err(std::io::Error::last_os_error())
}
}
impl TimerFd {
pub fn new_custom(realtime_clock: bool, nonblocking: bool, cloexec: bool) -> IoResult<TimerFd> {
let clock = if realtime_clock { libc::CLOCK_REALTIME } else { libc::CLOCK_MONOTONIC };
let mut flags = 0;
if nonblocking {
flags |= TFD_NONBLOCK;
}
if cloexec {
flags |= TFD_CLOEXEC;
}
let fd = neg_is_err(unsafe { timerfd_create(clock, flags) })?;
Ok(TimerFd(fd))
}
pub fn new() -> IoResult<TimerFd> {
TimerFd::new_custom(false, false, false)
}
pub fn set_state(&mut self, state: TimerState) -> TimerState {
let mut old = itimerspec::null();
let new: itimerspec = state.into();
neg_is_err(unsafe { timerfd_settime(self.0, 0, &new, &mut old) })
.expect("Looks like timerfd_settime failed in some undocumented way");
old.into()
}
pub fn get_state(&self) -> TimerState {
let mut state = itimerspec::null();
neg_is_err(unsafe { timerfd_gettime(self.0, &mut state) })
.expect("Looks like timerfd_gettime failed in some undocumented way");
state.into()
}
pub fn read(&mut self) -> u64 {
const BUFSIZE: usize = 8;
let mut buffer: u64 = 0;
let bufptr: *mut _ = &mut buffer;
loop {
let res = unsafe { libc::read(self.0, bufptr as *mut libc::c_void, BUFSIZE) };
match res {
8 => {
assert!(buffer != 0);
return buffer;
}
-1 => {
let err = std::io::Error::last_os_error();
match err.kind() {
ErrorKind::WouldBlock => return 0,
ErrorKind::Interrupted => (),
_ => panic!("Unexpected read error: {}", err),
}
}
_ => unreachable!(),
}
}
}
}
impl AsRawFd for TimerFd {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl Drop for TimerFd {
fn drop(&mut self) {
unsafe {
libc::close(self.0);
}
}
}