1use std::{borrow::Cow, cmp::Ordering, fmt, io::{self}};
16
17use chrono::{DateTime, Local};
18use nix::libc::{self, ECANCELED, EWOULDBLOCK};
19use bitflags::bitflags;
20
21use crate::timer_portable::portable_error::TimerPortResult;
22
23#[cfg(target_os = "linux")]
24pub use super::linux::timer_fd_linux::*;
25
26#[cfg(any(
27 target_os = "freebsd",
28 target_os = "dragonfly",
29 target_os = "netbsd",
30 target_os = "openbsd",
31 target_os = "macos",
32))]
33pub use super::bsd::timer_kqueue_bsd::*;
34
35
36#[allow(non_camel_case_types)]
38#[derive(Debug)]
39pub enum TimerType
40{
41 CLOCK_REALTIME,
43
44 CLOCK_MONOTONIC,
49
50 CLOCK_BOOTTIME,
58
59 CLOCK_REALTIME_ALARM,
63
64 CLOCK_BOOTTIME_ALARM,
68}
69
70impl Into<libc::clockid_t> for TimerType
71{
72 fn into(self) -> libc::clockid_t
73 {
74 match self
75 {
76 Self::CLOCK_REALTIME => return libc::CLOCK_REALTIME,
77 Self::CLOCK_MONOTONIC => return libc::CLOCK_MONOTONIC,
78 #[cfg(target_os = "linux")]
79 Self::CLOCK_BOOTTIME => return libc::CLOCK_BOOTTIME,
80 #[cfg(target_os = "linux")]
81 Self::CLOCK_REALTIME_ALARM => return libc::CLOCK_REALTIME_ALARM,
82 #[cfg(target_os = "linux")]
83 Self::CLOCK_BOOTTIME_ALARM => return libc::CLOCK_BOOTTIME_ALARM,
84 #[cfg(any(
85 target_os = "freebsd",
86 target_os = "dragonfly",
87 target_os = "netbsd",
88 target_os = "openbsd",
89 target_os = "macos",
90 ))]
91 _ => return libc::CLOCK_REALTIME,
92 }
93 }
94}
95
96#[cfg(target_os = "linux")]
97bitflags! {
98 #[derive(Default)]
100 pub struct TimerFlags: i32
101 {
102 const TFD_NONBLOCK = libc::TFD_NONBLOCK;
107
108 const TFD_CLOEXEC = libc::TFD_CLOEXEC;
112 }
113}
114
115#[cfg(any(
116 target_os = "freebsd",
117 target_os = "dragonfly",
118 target_os = "netbsd",
119 target_os = "openbsd",
120 target_os = "macos",
121))]
122bitflags! {
123 #[derive(Default)]
125 pub struct TimerFlags: i32
126 {
127 const TFD_NONBLOCK = 0;
129
130 const TFD_CLOEXEC = 0;
132 }
133}
134
135#[cfg(target_os = "linux")]
136bitflags! {
137 #[derive(Default)]
139 pub struct TimerSetTimeFlags: i32
140 {
141 const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
145
146 const TFD_TIMER_CANCEL_ON_SET = (1 << 1);
154 }
155}
156
157#[cfg(any(
158 target_os = "freebsd",
159 target_os = "dragonfly",
160 target_os = "netbsd",
161 target_os = "openbsd",
162 target_os = "macos",
163))]
164
165bitflags! {
166 #[derive(Default)]
168 pub struct TimerSetTimeFlags: i32
169 {
170 const TFD_TIMER_ABSTIME = 0;
172
173 const TFD_TIMER_CANCEL_ON_SET = 0;
175 }
176}
177
178#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183pub enum TimerExpMode
184{
185 None,
187
188 OneShot
190 {
191 sec: i64,
193
194 nsec: i64,
196 },
197
198 IntervalDelayed
200 {
201 delay_sec: i64,
203
204 delay_nsec: i64,
206
207 interv_sec: i64,
209
210 interv_nsec: i64
212 },
213
214 Interval
217 {
218 sec: i64,
220
221 nsec: i64,
223 },
224}
225
226impl Ord for TimerExpMode
227{
228 fn cmp(&self, other: &Self) -> Ordering
229 {
230 match (self, other)
231 {
232 (TimerExpMode::None, TimerExpMode::None) =>
233 return Ordering::Equal,
234 (TimerExpMode::OneShot{ sec, nsec }, TimerExpMode::OneShot { sec: sec2, nsec: nsec2 }) =>
235 {
236 let s1 = sec.cmp(sec2);
237 let s2 = nsec.cmp(nsec2);
238
239 return s1.then(s2);
240 },
241 (
242 TimerExpMode::IntervalDelayed
243 { delay_sec, delay_nsec, interv_sec, interv_nsec },
244 TimerExpMode::IntervalDelayed
245 { delay_sec: delay_sec2, delay_nsec: delay_nsec2, interv_sec: interv_sec2, interv_nsec: interv_nsec2 }) =>
246 {
247 return
248 delay_sec.cmp(delay_sec2)
249 .then(delay_nsec.cmp(delay_nsec2)
250 .then(interv_sec.cmp(interv_sec2)
251 .then(interv_nsec.cmp(interv_nsec2))
252 )
253 );
254 },
255 (TimerExpMode::Interval { sec, nsec }, TimerExpMode::Interval { sec: sec2, nsec: nsec2 }) =>
256 {
257 return sec.cmp(sec2).then(nsec.cmp(nsec2));
258 },
259 _ =>
260 panic!("cannot compare different types {} and {}", self, other)
261 }
262 }
263}
264
265impl PartialOrd for TimerExpMode
266{
267 fn partial_cmp(&self, other: &Self) -> Option<Ordering>
268 {
269 return Some(self.cmp(other));
270 }
271}
272
273
274impl TimerExpMode
275{
276 pub
278 fn reset() -> Self
279 {
280 return Self::None;
281 }
282
283 pub
286 fn is_valid(&self) -> bool
287 {
288 match *self
289 {
290 Self::None =>
291 return false,
292 Self::OneShot{ sec, nsec } =>
293 return sec != 0 || nsec != 0,
294 Self::IntervalDelayed{ delay_sec, delay_nsec, interv_sec, interv_nsec} =>
295 return (delay_sec != 0 || delay_nsec != 0) && (interv_sec != 0 || interv_nsec != 0),
296 Self::Interval{ sec, nsec } =>
297 return sec != 0 || nsec != 0
298 }
299 }
300}
301
302impl fmt::Display for TimerExpMode
303{
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
305 {
306 match self
307 {
308 Self::None =>
309 write!(f, "disarmed"),
310 Self::OneShot{ sec, nsec } =>
311 write!(f, "oneshot sec: {} nsec: {}", sec, nsec),
312 Self::IntervalDelayed
313 { delay_sec, delay_nsec, interv_sec, interv_nsec } =>
314 write!(f, "interval sec: {} nsec: {} with delay sec: {} nsec: {}",
315 interv_sec, interv_nsec, delay_sec, delay_nsec),
316 Self::Interval{ sec, nsec } =>
317 write!(f, "interval sec: {} nsec: {}", sec, nsec),
318 }
319 }
320}
321
322#[cfg(any(
323 target_os = "freebsd",
324 target_os = "dragonfly",
325 target_os = "netbsd",
326 target_os = "openbsd",
327 target_os = "macos",
328 target_os = "linux",
329))]
330use nix::libc::{itimerspec, timespec};
331
332#[cfg(target_os = "macos")]
333#[repr(C)]
334pub struct timespec
335{
336 pub tv_sec: libc::time_t,
337 pub tv_nsec: libc::c_long,
338}
339#[cfg(target_os = "macos")]
340#[repr(C)]
341pub struct itimerspec
342{
343 pub it_interval: timespec,
344 pub it_value: timespec,
345}
346
347impl From<DateTime<Local>> for TimerExpMode
348{
349 fn from(value: DateTime<Local>) -> Self
350 {
351 return Self::OneShot { sec: value.timestamp(), nsec: value.timestamp_subsec_nanos() as i64 };
352 }
353}
354
355impl From<itimerspec> for TimerExpMode
356{
357 fn from(value: itimerspec) -> Self
358 {
359 if value.it_interval.tv_sec == 0 && value.it_interval.tv_nsec == 0 &&
360 value.it_value.tv_sec == 0 && value.it_value.tv_nsec == 0
361 {
362 return Self::None;
364 }
365 else if value.it_interval.tv_sec == 0 && value.it_interval.tv_nsec == 0
366 {
367 return Self::OneShot{ sec: value.it_interval.tv_sec, nsec: value.it_interval.tv_nsec };
369 }
370 else if value.it_interval.tv_sec == value.it_value.tv_sec &&
371 value.it_interval.tv_nsec == value.it_value.tv_nsec
372 {
373 return Self::Interval { sec: value.it_value.tv_sec, nsec: value.it_value.tv_nsec };
375 }
376 else
377 {
378 return
380 Self::IntervalDelayed
381 {
382 delay_sec: value.it_value.tv_sec,
383 delay_nsec: value.it_value.tv_nsec,
384 interv_sec: value.it_interval.tv_sec,
385 interv_nsec: value.it_interval.tv_nsec
386 };
387 }
388 }
389}
390
391impl From<TimerExpMode> for itimerspec
392{
393 fn from(value: TimerExpMode) -> Self
394 {
395 match value
396 {
397 TimerExpMode::None =>
398 return
399 itimerspec
400 {
401 it_interval: timespec
402 {
403 tv_sec: 0,
404 tv_nsec: 0,
405 },
406 it_value: timespec
407 {
408 tv_sec: 0,
409 tv_nsec: 0,
410 },
411 },
412 TimerExpMode::OneShot{ sec, nsec} =>
413 return
414 itimerspec
415 {
416 it_interval: timespec
417 {
418 tv_sec: 0,
419 tv_nsec: 0,
420 },
421 it_value: timespec
422 {
423 tv_sec: sec,
424 tv_nsec: nsec,
425 },
426 },
427 TimerExpMode::IntervalDelayed
428 { delay_sec, delay_nsec, interv_sec, interv_nsec } =>
429 return
430 itimerspec
431 {
432 it_interval: timespec
433 {
434 tv_sec: interv_sec,
435 tv_nsec: interv_nsec,
436 },
437 it_value: timespec
438 {
439 tv_sec: delay_sec,
440 tv_nsec: delay_nsec,
441 },
442 },
443 TimerExpMode::Interval{ sec, nsec } =>
444 return
445 itimerspec
446 {
447 it_interval: timespec
448 {
449 tv_sec: sec,
450 tv_nsec: nsec,
451 },
452 it_value: timespec
453 {
454 tv_sec: sec,
455 tv_nsec: nsec,
456 },
457 }
458 }
459 }
460}
461
462#[derive(Debug, Clone, PartialEq, Eq)]
464pub enum TimerReadRes<T: Sized + fmt::Debug + fmt::Display + Clone + Eq + PartialEq>
465{
466 Ok(T),
468
469 Cancelled,
476
477 WouldBlock,
480}
481
482impl TimerReadRes<u64>
483{
484 pub
485 fn ok() -> Self
486 {
487 return Self::Ok(1);
488 }
489}
490
491impl<T: Sized + fmt::Debug + fmt::Display + Clone + Eq + PartialEq> From<io::Error> for TimerReadRes<T>
492{
493 fn from(value: io::Error) -> Self
494 {
495 if let Some(errn) = value.raw_os_error()
496 {
497 if errn == ECANCELED
498 {
499 return Self::Cancelled;
500 }
501 else if errn == EWOULDBLOCK
502 {
503 return Self::WouldBlock;
504 }
505 }
506
507 return Self::Cancelled;
508 }
509}
510
511impl<T: Sized + fmt::Debug + fmt::Display + Clone + Eq + PartialEq> fmt::Display for TimerReadRes<T>
512{
513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
514 {
515 match self
516 {
517 Self::Ok(ovfl) =>
518 write!(f, "OK(overflows:{})", ovfl),
519 Self::Cancelled =>
520 write!(f, "CANCELLED"),
521 Self::WouldBlock =>
522 write!(f, "WOULDBLOCK"),
523 }
524 }
525}
526
527impl<T: Sized + fmt::Debug + fmt::Display + Clone + Eq + PartialEq> TimerReadRes<T>
528{
529 pub
530 fn unwrap(self) -> T
531 {
532 let Self::Ok(t) = self
533 else { panic!("can not unwrap {:?}", self)};
534
535 return t;
536 }
537}
538
539pub trait FdTimerCom
541{
542 fn new(label: Cow<'static, str>, timer_type: TimerType, timer_flags: TimerFlags) -> TimerPortResult<Self>
556 where Self: Sized;
557
558 fn read(&self) -> TimerPortResult<TimerReadRes<u64>>;
561
562 fn read_buf(&self, unfilled: &mut [u8]) -> std::io::Result<usize>;
565
566 fn set_time(&self, flags: TimerSetTimeFlags, timer_exp: TimerExpMode) -> TimerPortResult<()>;
568
569 fn unset_time(&self) -> TimerPortResult<()>;
571}
572
573
574#[cfg(test)]
575mod tests
576{
577
578 use crate::{common, timer_portable::timer::TimerExpMode};
579
580 #[test]
581 fn test_0()
582 {
583 let ts = common::get_current_timestamp();
584
585 let texp1 = TimerExpMode::from(ts);
586
587 assert_eq!(
588 TimerExpMode::OneShot { sec: ts.timestamp(), nsec: ts.timestamp_subsec_nanos() as i64 },
589 texp1
590 );
591
592 }
593
594 #[test]
595 fn test_1()
596 {
597 let ts = common::get_current_timestamp();
598
599 let texp1 = TimerExpMode::from(ts);
600 let texp2 = TimerExpMode::OneShot { sec: ts.timestamp()+1, nsec: ts.timestamp_subsec_nanos() as i64 };
601
602 assert_eq!(texp1 < texp2, true);
603 assert_eq!(texp2 > texp1, true);
604 assert_eq!(texp2 != texp1, true);
605 assert_eq!(texp2 < texp1, false);
606 }
607
608 #[test]
609 fn test_2()
610 {
611 let ts = common::get_current_timestamp();
612
613 let texp1 = TimerExpMode::from(ts);
614 let (sec, nanos) =
615 if ts.timestamp_subsec_nanos() == 999_999_999
616 {
617 (ts.timestamp()+1, 0)
618 }
619 else
620 {
621 (ts.timestamp(), ts.timestamp_subsec_nanos() + 1)
622 };
623 let texp2 = TimerExpMode::OneShot { sec: sec, nsec: nanos as i64 };
624
625 assert_eq!(texp1 < texp2, true);
626 assert_eq!(texp2 > texp1, true);
627 assert_eq!(texp2 != texp1, true);
628 assert_eq!(texp2 < texp1, false);
629 }
630
631 #[should_panic]
632 #[test]
633 fn test_2_fail()
634 {
635 let ts = common::get_current_timestamp();
636
637 let texp1 = TimerExpMode::from(ts);
638 let texp2 = TimerExpMode::Interval { sec: 4, nsec: 4 };
639
640 assert_eq!(texp1 < texp2, true);
641 }
642}