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
 */

/// A realization of the timer based on the Linux compat `timerfd`.
#[cfg(feature = "bsd_use_timerfd")]
pub mod timer_fd_bsd;

/// A realization of the timer based on the KQueue.
#[cfg(not(feature = "bsd_use_timerfd"))]
pub mod timer_kqueue_fd_bsd;

/// A polling using poll.
#[cfg(feature = "bsd_use_poll")]
pub mod timer_poll;

#[cfg(feature = "bsd_use_poll")]
pub use timer_poll::TimerEventWatch;

/// A polling using KQueue.
#[cfg(not(feature = "bsd_use_poll"))]
pub mod timer_kqueue_bsd;

#[cfg(not(feature = "bsd_use_poll"))]
pub use timer_kqueue_bsd::TimerEventWatch;

#[cfg(not(feature = "bsd_use_timerfd"))]
pub mod kqueue_itimerspecs_specific
{
    use nix::{errno::Errno, sys::event::{EvFlags, EventFilter, FilterFlag, KEvent}};

    use crate::{portable_err, timer_portable::{portable_error::TimerPortableErr, timer::ModeTimeType, TimerExpMode, TimerSetTimeFlags}};


    /// A temporary solution interfacing KQueue with timerfd which uses `itimerspec`.
    #[allow(non_camel_case_types)]
    #[derive(Clone, Debug)]
    pub(crate) struct itimerspeckenent
    {
        /// A KEvent to add now.
        pub it_value: KEvent,

        /// A prepared timeout and flags to set for the periodic with delay timer.
        pub it_delay_int: Option<(i64, FilterFlag)>,
    }

    impl itimerspeckenent
    {
        /// Tells if this is a interval with delay.
        pub(crate) 
        fn is_interv_with_delay(&self) -> bool
        {
            return self.it_delay_int.is_some();
        }
    }

    impl From<TimerSetTimeFlags> for FilterFlag
    {
        fn from(value: TimerSetTimeFlags) -> Self 
        {
            if value.intersects(TimerSetTimeFlags::TFD_TIMER_ABSTIME) == true
            {
                return Self::from_bits_retain(0x00000010);
            }

            return Self::empty();
        }
    }

    /// Converts the `M` [ModeTimeType] into the KEvent timeout format and
    /// KEvent flags.
    pub(crate) 
    fn from_timeout<M: ModeTimeType>(timeout: &M) -> (i64, FilterFlag)
    {
        // try to save the time resolution
        let (data, fflags) = 
            timeout
                .get_sec()
                .checked_mul(1_000_000_000)
                .map(|val| 
                    {
                        let ret_val = 
                            val
                                .checked_add(timeout.get_nsec())
                                .or(Some(val))
                                .unwrap();

                        (ret_val, FilterFlag::NOTE_NSECONDS)
                    }
                )
                .or(
                    timeout
                        .get_sec()
                        .checked_mul(1_000_000)
                        .map(|val| 
                            {
                                let ret_val = 
                                    timeout
                                        .get_nsec()
                                        .checked_div(1_000_000)
                                        .and_then(|v| val.checked_add(v))
                                        .or(Some(val))
                                        .unwrap();

                                (ret_val, FilterFlag::NOTE_USECONDS)
                            }
                        )
                        .or(
                            timeout
                                .get_sec()
                                .checked_mul(1_000)
                                .map(|val| 
                                    {
                                        let ret_val = 
                                            timeout
                                                .get_nsec()
                                                .checked_div(1_000)
                                                .and_then(|v| val.checked_add(v))
                                                .or(Some(val))
                                                .unwrap();

                                        (ret_val, FilterFlag::NOTE_MSECONDS)
                                    }
                                )
                                .or(Some((timeout.get_sec(), FilterFlag::NOTE_SECONDS)))
                        )
                )
                .unwrap(); // panic safe because always ends with Some()

        return (data, fflags);
    }


    impl<TIMERTYPE: ModeTimeType> TryFrom<TimerExpMode<TIMERTYPE>> for itimerspeckenent
    {
        type Error = TimerPortableErr;
    
        fn try_from(value: TimerExpMode<TIMERTYPE>) -> Result<Self, Self::Error> 
        {
            return (&value).try_into();
        }
    }

    /// Converts from the reference to [TimerExpMode] of the `TIMERTYPE` into the 
    /// [itimerspec].
    impl<TIMERTYPE: ModeTimeType> TryFrom<&TimerExpMode<TIMERTYPE>> for itimerspeckenent
    {
        type Error = TimerPortableErr;
    
        fn try_from(value: &TimerExpMode<TIMERTYPE>) -> Result<Self, Self::Error> 
        {
             match value
            {
                TimerExpMode::None => 
                    portable_err!(Errno::EINVAL, "null timer values"),
                TimerExpMode::OneShot { timeout } => 
                {
                    let (data, fflags) = from_timeout(timeout);

                    let fflags = fflags | TIMERTYPE::get_flags().into();

                    let ev = 
                        KEvent::new(
                            0, 
                            EventFilter::EVFILT_TIMER, 
                            EvFlags::EV_ADD | EvFlags::EV_ENABLE | EvFlags::EV_ONESHOT, 
                            fflags, 
                            data as isize, 
                            0
                        );

                    return Ok(
                        itimerspeckenent
                        {
                            it_value: ev,
                            it_delay_int: None,
                        }
                    ); 
                },
                TimerExpMode::IntervalDelayed { delay_tm, interv_tm } => 
                {
                    let (data_delay, fflags) = from_timeout(delay_tm);

                    let fflags = fflags | TIMERTYPE::get_flags().into();

                    let ev_delay = 
                        KEvent::new(
                            0, 
                            EventFilter::EVFILT_TIMER, 
                            EvFlags::EV_ADD | EvFlags::EV_ENABLE | EvFlags::EV_ONESHOT, 
                            fflags, 
                            data_delay as isize, 
                            0
                        );
                    
                    // first interval with delay
                    let (data_interv, fflags) = from_timeout(interv_tm);

                    let fflags = fflags | TIMERTYPE::get_flags().into();


                    /*let ev_inerv = 
                        KEvent::new(
                            0, 
                            EventFilter::EVFILT_TIMER, 
                            EvFlags::EV_ADD | EvFlags::EV_ENABLE, 
                            fflags, 
                            data as isize, 
                            0
                        );*/

                    return Ok(
                        itimerspeckenent
                        {
                            it_value: ev_delay,
                            it_delay_int: Some((data_interv, fflags)),
                        }
                    ); 
                },
                TimerExpMode::Interval { interv_tm } => 
                {
                    let (data, fflags) = from_timeout(interv_tm);

                    let fflags = fflags | TIMERTYPE::get_flags().into();

                    let ev = 
                        KEvent::new(
                            0, 
                            EventFilter::EVFILT_TIMER, 
                            EvFlags::EV_ADD | EvFlags::EV_ENABLE, 
                            fflags, 
                            data as isize, 
                            0
                        );

                    return Ok(
                        itimerspeckenent
                        {
                            it_value: ev,
                            it_delay_int: None,
                        }
                    ); 
                },
            }
        }
    }

    #[cfg(test)]
    mod tests
    {
        use super::*;

        #[test]
        fn test_itimerspeckenent_1()
        {
            let fflags: FilterFlag = (TimerSetTimeFlags::TFD_TIMER_ABSTIME | TimerSetTimeFlags::TFD_TIMER_CANCEL_ON_SET).into();

            assert_eq!(fflags.intersects(FilterFlag::from_bits_retain(0x00000010)), true);

            let fflags: FilterFlag = TimerSetTimeFlags::empty().into();
            
            assert_eq!(fflags.is_empty(), true);
        }
    }
}