1#[cfg_attr(
2 any(target_env = "musl", target_env = "ohos"),
3 allow(deprecated)
4)]
5pub use libc::{suseconds_t, time_t};
7use libc::{timespec, timeval};
8use std::time::Duration;
9use std::{cmp, fmt, ops};
10
11const fn zero_init_timespec() -> timespec {
12 unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
16}
17
18#[cfg(any(
19 all(feature = "time", any(target_os = "android", target_os = "linux")),
20 all(
21 any(
22 target_os = "freebsd",
23 solarish,
24 target_os = "linux",
25 target_os = "netbsd"
26 ),
27 feature = "time",
28 feature = "signal"
29 )
30))]
31pub(crate) mod timer {
32 use crate::sys::time::{zero_init_timespec, TimeSpec};
33 use bitflags::bitflags;
34
35 #[derive(Debug, Clone, Copy)]
36 pub(crate) struct TimerSpec(libc::itimerspec);
37
38 impl TimerSpec {
39 pub const fn none() -> Self {
40 Self(libc::itimerspec {
41 it_interval: zero_init_timespec(),
42 it_value: zero_init_timespec(),
43 })
44 }
45 }
46
47 impl AsMut<libc::itimerspec> for TimerSpec {
48 fn as_mut(&mut self) -> &mut libc::itimerspec {
49 &mut self.0
50 }
51 }
52
53 impl AsRef<libc::itimerspec> for TimerSpec {
54 fn as_ref(&self) -> &libc::itimerspec {
55 &self.0
56 }
57 }
58
59 impl From<Expiration> for TimerSpec {
60 fn from(expiration: Expiration) -> TimerSpec {
61 match expiration {
62 Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
63 it_interval: zero_init_timespec(),
64 it_value: *t.as_ref(),
65 }),
66 Expiration::IntervalDelayed(start, interval) => {
67 TimerSpec(libc::itimerspec {
68 it_interval: *interval.as_ref(),
69 it_value: *start.as_ref(),
70 })
71 }
72 Expiration::Interval(t) => TimerSpec(libc::itimerspec {
73 it_interval: *t.as_ref(),
74 it_value: *t.as_ref(),
75 }),
76 }
77 }
78 }
79
80 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
83 pub enum Expiration {
84 OneShot(TimeSpec),
86 IntervalDelayed(TimeSpec, TimeSpec),
89 Interval(TimeSpec),
91 }
92
93 #[cfg(linux_android)]
94 bitflags! {
95 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
97 pub struct TimerSetTimeFlags: libc::c_int {
98 const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
99 const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET;
100 }
101 }
102 #[cfg(any(freebsdlike, target_os = "netbsd", solarish))]
103 bitflags! {
104 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
106 pub struct TimerSetTimeFlags: libc::c_int {
107 const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
108 }
109 }
110
111 impl From<TimerSpec> for Expiration {
112 fn from(timerspec: TimerSpec) -> Expiration {
113 match timerspec {
114 TimerSpec(libc::itimerspec {
115 it_interval:
116 libc::timespec {
117 tv_sec: 0,
118 tv_nsec: 0,
119 ..
120 },
121 it_value: ts,
122 }) => Expiration::OneShot(ts.into()),
123 TimerSpec(libc::itimerspec {
124 it_interval: int_ts,
125 it_value: val_ts,
126 }) => {
127 if (int_ts.tv_sec == val_ts.tv_sec)
128 && (int_ts.tv_nsec == val_ts.tv_nsec)
129 {
130 Expiration::Interval(int_ts.into())
131 } else {
132 Expiration::IntervalDelayed(
133 val_ts.into(),
134 int_ts.into(),
135 )
136 }
137 }
138 }
139 }
140 }
141}
142
143pub trait TimeValLike: Sized {
144 #[inline]
145 fn zero() -> Self {
146 Self::seconds(0)
147 }
148
149 #[inline]
150 fn hours(hours: i64) -> Self {
151 let secs = hours
152 .checked_mul(SECS_PER_HOUR)
153 .expect("TimeValLike::hours ouf of bounds");
154 Self::seconds(secs)
155 }
156
157 #[inline]
158 fn minutes(minutes: i64) -> Self {
159 let secs = minutes
160 .checked_mul(SECS_PER_MINUTE)
161 .expect("TimeValLike::minutes out of bounds");
162 Self::seconds(secs)
163 }
164
165 fn seconds(seconds: i64) -> Self;
166 fn milliseconds(milliseconds: i64) -> Self;
167 fn microseconds(microseconds: i64) -> Self;
168 fn nanoseconds(nanoseconds: i64) -> Self;
169
170 #[inline]
171 fn num_hours(&self) -> i64 {
172 self.num_seconds() / 3600
173 }
174
175 #[inline]
176 fn num_minutes(&self) -> i64 {
177 self.num_seconds() / 60
178 }
179
180 fn num_seconds(&self) -> i64;
181 fn num_milliseconds(&self) -> i64;
182 fn num_microseconds(&self) -> i64;
183 fn num_nanoseconds(&self) -> i64;
184}
185
186#[repr(C)]
187#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
188pub struct TimeSpec(timespec);
189
190const NANOS_PER_SEC: i64 = 1_000_000_000;
191const SECS_PER_MINUTE: i64 = 60;
192const SECS_PER_HOUR: i64 = 3600;
193
194#[cfg(target_pointer_width = "64")]
195const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
196
197#[cfg(target_pointer_width = "32")]
198const TS_MAX_SECONDS: i64 = isize::MAX as i64;
199
200const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
201
202#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
205type timespec_tv_nsec_t = i64;
206#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
207type timespec_tv_nsec_t = libc::c_long;
208
209impl From<timespec> for TimeSpec {
210 fn from(ts: timespec) -> Self {
211 Self(ts)
212 }
213}
214
215impl From<Duration> for TimeSpec {
216 fn from(duration: Duration) -> Self {
217 Self::from_duration(duration)
218 }
219}
220
221impl From<TimeSpec> for Duration {
222 fn from(timespec: TimeSpec) -> Self {
223 Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
224 }
225}
226
227impl AsRef<timespec> for TimeSpec {
228 fn as_ref(&self) -> ×pec {
229 &self.0
230 }
231}
232
233impl AsMut<timespec> for TimeSpec {
234 fn as_mut(&mut self) -> &mut timespec {
235 &mut self.0
236 }
237}
238
239impl Ord for TimeSpec {
240 fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
243 if self.tv_sec() == other.tv_sec() {
244 self.tv_nsec().cmp(&other.tv_nsec())
245 } else {
246 self.tv_sec().cmp(&other.tv_sec())
247 }
248 }
249}
250
251impl PartialOrd for TimeSpec {
252 fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
253 Some(self.cmp(other))
254 }
255}
256
257impl TimeValLike for TimeSpec {
258 #[inline]
259 #[cfg_attr(
260 any(target_env = "musl", target_env = "ohos"),
261 allow(deprecated)
262 )]
263 fn seconds(seconds: i64) -> TimeSpec {
265 assert!(
266 (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
267 "TimeSpec out of bounds; seconds={seconds}",
268 );
269 let mut ts = zero_init_timespec();
270 ts.tv_sec = seconds as time_t;
271 TimeSpec(ts)
272 }
273
274 #[inline]
275 fn milliseconds(milliseconds: i64) -> TimeSpec {
276 let nanoseconds = milliseconds
277 .checked_mul(1_000_000)
278 .expect("TimeSpec::milliseconds out of bounds");
279
280 TimeSpec::nanoseconds(nanoseconds)
281 }
282
283 #[inline]
285 fn microseconds(microseconds: i64) -> TimeSpec {
286 let nanoseconds = microseconds
287 .checked_mul(1_000)
288 .expect("TimeSpec::milliseconds out of bounds");
289
290 TimeSpec::nanoseconds(nanoseconds)
291 }
292
293 #[inline]
295 #[cfg_attr(
296 any(target_env = "musl", target_env = "ohos"),
297 allow(deprecated)
298 )]
299 fn nanoseconds(nanoseconds: i64) -> TimeSpec {
301 let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
302 assert!(
303 (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
304 "TimeSpec out of bounds"
305 );
306 let mut ts = zero_init_timespec();
307 ts.tv_sec = secs as time_t;
308 ts.tv_nsec = nanos as timespec_tv_nsec_t;
309 TimeSpec(ts)
310 }
311
312 #[allow(clippy::unnecessary_cast)]
314 fn num_seconds(&self) -> i64 {
315 if self.tv_sec() < 0 && self.tv_nsec() > 0 {
316 (self.tv_sec() + 1) as i64
317 } else {
318 self.tv_sec() as i64
319 }
320 }
321
322 fn num_milliseconds(&self) -> i64 {
323 self.num_nanoseconds() / 1_000_000
324 }
325
326 fn num_microseconds(&self) -> i64 {
327 self.num_nanoseconds() / 1_000
328 }
329
330 #[allow(clippy::unnecessary_cast)]
332 fn num_nanoseconds(&self) -> i64 {
333 let secs = self.num_seconds() * 1_000_000_000;
334 let nsec = self.nanos_mod_sec();
335 secs + nsec as i64
336 }
337}
338
339impl TimeSpec {
340 #[cfg(not(target_os = "redox"))]
342 pub const UTIME_OMIT: TimeSpec =
344 TimeSpec::new(0, libc::UTIME_OMIT as timespec_tv_nsec_t);
345 #[cfg(not(target_os = "redox"))]
348 pub const UTIME_NOW: TimeSpec =
349 TimeSpec::new(0, libc::UTIME_NOW as timespec_tv_nsec_t);
350
351 #[cfg_attr(
353 any(target_env = "musl", target_env = "ohos"),
354 allow(deprecated)
355 )] pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
357 let mut ts = zero_init_timespec();
358 ts.tv_sec = seconds;
359 ts.tv_nsec = nanoseconds;
360 Self(ts)
361 }
362
363 fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
364 if self.tv_sec() < 0 && self.tv_nsec() > 0 {
365 self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
366 } else {
367 self.tv_nsec()
368 }
369 }
370
371 #[cfg_attr(
372 any(target_env = "musl", target_env = "ohos"),
373 allow(deprecated)
374 )] pub const fn tv_sec(&self) -> time_t {
376 self.0.tv_sec
377 }
378
379 pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
380 self.0.tv_nsec
381 }
382
383 #[cfg_attr(
384 any(target_env = "musl", target_env = "ohos"),
385 allow(deprecated)
386 )]
387 pub const fn from_duration(duration: Duration) -> Self {
389 let mut ts = zero_init_timespec();
390 ts.tv_sec = duration.as_secs() as time_t;
391 ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
392 TimeSpec(ts)
393 }
394
395 pub const fn from_timespec(timespec: timespec) -> Self {
396 Self(timespec)
397 }
398}
399
400impl ops::Neg for TimeSpec {
401 type Output = TimeSpec;
402
403 fn neg(self) -> TimeSpec {
404 TimeSpec::nanoseconds(-self.num_nanoseconds())
405 }
406}
407
408impl ops::Add for TimeSpec {
409 type Output = TimeSpec;
410
411 fn add(self, rhs: TimeSpec) -> TimeSpec {
412 TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
413 }
414}
415
416impl ops::Sub for TimeSpec {
417 type Output = TimeSpec;
418
419 fn sub(self, rhs: TimeSpec) -> TimeSpec {
420 TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
421 }
422}
423
424impl ops::Mul<i32> for TimeSpec {
425 type Output = TimeSpec;
426
427 fn mul(self, rhs: i32) -> TimeSpec {
428 let usec = self
429 .num_nanoseconds()
430 .checked_mul(i64::from(rhs))
431 .expect("TimeSpec multiply out of bounds");
432
433 TimeSpec::nanoseconds(usec)
434 }
435}
436
437impl ops::Div<i32> for TimeSpec {
438 type Output = TimeSpec;
439
440 fn div(self, rhs: i32) -> TimeSpec {
441 let usec = self.num_nanoseconds() / i64::from(rhs);
442 TimeSpec::nanoseconds(usec)
443 }
444}
445
446impl fmt::Display for TimeSpec {
447 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
448 let (abs, sign) = if self.tv_sec() < 0 {
449 (-*self, "-")
450 } else {
451 (*self, "")
452 };
453
454 let sec = abs.tv_sec();
455
456 write!(f, "{sign}")?;
457
458 if abs.tv_nsec() == 0 {
459 if sec == 1 {
460 write!(f, "1 second")?;
461 } else {
462 write!(f, "{sec} seconds")?;
463 }
464 } else if abs.tv_nsec() % 1_000_000 == 0 {
465 write!(f, "{sec}.{:03} seconds", abs.tv_nsec() / 1_000_000)?;
466 } else if abs.tv_nsec() % 1_000 == 0 {
467 write!(f, "{sec}.{:06} seconds", abs.tv_nsec() / 1_000)?;
468 } else {
469 write!(f, "{sec}.{:09} seconds", abs.tv_nsec())?;
470 }
471
472 Ok(())
473 }
474}
475
476#[repr(transparent)]
477#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
478pub struct TimeVal(timeval);
479
480const MICROS_PER_SEC: i64 = 1_000_000;
481
482#[cfg(target_pointer_width = "64")]
483const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
484
485#[cfg(target_pointer_width = "32")]
486const TV_MAX_SECONDS: i64 = isize::MAX as i64;
487
488const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
489
490impl AsRef<timeval> for TimeVal {
491 fn as_ref(&self) -> &timeval {
492 &self.0
493 }
494}
495
496impl AsMut<timeval> for TimeVal {
497 fn as_mut(&mut self) -> &mut timeval {
498 &mut self.0
499 }
500}
501
502impl Ord for TimeVal {
503 fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
506 if self.tv_sec() == other.tv_sec() {
507 self.tv_usec().cmp(&other.tv_usec())
508 } else {
509 self.tv_sec().cmp(&other.tv_sec())
510 }
511 }
512}
513
514impl PartialOrd for TimeVal {
515 fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
516 Some(self.cmp(other))
517 }
518}
519
520impl TimeValLike for TimeVal {
521 #[inline]
522 fn seconds(seconds: i64) -> TimeVal {
523 assert!(
524 (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
525 "TimeVal out of bounds; seconds={seconds}"
526 );
527 #[cfg_attr(
528 any(target_env = "musl", target_env = "ohos"),
529 allow(deprecated)
530 )]
531 TimeVal(timeval {
533 tv_sec: seconds as time_t,
534 tv_usec: 0,
535 })
536 }
537
538 #[inline]
539 fn milliseconds(milliseconds: i64) -> TimeVal {
540 let microseconds = milliseconds
541 .checked_mul(1_000)
542 .expect("TimeVal::milliseconds out of bounds");
543
544 TimeVal::microseconds(microseconds)
545 }
546
547 #[inline]
549 fn microseconds(microseconds: i64) -> TimeVal {
550 let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
551 assert!(
552 (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
553 "TimeVal out of bounds"
554 );
555 #[cfg_attr(
556 any(target_env = "musl", target_env = "ohos"),
557 allow(deprecated)
558 )]
559 TimeVal(timeval {
561 tv_sec: secs as time_t,
562 tv_usec: micros as suseconds_t,
563 })
564 }
565
566 #[inline]
569 fn nanoseconds(nanoseconds: i64) -> TimeVal {
570 let microseconds = nanoseconds / 1000;
571 let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
572 assert!(
573 (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
574 "TimeVal out of bounds"
575 );
576 #[cfg_attr(
577 any(target_env = "musl", target_env = "ohos"),
578 allow(deprecated)
579 )]
580 TimeVal(timeval {
582 tv_sec: secs as time_t,
583 tv_usec: micros as suseconds_t,
584 })
585 }
586
587 #[allow(clippy::unnecessary_cast)]
589 fn num_seconds(&self) -> i64 {
590 if self.tv_sec() < 0 && self.tv_usec() > 0 {
591 (self.tv_sec() + 1) as i64
592 } else {
593 self.tv_sec() as i64
594 }
595 }
596
597 fn num_milliseconds(&self) -> i64 {
598 self.num_microseconds() / 1_000
599 }
600
601 #[allow(clippy::unnecessary_cast)]
603 fn num_microseconds(&self) -> i64 {
604 let secs = self.num_seconds() * 1_000_000;
605 let usec = self.micros_mod_sec();
606 secs + usec as i64
607 }
608
609 fn num_nanoseconds(&self) -> i64 {
610 self.num_microseconds() * 1_000
611 }
612}
613
614impl TimeVal {
615 #[cfg_attr(
617 any(target_env = "musl", target_env = "ohos"),
618 allow(deprecated)
619 )] pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
621 Self(timeval {
622 tv_sec: seconds,
623 tv_usec: microseconds,
624 })
625 }
626
627 fn micros_mod_sec(&self) -> suseconds_t {
628 if self.tv_sec() < 0 && self.tv_usec() > 0 {
629 self.tv_usec() - MICROS_PER_SEC as suseconds_t
630 } else {
631 self.tv_usec()
632 }
633 }
634
635 #[cfg_attr(
636 any(target_env = "musl", target_env = "ohos"),
637 allow(deprecated)
638 )] pub const fn tv_sec(&self) -> time_t {
640 self.0.tv_sec
641 }
642
643 pub const fn tv_usec(&self) -> suseconds_t {
644 self.0.tv_usec
645 }
646}
647
648impl ops::Neg for TimeVal {
649 type Output = TimeVal;
650
651 fn neg(self) -> TimeVal {
652 TimeVal::microseconds(-self.num_microseconds())
653 }
654}
655
656impl ops::Add for TimeVal {
657 type Output = TimeVal;
658
659 fn add(self, rhs: TimeVal) -> TimeVal {
660 TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
661 }
662}
663
664impl ops::Sub for TimeVal {
665 type Output = TimeVal;
666
667 fn sub(self, rhs: TimeVal) -> TimeVal {
668 TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
669 }
670}
671
672impl ops::Mul<i32> for TimeVal {
673 type Output = TimeVal;
674
675 fn mul(self, rhs: i32) -> TimeVal {
676 let usec = self
677 .num_microseconds()
678 .checked_mul(i64::from(rhs))
679 .expect("TimeVal multiply out of bounds");
680
681 TimeVal::microseconds(usec)
682 }
683}
684
685impl ops::Div<i32> for TimeVal {
686 type Output = TimeVal;
687
688 fn div(self, rhs: i32) -> TimeVal {
689 let usec = self.num_microseconds() / i64::from(rhs);
690 TimeVal::microseconds(usec)
691 }
692}
693
694impl fmt::Display for TimeVal {
695 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
696 let (abs, sign) = if self.tv_sec() < 0 {
697 (-*self, "-")
698 } else {
699 (*self, "")
700 };
701
702 let sec = abs.tv_sec();
703
704 write!(f, "{sign}")?;
705
706 if abs.tv_usec() == 0 {
707 if sec == 1 {
708 write!(f, "1 second")?;
709 } else {
710 write!(f, "{sec} seconds")?;
711 }
712 } else if abs.tv_usec() % 1000 == 0 {
713 write!(f, "{sec}.{:03} seconds", abs.tv_usec() / 1000)?;
714 } else {
715 write!(f, "{sec}.{:06} seconds", abs.tv_usec())?;
716 }
717
718 Ok(())
719 }
720}
721
722impl From<timeval> for TimeVal {
723 fn from(tv: timeval) -> Self {
724 TimeVal(tv)
725 }
726}
727
728#[inline]
729fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
730 (div_floor_64(this, other), mod_floor_64(this, other))
731}
732
733#[inline]
734fn div_floor_64(this: i64, other: i64) -> i64 {
735 match div_rem_64(this, other) {
736 (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
737 (d, _) => d,
738 }
739}
740
741#[inline]
742fn mod_floor_64(this: i64, other: i64) -> i64 {
743 match this % other {
744 r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
745 r => r,
746 }
747}
748
749#[inline]
750fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
751 (this / other, this % other)
752}