timer-deque-rs 0.7.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, 
    fmt, 
    os::{fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd}, unix::prelude::RawFd}, 
    sync::{Arc, RwLock, TryLockResult, Weak}, 
    task::Poll
};

use crate::
{
    timer_portable::
    {
        timer::{itimerspec, ModeTimeType}, 
        TimerExpMode
    }, 
    AbsoluteTime
};

use nix::
{
    errno::Errno, fcntl::{self, FcntlArg, OFlag}, libc::{self}
};

use crate::
{
    map_portable_err, 
    portable_err, 
    timer_portable::
    {
        portable_error::TimerPortResult,
        TimerFlags, 
        TimerType
    }, 
    FdTimerCom, 
    TimerReadRes
};
use crate::timer_portable::timer::FdTimerRead;

/// An async timer implementation based on the TimrFD for the BSD based
/// systems.
#[derive(Debug)]
pub struct TimerFdInternal
{
    /// A timer's label.
    label: Cow<'static, str>,

    /// A TimerFD FD.
    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 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);
            /*.map_err(|e|
                map_portable_err!(e, "read timer overflow error for timer: '{}'", self.label)
            )?;*/

            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>
    {
        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<()>
    {
        // 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 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);
        } 
    }
}