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