use std::
{
borrow::Cow,
fmt,
os::{fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd}, unix::prelude::RawFd},
task::Poll
};
use crate::
{
timer_portable::
{
timer::ModeTimeType, AsTimerId, TimerExpMode, TimerId, UnixFd
},
AbsoluteTime
};
use nix::
{
errno::Errno, fcntl::{self, FcntlArg, OFlag}, libc::{self, itimerspec}
};
use crate::
{
map_portable_err,
portable_err,
timer_portable::
{
portable_error::TimerPortResult,
TimerFlags,
TimerType
},
FdTimerCom,
TimerReadRes
};
use crate::timer_portable::timer::FdTimerRead;
#[derive(Debug)]
pub struct TimerFdInternal
{
label: Cow<'static, str>,
timer_fd: OwnedFd,
}
impl fmt::Display for TimerFdInternal
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.timer_fd.as_fd().as_raw_fd())
}
}
impl AsFd for TimerFdInternal
{
fn as_fd(&self) -> BorrowedFd<'_>
{
return self.timer_fd.as_fd();
}
}
impl AsRawFd for TimerFdInternal
{
fn as_raw_fd(&self) -> RawFd
{
return self.timer_fd.as_raw_fd();
}
}
impl Eq for TimerFdInternal {}
impl PartialEq for TimerFdInternal
{
fn eq(&self, other: &Self) -> bool
{
return self.timer_fd.as_fd().as_raw_fd() == other.timer_fd.as_fd().as_raw_fd();
}
}
impl PartialEq<RawFd> for TimerFdInternal
{
fn eq(&self, other: &RawFd) -> bool
{
return self.timer_fd.as_fd().as_raw_fd() == *other;
}
}
impl PartialEq<str> for TimerFdInternal
{
fn eq(&self, other: &str) -> bool
{
return self.label == other;
}
}
impl AsRef<str> for TimerFdInternal
{
fn as_ref(&self) -> &str
{
return &self.label;
}
}
impl UnixFd for TimerFdInternal
{
}
impl AsTimerId for TimerFdInternal
{
fn as_timer_id(&self) -> TimerId
{
return TimerId::from(self.timer_fd.as_raw_fd());
}
}
impl Ord for TimerFdInternal
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering
{
return self.timer_fd.as_fd().as_raw_fd().cmp(&other.timer_fd.as_fd().as_raw_fd());
}
}
impl PartialOrd for TimerFdInternal
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>
{
return Some(self.cmp(other));
}
}
impl FdTimerRead for TimerFdInternal
{
fn read(&self) -> TimerPortResult<TimerReadRes<u64>>
{
let mut timer_overfl = [0u8; size_of::<u64>()];
loop
{
let ret =
nix::unistd::read(&self.timer_fd, &mut timer_overfl);
if let Ok(_) = ret
{
let overl: u64 = u64::from_ne_bytes(timer_overfl);
return Ok(TimerReadRes::Ok(overl));
}
else if let Err(Errno::EINTR) = ret
{
continue;
}
else if let Err(Errno::EAGAIN) = ret
{
return Ok(TimerReadRes::WouldBlock);
}
else if let Err(Errno::ECANCELED) = ret
{
return Ok(TimerReadRes::Cancelled);
}
else if let Err(e) = ret
{
portable_err!(e, "read timer overflow error for timer: '{}'", self.label)
}
}
}
}
impl FdTimerCom for TimerFdInternal
{
fn new(label: Cow<'static, str>, timer_type: TimerType, timer_flags: TimerFlags) -> TimerPortResult<Self>
{
let timer_fd =
unsafe { libc::timerfd_create(timer_type.into(), timer_flags.bits())};
return Ok(
Self
{
label:
label,
timer_fd:
unsafe { OwnedFd::from_raw_fd(timer_fd) },
}
);
}
fn set_time<TIMERTYPE: ModeTimeType>(&self, timer_exp: TimerExpMode<TIMERTYPE>) -> TimerPortResult<()>
{
let flags = TIMERTYPE::get_flags().bits();
let timer_value: itimerspec = timer_exp.into();
let res =
unsafe
{
libc::timerfd_settime(self.timer_fd.as_raw_fd(), flags,
&timer_value, core::ptr::null_mut())
};
if res == -1
{
portable_err!(Errno::last(), "can not init timer: '{}'", self.label);
}
return Ok(());
}
fn unset_time(&self) -> TimerPortResult<()>
{
let mut timer_value: itimerspec = unsafe { std::mem::zeroed() };
let res =
unsafe
{
libc::timerfd_gettime(self.timer_fd.as_raw_fd(),
&mut timer_value as *mut _ as *mut itimerspec)
};
if res == -1
{
portable_err!(Errno::last(), "can not timerfd_gettime for timer: '{}'", self.label);
}
let timer_mode = TimerExpMode::<AbsoluteTime>::from(timer_value);
if TimerExpMode::<AbsoluteTime>::reset() == timer_mode
{
return Ok(());
}
let timer_value: itimerspec = TimerExpMode::<AbsoluteTime>::reset().into();
let res =
unsafe
{
libc::timerfd_settime(self.timer_fd.as_raw_fd(), 0,
&timer_value, core::ptr::null_mut())
};
if res == -1
{
portable_err!(Errno::last(), "can not unset timer: '{}'", self.label);
}
return Ok(());
}
fn set_nonblocking(&self, flag: bool) -> TimerPortResult<()>
{
let mut fl =
OFlag::from_bits_retain(
fcntl::fcntl(&self.timer_fd, FcntlArg::F_GETFL)
.map_err(|e|
map_portable_err!(e, "timer: '{}', fcntl F_GETFL failed", self.label)
)?
);
fl.set(OFlag::O_NONBLOCK, flag);
fcntl::fcntl(&self.timer_fd, FcntlArg::F_SETFL(fl))
.map_err(|e|
map_portable_err!(e, "timer: '{}', fcntl F_SETFL failed", self.label)
)?;
return Ok(());
}
fn is_nonblocking(&self) -> TimerPortResult<bool>
{
let fl =
OFlag::from_bits_retain(
fcntl::fcntl(&self.timer_fd, FcntlArg::F_GETFL)
.map_err(|e|
map_portable_err!(e, "timer: '{}', fcntl F_GETFL failed", self.label)
)?
);
return Ok(fl.intersects(OFlag::O_NONBLOCK));
}
}
impl Future for &TimerFdInternal
{
type Output = TimerPortResult<TimerReadRes<u64>>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output>
{
if self.is_nonblocking()?== false
{
return Poll::Ready(Err(map_portable_err!(Errno::EINVAL, "timer fd is in blocking mode")));
}
let res = self.read();
if let Ok(TimerReadRes::WouldBlock) = res
{
cx.waker().wake_by_ref();
return Poll::Pending;
}
else
{
return Poll::Ready(res);
}
}
}
impl Future for TimerFdInternal
{
type Output = TimerPortResult<TimerReadRes<u64>>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output>
{
if self.is_nonblocking()? == false
{
return Poll::Ready(Err(map_portable_err!(Errno::EINVAL, "timer fd is in blocking mode")));
}
let res = self.read();
if let Ok(TimerReadRes::WouldBlock) = res
{
cx.waker().wake_by_ref();
return Poll::Pending;
}
else
{
return Poll::Ready(res);
}
}
}