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