timer-deque-rs 0.8.0

A OS based timer and timer queue which implements timeout queues of different types.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/*-
 * 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::{Borrow, Cow}, ffi::CString, fmt, io::{self, ErrorKind}, mem, os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, OwnedHandle, RawHandle}};

use bitflags::bitflags;
use nt_time::FileTime;
use windows::{Win32::{Foundation::HANDLE, System::Threading::{CreateEventA, SetEvent}}, core::{Error, PCSTR, PCWSTR}};

use crate::{AbsoluteTime, RelativeTime, TimerFd, error::{TimerErrorType, TimerResult}, map_portable_err, map_timer_err, timer_portable::{TimerExpMode, portable_error::TimerPortableErr, timer::{ModeTimeType, TimerId}}};

/// A timer imlementation.
pub mod timer_fd_windows;


pub mod timer_poll;


impl From<RawHandle> for TimerId
{
    fn from(value: RawHandle) -> Self 
    {
        return Self(value as usize);
    }
}

/*
impl From<&TimerFd> for TimerId
{
    fn from(value: &TimerFd) -> Self 
    {
        return Self(value.as_raw_handle() as usize);
    }
}*/


impl PartialEq<RawHandle> for TimerId
{
    fn eq(&self, other: &RawHandle) -> bool 
    {
        return self.0 == *other as usize;
    }
}

// ----- EVENTFD implementation ------

/// An implementation of the Linuxe's `eventfd`. The name is selected
/// for compat. But functionality is a bit  different.
#[derive(Debug)]
pub(crate) struct EventFd
{
    handler: OwnedHandle
}

impl AsRawHandle for EventFd
{
    fn as_raw_handle(&self) -> RawHandle 
    {
        return self.handler.as_raw_handle()
    }
}

impl AsHandle for EventFd
{
    fn as_handle(&self) -> BorrowedHandle<'_> 
    {
        return self.handler.as_handle();
    }
}

impl EventFd
{
    pub(crate) 
    fn new(label: Cow<'static, str>) -> TimerResult<Self>
    {
        let label_cstr = 
            CString::new(label.as_ref())
                .map_err(|e|
                    map_timer_err!(TimerErrorType::Conversion, "CString error '{}' converting '{}'", 
                        e, label)
                )?;
        
        let hndl = 
            unsafe
            {
                CreateEventA(None, false, false, 
                    PCSTR::from_raw( mem::transmute(label_cstr.as_ptr())))
                        .map_err(|e|
                            map_timer_err!(TimerErrorType::EPoll(e), "CreateEventA failed")
                        )?
            };

        return Ok(Self{ handler: unsafe { OwnedHandle::from_raw_handle(hndl.0) } })
    }

    pub(crate) 
    fn write(&self, _val: usize) -> Result<(), Error>
    {
        return 
            unsafe 
            {
                SetEvent(HANDLE(self.handler.as_raw_handle()))
            };
    }
}

// ------------/ EVENT FD /-----------------


/// In order to leave a 100% compat with the UNIX code,
/// this structure was left as is. But `Windows` uses 
/// different `since EPOCH` time calculation method,
/// when coverting into and from, this should be taken into account.
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct timespec 
{
    pub tv_sec: i64,
    pub tv_nsec: i64,
}

/// In order to leave a 100% compat with the UNIX code,
/// this structure was left as is. But `Windows` uses 
/// different `since EPOCH` time calculation method,
/// when coverting into and from, this should be taken into account.
/// 
/// This struct is returned during a conversion from [TimerExpMode]
/// where all necessary adjustments are performed. 
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct itimerspec 
{
    /// An interval time in ms.
    pub it_interval: i32,

    /// A oneshot, delay value in ns (100ns step)
    pub it_value: i64,
}


impl From<timespec> for RelativeTime
{
    fn from(time_spec: timespec) -> Self 
    {
        return   
            Self::new_time(time_spec.tv_sec, time_spec.tv_nsec);
    }
}


impl From<timespec> for AbsoluteTime
{
    fn from(time_spec: timespec) -> Self 
    {
        return 
            unsafe 
            { 
                Self::new_time_unchecked(time_spec.tv_sec, time_spec.tv_nsec) 
            };
    }
}

/*impl<T: Sized + fmt::Debug + fmt::Display + Clone + Eq + PartialEq> From<io::Error> for TimerReadRes<T>
{
    fn from(value: io::Error) -> Self 
    {
        if let Some(errn) = value.raw_os_error()
        {
            todo!(" From<io::Error> for TimerReadRes<T>");
            /*if errn == ECANCELED
            {
                return Self::Cancelled;
            }
            else if errn == EWOULDBLOCK
            {
                return Self::WouldBlock;
            }*/
        }

        return Self::Cancelled;
    }
}
*/

/// A timer type (**not appliciable for Windows target**)
#[allow(non_camel_case_types)]
#[repr(i32)]
#[derive(Debug)]
pub enum TimerType
{
    /// A settable system-wide real-time clock.
    CLOCK_REALTIME = 1,

    /// A nonsettable monotonically increasing clock that measures  time
    /// from some unspecified point in the past that does not change af‐
    /// ter system startup.
    CLOCK_MONOTONIC = 2,
}


bitflags! {     
    /// Flags controling the type of the timer type.  
    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] 
    pub struct TimerFlags: i32  
    {     
        /// Set the O_NONBLOCK file status flag on the open file  de‐
        /// scription  (see  open(2)) referred to by the new file de‐
        /// scriptor.  Using this flag saves extra calls to  fcntl(2)
        /// to achieve the same result.
        const TFD_NONBLOCK = 1;

        /// If this parameter is SET, the timer handle cannot be inherited 
        /// by child processes. **NOT APPLICIABLE FOR WINDOWS**
        const TFD_CLOEXEC = 2;
    }
}


bitflags! {     
    /// A bit mask that can include the values.
    #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] 
    pub struct TimerSetTimeFlags: i32  
    {     
        /// Sets the timer to count to absolute time values.
        const TFD_TIMER_ABSTIME = 1;

        /// On Windows has no effect.
        const TFD_TIMER_CANCEL_ON_SET = 2;
    }
}

/// A declaration of the sub-super trait for `Windows`. 
pub trait UnixFd: AsHandle + AsRawHandle + PartialEq<RawHandle> {}

// ---- TimerFd ------

/// Implementation for the TimrFD.
impl UnixFd for TimerFd {}

impl PartialEq<RawHandle> for TimerFd
{
    fn eq(&self, other: &RawHandle) -> bool 
    {
        return self.as_raw_handle().eq(other);
    }
}

impl AsHandle for TimerFd
{
    #[inline]
    fn as_handle(&self) -> BorrowedHandle<'_> 
    {
        return self.get_timer().as_handle();
    }
}

impl AsRawHandle for TimerFd
{
    #[inline]
    fn as_raw_handle(&self) -> RawHandle 
    {
        return self.get_timer().as_raw_handle();
    }
}

// -----------------------

/// ----- TryFrom for TimerExpMode<TIMERTYPE> ----------
impl<TIMERTYPE: ModeTimeType> TryFrom<TimerExpMode<TIMERTYPE>> for itimerspec
{
    type Error = TimerPortableErr;

    fn try_from(value: TimerExpMode<TIMERTYPE>) -> Result<Self, Self::Error> 
    {
        return (&value).try_into();
    }
}

impl<TIMERTYPE: ModeTimeType> TryFrom<&TimerExpMode<TIMERTYPE>> for itimerspec
{
    type Error = TimerPortableErr;

    /// Attempts to convert from the [TimerExpMode] into the [itimerspec].
    /// 
    /// If [FileTime::from_unix_time] failes to convert the provided data into
    /// [FileTime] an error type [Error::empty()] is returned and error description
    /// is attached.
    fn try_from(value: &TimerExpMode<TIMERTYPE>) -> Result<Self, Self::Error> 
    {
        match value
        {
            TimerExpMode::None => 
                return Ok(
                    itimerspec 
                    {
                        it_interval: 0,
                        it_value: 0
                    }
                ),
            TimerExpMode::OneShot{ timeout} =>
            {
                if TIMERTYPE::get_flags().intersects(TimerSetTimeFlags::TFD_TIMER_ABSTIME) == true
                {
                    let it_value = 
                        FileTime::from_unix_time(timeout.get_sec() as i64, timeout.get_nsec() as u32)
                            .map_err(|e|
                                map_portable_err!(Error::empty(), "cannot convert unix time to filetime, err: '{}'", e)
                            )?
                            .to_raw() as i64;

                    return Ok(
                        itimerspec 
                        {
                            it_interval: 0,
                            it_value: it_value 
                        }
                    );
                }
                else
                {
                    return Ok(
                        itimerspec 
                        {
                            it_interval: 0,
                            it_value: -((timeout.get_sec() * RelativeTime::MAX_NS + timeout.get_nsec()) / 100)
                        }
                    );
                }
            },
            TimerExpMode::IntervalDelayed{ delay_tm, interv_tm } => 
            {
                if TIMERTYPE::get_flags().intersects(TimerSetTimeFlags::TFD_TIMER_ABSTIME) == true
                {
                    // not possible, because interval is only possible for relative values
                    
                    let it_value = 
                        FileTime::from_unix_time(delay_tm.get_sec() as i64, delay_tm.get_nsec() as u32)
                            .map_err(|e|
                                map_portable_err!(Error::empty(), "cannot convert unix time to filetime, err: '{}'", e)
                            )?
                            .to_raw() as i64;

                    return Ok(
                        itimerspec 
                        {
                            // in ms
                            it_interval: 
                                (((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
                            it_value: 
                                it_value
                        }
                    );
                }
                else
                {
                    return Ok(
                        itimerspec 
                        {
                            // in ms
                            it_interval: 
                                (((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
                            it_value: 
                                -((delay_tm.get_sec() * RelativeTime::MAX_NS + delay_tm.get_nsec()) / 100)
                        }
                    );
                }
            },
            TimerExpMode::Interval{ interv_tm } => 
            {
                if TIMERTYPE::get_flags().intersects(TimerSetTimeFlags::TFD_TIMER_ABSTIME) == true
                {
                    // not possible, because interval is only possible for relative values

                    let it_value = 
                        FileTime::from_unix_time(interv_tm.get_sec() as i64, interv_tm.get_nsec() as u32)
                            .map_err(|e|
                                map_portable_err!(Error::empty(), "cannot convert unix time to filetime, err: '{}'", e)
                            )?
                            .to_raw() as i64;

                    return Ok(
                        itimerspec 
                        {
                            // in ms
                            it_interval: 
                                (((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
                            it_value: 
                                it_value
                        }
                    );
                }
                else
                {
                    return Ok(
                        itimerspec 
                        {
                            // in ms
                            it_interval: 
                                (((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 1_000_000) & 0xFFFFFFFF) as i32,
                            it_value: 
                                -((interv_tm.get_sec() * RelativeTime::MAX_NS + interv_tm.get_nsec()) / 100)
                        }
                    );
                }
            }
                
        }
    }
}

// ----------------