timer-deque-rs 0.8.0

A OS based timer and timer queue which implements timeout queues of different types.
Documentation
/*-
 * timer-deque-rs - a Rust crate which provides timer and timer queues based on target OS
 *  functionality.
 * 
 * Copyright (C) 2025 Aleksandr Morozov alex@nixd.org
 *  4neko.org alex@4neko.org
 * 
 * The timer-rs crate can be redistributed and/or modified
 * under the terms of either of the following licenses:
 *
 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
 *                     
 *   2. The MIT License (MIT)
 *                     
 *   3. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
 */
 


use std::borrow::Cow;
use std::{fmt};
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
use std::os::unix::prelude::RawFd;
use std::task::Poll;

use nix::fcntl::{FcntlArg, OFlag};
use nix::{fcntl, libc};
use nix::libc::itimerspec;


use crate::nix::errno::Errno;
use crate::timer_portable::portable_error::TimerPortResult;
use crate::timer_portable::timer::{FdTimerRead, ModeTimeType, TimerReadRes};
use crate::timer_portable::unix::{TimerFlags, TimerType, UnixFd};
use crate::{map_portable_err, portable_err, AbsoluteTime};
use crate::timer_portable::
{
    AsTimerId, FdTimerCom, TimerExpMode, TimerId
};


/// A `synchronious` timer instance based on the Linux's `timerfd_create`.
/// 
/// A `EPoll` or `select` can be used to monitor the instance. Also the 
/// instance implements [Future] which can be polled from async code. Or 
/// used with tokio's `AsyncFd` or any other equivalent.
/// 
/// It can be used with provided `poll.rs` event notification system wrapper.
#[derive(Debug)]
pub struct TimerFdInternal
{
    /// A timer's label.
    label: Cow<'static, str>,

    /// A timer's FD
    timer_fd: OwnedFd,
}

impl fmt::Display for TimerFdInternal
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
    {
        write!(f, "{}", self.timer_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_raw_fd() == other.timer_fd.as_raw_fd();
    }
}

impl PartialEq<RawFd> for TimerFdInternal
{
    fn eq(&self, other: &RawFd) -> bool 
    {
        return self.timer_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_raw_fd().cmp(&other.timer_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
{
    #[inline]
    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
            {
                // would block
                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>
    where 
        Self: Sized 
    {
        let timer_fd = 
            unsafe { libc::timerfd_create(timer_type.into(), timer_flags.bits()) };

        if timer_fd == -1
        {
            portable_err!(Errno::last(), "timer: '{}', timerfd_create failed with error", label);
        }

        return Ok(
            Self
            { 
                label: 
                    label,
                timer_fd: 
                    unsafe { OwnedFd::from_raw_fd(timer_fd) }, 
                /*poll_bind: 
                    RwLock::new(None),*/
            }
        );
    }

    fn set_time<TIMERTYPE: ModeTimeType>(&self, timer_exp: TimerExpMode<TIMERTYPE>) -> TimerPortResult<()>
    {
        // the `timer_exp` is in the [TimerExpMode] so there is no need to check if it is
        // valid

        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<()>
    {
        // this is safe because it is primitive without Rust's std
        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
        {
            // already disarmed
            return Ok(());
        }

        // disarm timer

        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 TimerFdInternal
{

}

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> 
    {
        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> 
    {
        let res = self.read();

        if let Ok(TimerReadRes::WouldBlock) = res
        {
            cx.waker().wake_by_ref();

            return Poll::Pending;
        }
        else
        {
            return Poll::Ready(res);
        } 
    }
}