1#![no_std]
2#![cfg_attr(all(feature = "std", target_env = "sgx"), feature(sgx_platform))]
3
4#[cfg(feature = "std")]
5extern crate std;
6
7#[cfg(feature = "std")]
8extern crate alloc;
9
10use alloc::borrow::ToOwned;
11
12#[cfg(feature = "std")]
13use std::time::SystemTime;
14
15#[cfg(all(feature = "std", target_os = "linux"))]
16use std::fs;
17
18#[cfg(not(target_env = "sgx"))]
19use core::arch::x86_64::__cpuid;
20use core::arch::x86_64::__rdtscp;
21use core::cell::RefCell;
22use core::cmp::{self, PartialEq, PartialOrd};
23use core::fmt::{self, Debug, Display, Formatter};
24use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
25use core::time::Duration;
26use core::ops::Add;
27
28const NANOS_PER_SEC: u64 = 1_000_000_000;
29
30pub trait NativeTime: Debug + PartialOrd + Copy + Add<core::time::Duration, Output = Self> + ToOwned {
31 fn minimum() -> Self;
32 fn abs_diff(&self, other: &Self) -> Duration;
33 fn now() -> Self;
34}
35
36#[cfg(feature = "std")]
37impl NativeTime for SystemTime {
38 fn minimum() -> Self {
39 SystemTime::UNIX_EPOCH
40 }
41
42 fn abs_diff(&self, earlier: &Self) -> Duration {
43 match self.duration_since(*earlier) {
46 Ok(duration) => duration,
47 Err(e) => e.duration()
48 }
49 }
50
51 fn now() -> Self {
52 SystemTime::now()
53 }
54}
55
56#[derive(Debug)]
57pub enum Error {
58 NoInvariantTsc,
59 NoCrystalFreqReported,
60 UnstableTsc,
61 UnexpectedTscInfo,
62 UnknownFrequency,
63 FrequencyCannotBeDetermined,
64 TypeOverflow,
65}
66
67#[cfg(not(target_env = "sgx"))]
68fn cpuid(leaf: u32, eax: &mut u32, ebx: &mut u32, ecx: &mut u32, edx: &mut u32) {
69 unsafe {
70 let res = __cpuid(leaf);
71 *eax = res.eax;
72 *ebx = res.ebx;
73 *ecx = res.ecx;
74 *edx = res.edx;
75 }
76}
77
78#[derive(Debug, Default)]
79pub struct Ticks(AtomicU64);
80
81impl PartialEq<Ticks> for Ticks {
82 fn eq(&self, other: &Ticks) -> bool {
83 let t = self.0.load(Ordering::Relaxed);
84 let o = other.0.load(Ordering::Relaxed);
85 t.eq(&o)
86 }
87}
88
89impl PartialOrd<Ticks> for Ticks {
90 fn partial_cmp(&self, other: &Ticks) -> Option<cmp::Ordering> {
91 let t = self.0.load(Ordering::Relaxed);
92 let o = other.0.load(Ordering::Relaxed);
93 t.partial_cmp(&o)
94 }
95}
96
97impl Add for Ticks {
98 type Output = Result<Ticks, Error>;
99
100 fn add(self, other: Self) -> Self::Output {
101 let t0 = self.0.load(Ordering::Relaxed);
102 let t1 = other.0.load(Ordering::Relaxed);
103 t0.checked_add(t1)
104 .ok_or(Error::TypeOverflow)
105 .map(|sum| Ticks(AtomicU64::new(sum)))
106 }
107}
108
109impl Display for Ticks {
110 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
111 write!(f, "{} ticks", self.0.load(Ordering::Relaxed))
112 }
113}
114
115impl Ticks {
116 pub fn new(t: u64) -> Self {
117 Ticks(AtomicU64::new(t))
118 }
119
120 pub const fn max() -> Self {
121 Ticks(AtomicU64::new(u64::MAX))
122 }
123
124 pub fn now() -> Self {
125 Ticks(Rdtscp::read().into())
136 }
137
138 pub fn abs_diff(&self, t1: &Ticks) -> Ticks {
139 let t0 = self.0.load(Ordering::Relaxed);
140 let t1 = t1.0.load(Ordering::Relaxed);
141
142 if t1 > t0 {
143 Ticks::new(t1 - t0)
144 } else {
145 Ticks::new(t0 - t1)
146 }
147 }
148
149 fn set(&self, t: Ticks) {
150 let t = t.0.load(Ordering::Relaxed);
151 self.0.store(t, Ordering::Relaxed);
152 }
153
154 pub fn from_duration(duration: Duration, freq: &Freq) -> Result<Self, Error> {
155 let freq = freq.as_u64();
156 let ticks_secs = duration.as_secs().checked_mul(freq).ok_or(Error::TypeOverflow)?;
157 let ticks_nsecs = (duration.subsec_nanos() as u64)
158 .checked_mul(freq).ok_or(Error::TypeOverflow)? / NANOS_PER_SEC;
159 Ok(Ticks::new(ticks_secs + ticks_nsecs))
160 }
161
162 pub fn as_duration_ex(&self, freq: &Freq) -> Duration {
163 let freq = freq.as_u64();
164 let ticks = self.0.load(Ordering::Relaxed);
165
166 let time_secs = ticks / freq;
167 let time_nsecs = (ticks % freq * NANOS_PER_SEC) / freq;
168 let time_nsecs: u32 = time_nsecs.try_into().expect("must be smaller than 1sec");
169
170 Duration::new(time_secs, time_nsecs)
171 }
172
173 pub fn elapsed(&self, freq: &Freq) -> Duration {
174 let tsc_now = Ticks::now();
175 self.abs_diff(&tsc_now).as_duration_ex(&freq)
176 }
177}
178
179pub struct Rdtscp;
180
181impl Rdtscp {
182 #[inline(never)]
183 pub fn read() -> u64 {
184 let mut aux: u32 = 0;
185 unsafe { __rdtscp(&mut aux) }
186 }
187
188 #[cfg(not(target_env = "sgx"))]
189 pub fn is_supported() -> bool {
190 let mut eax = 0;
191 let mut ebx = 0;
192 let mut ecx = 0;
193 let mut edx = 0;
194
195 cpuid(0x12, &mut eax, &mut ebx, &mut ecx, &mut edx);
196 eax & (0x1 << 1) != 0x0
197
198 }
199}
200
201pub struct Freq(AtomicU64);
203
204impl PartialEq<Freq> for Freq {
205 fn eq(&self, other: &Freq) -> bool {
206 let f = self.0.load(Ordering::Relaxed);
207 let o = other.0.load(Ordering::Relaxed);
208 f.eq(&o)
209 }
210}
211
212impl PartialOrd<Freq> for Freq {
213 fn partial_cmp(&self, other: &Freq) -> Option<cmp::Ordering> {
214 let f = self.0.load(Ordering::Relaxed);
215 let o = other.0.load(Ordering::Relaxed);
216 f.partial_cmp(&o)
217 }
218}
219
220impl Debug for Freq {
221 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
222 let freq = self.as_u64();
223 f.debug_struct("Freq")
224 .field("freq", &freq)
225 .finish()
226 }
227}
228
229impl Freq {
230 fn is_zero(&self) -> bool {
231 self.0.load(Ordering::Relaxed) == 0
232 }
233
234 fn set(&self, f: &Freq) {
235 let f = f.0.load(Ordering::Relaxed);
236 self.0.store(f, Ordering::Relaxed);
237 }
238}
239
240#[derive(PartialEq, Eq, Debug)]
241pub enum ClockSource {
242 TSC,
243}
244
245impl Freq {
246 pub fn new(crystal_hz: u32, numerator: u32, denominator: u32) -> Self {
247 Freq::from_u64(crystal_hz as u64 * numerator as u64 / denominator as u64)
248 }
249
250 pub fn from_u64(freq: u64) -> Freq {
251 assert!(freq != 0);
252 Freq(freq.into())
253 }
254
255 pub fn as_u64(&self) -> u64 {
256 self.0.load(Ordering::Relaxed)
257 }
258
259 pub fn estimate(ticks: Ticks, secs: Duration) -> Freq {
260 let nsecs = secs.as_secs() as u128 * NANOS_PER_SEC as u128 + secs.subsec_nanos() as u128;
261 let estimate = ticks.0.load(Ordering::Relaxed) as u128 * NANOS_PER_SEC as u128 / nsecs;
262
263 Freq(AtomicU64::from(estimate as u64))
264 }
265
266 #[cfg(not(target_env = "sgx"))]
267 pub fn invariant_tsc() -> bool {
268 let mut eax = 0;
269 let mut ebx = 0;
270 let mut ecx = 0;
271 let mut edx = 0;
272
273 cpuid(0x80000007, &mut eax, &mut ebx, &mut ecx, &mut edx);
274 edx & (0x1 << 8) != 0
275 }
276
277 #[cfg(all(feature = "std", target_os = "linux"))]
278 fn kernel_clock_source() -> Option<ClockSource> {
279 if fs::read_to_string("/sys/devices/system/clocksource/clocksource0/current_clocksource").ok()?.trim() == "tsc" {
280 Some(ClockSource::TSC)
281 } else {
282 None
283 }
284 }
285
286 #[cfg(not(target_env = "sgx"))]
288 pub fn get() -> Result<Self, Error> {
289 let mut eax_denominator: u32 = 0;
290 let mut ebx_numerator: u32 = 0;
291 let mut edx: u32 = 0;
292 let mut crystal_hz = 0;
293
294 #[cfg(all(feature = "std", target_os = "linux"))]
295 if Self::kernel_clock_source() != Some(ClockSource::TSC) {
296 return Err(Error::UnstableTsc)
297 }
298
299 if !Self::invariant_tsc() {
300 return Err(Error::NoInvariantTsc)
301 }
302
303 cpuid(0x15, &mut eax_denominator, &mut ebx_numerator, &mut crystal_hz, &mut edx);
304
305 if eax_denominator == 0 || ebx_numerator == 0 {
306 return Err(Error::UnexpectedTscInfo)
307 }
308
309 #[cfg(feature = "estimate_crystal_clock_freq")]
314 if crystal_hz == 0 {
315 let mut eax_base_mhz = 0;
316 let mut ebx = 0;
317 let mut ecx = 0;
318 let mut edx = 0;
319
320 cpuid(0x16, &mut eax_base_mhz, &mut ebx, &mut ecx, &mut edx);
321 crystal_hz = eax_base_mhz * 1000 * 1000 * eax_denominator / ebx_numerator;
322 }
323
324 if crystal_hz == 0 {
325 Err(Error::NoCrystalFreqReported)
326 } else {
327 Ok(Freq::new(crystal_hz, ebx_numerator, eax_denominator))
328 }
329 }
330}
331
332enum TscMode {
333 Fixed {
334 frequency: Freq,
335 },
336 Learn {
337 frequency_learning_period: Duration,
339 max_acceptable_drift: Duration,
341 max_sync_interval: Duration,
343 next_sync: Ticks,
345 frequency: Freq,
347 },
348 NoRdtsc,
349}
350
351enum TimeMode<T: NativeTime> {
352 Monotonic {
353 last_time: RefCell<Option<T>>,
354 mutex: AtomicBool,
355 },
356 NonMonotonic,
357}
358
359unsafe impl<T: NativeTime> Sync for TimeMode<T> {}
361
362impl<T: NativeTime> TimeMode<T> {
363 pub fn monotonic() -> Self {
364 TimeMode::Monotonic {
365 last_time: RefCell::new(None),
366 mutex: AtomicBool::new(false),
367 }
368 }
369
370 pub fn non_monotonic() -> Self {
371 TimeMode::NonMonotonic
372 }
373
374 pub fn observe(&self, now: T) -> T {
375 fn lock<T: NativeTime>(mode: &TimeMode<T>) -> Option<&RefCell<Option<T>>> {
376 if let TimeMode::Monotonic { mutex, last_time } = mode {
377 while mutex.compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire).is_err()
378 {}
379 Some(&last_time)
380 } else {
381 None
382 }
383 }
384
385 fn unlock<T: NativeTime>(mode: &TimeMode<T>) {
386 if let TimeMode::Monotonic { mutex, .. } = mode {
387 mutex.store(false, Ordering::Release);
388 }
389 }
390
391 if let Some(last_time) = lock(self) {
392 let mut last_time = last_time.borrow_mut();
393 let now = match last_time.to_owned() {
394 Some(last) if last < now => {
395 *last_time = Some(now);
396 now
397 },
398 Some(last) => {
399 last
400 },
401 None => {
402 *last_time = Some(now);
403 now
404 }
405 };
406 drop(last_time);
407 unlock(self);
408 now
409 } else {
410 now
411 }
412 }
413}
414
415pub struct Tsc<T: NativeTime> {
421 t0: (Ticks, T),
422 tsc_mode: TscMode,
424 time_mode: TimeMode<T>,
426}
427
428pub trait TscBuilder<T: NativeTime> {
429 fn set_monotonic_time(self) -> Self;
430 fn build(self) -> Tsc<T>;
431}
432
433pub struct NoRdtscTscBuilder<T: NativeTime> {
434 time_mode: TimeMode<T>,
436}
437
438impl<T: NativeTime> NoRdtscTscBuilder<T> {
439 pub fn new() -> Self {
440 NoRdtscTscBuilder {
441 time_mode: TimeMode::NonMonotonic,
442 }
443 }
444}
445
446impl<T: NativeTime> TscBuilder<T> for NoRdtscTscBuilder<T> {
447 fn set_monotonic_time(mut self) -> Self {
448 self.time_mode = TimeMode::monotonic();
449 self
450 }
451
452 fn build(self) -> Tsc<T> {
453 Tsc::new(TscMode::NoRdtsc, self.time_mode)
454 }
455}
456
457pub struct LearningFreqTscBuilder<T: NativeTime> {
458 max_sync_interval: Duration,
460 max_acceptable_drift: Duration,
462 frequency_learning_period: Duration,
464 time_mode: TimeMode<T>,
466 frequency: Option<Freq>,
468}
469
470impl<T: NativeTime> LearningFreqTscBuilder<T> {
471 pub fn new() -> Self {
472 LearningFreqTscBuilder {
473 frequency_learning_period: Duration::from_secs(1),
474 max_acceptable_drift: Duration::from_millis(1),
475 max_sync_interval: Duration::from_secs(60),
476 time_mode: TimeMode::non_monotonic(),
477 frequency: None,
478 }
479 }
480
481 pub fn set_initial_frequency(mut self, freq: Freq) -> Self {
482 self.frequency = Some(freq);
483 self
484 }
485
486 pub fn initial_frequency(&self) -> &Option<Freq> {
487 &self.frequency
488 }
489
490 pub fn set_max_acceptable_drift(mut self, error: Duration) -> Self {
491 self.max_acceptable_drift = error;
492 self
493 }
494
495 pub fn max_acceptable_drift(&self) -> &Duration {
496 &self.max_acceptable_drift
497 }
498
499 pub fn set_max_sync_interval(mut self, interval: Duration) -> Self {
500 self.max_sync_interval = interval;
501 self
502 }
503
504 pub fn max_sync_interval(&self) -> &Duration {
505 &self.max_sync_interval
506 }
507
508 pub fn set_frequency_learning_period(mut self, period: Duration) -> Self {
509 self.frequency_learning_period = period;
510 self
511 }
512
513 pub fn frequency_learning_period(&self) -> Duration {
514 self.frequency_learning_period
515 }
516}
517
518impl<T: NativeTime> TscBuilder<T> for LearningFreqTscBuilder<T> {
519 fn set_monotonic_time(mut self) -> Self {
520 self.time_mode = TimeMode::monotonic();
521 self
522 }
523
524 fn build(self) -> Tsc<T> {
525 let LearningFreqTscBuilder { frequency, max_sync_interval, max_acceptable_drift, frequency_learning_period, time_mode, } = self;
526 let (next_sync, frequency, frequency_learning_period) =
529 if let Some((Ok(next_sync), frequency)) = frequency.map(|f| (Ticks::from_duration(max_sync_interval, &f), f)) {
530 (next_sync, frequency, frequency_learning_period.max(max_sync_interval))
533 } else {
534 (Ticks::new(0), Freq(AtomicU64::from(0)), frequency_learning_period)
535 };
536 let tsc_mode = TscMode::Learn {
537 max_sync_interval,
538 max_acceptable_drift,
539 frequency_learning_period,
540 next_sync,
541 frequency,
542 };
543 Tsc::new(tsc_mode, time_mode)
544 }
545}
546
547pub struct FixedFreqTscBuilder<T: NativeTime> {
548 frequency: Freq,
549 time_mode: TimeMode<T>,
551}
552
553impl<T: NativeTime> TscBuilder<T> for FixedFreqTscBuilder<T> {
554 fn set_monotonic_time(mut self) -> Self {
555 self.time_mode = TimeMode::monotonic();
556 self
557 }
558
559 fn build(self) -> Tsc<T> {
560 let tsc_mode = TscMode::Fixed {
561 frequency: self.frequency,
562 };
563 Tsc::new(tsc_mode, self.time_mode)
564 }
565}
566
567impl<T: NativeTime> FixedFreqTscBuilder<T> {
568 pub fn new(frequency: Freq) -> Self {
569 FixedFreqTscBuilder {
570 frequency,
571 time_mode: TimeMode::non_monotonic(),
572 }
573 }
574}
575
576enum ResyncError<T> {
577 WithinFrequencyLearningPeriod(T)
578}
579
580impl<T: NativeTime> Tsc<T> {
581 fn new(tsc_mode: TscMode, time_mode: TimeMode<T>) -> Self {
582 let now = T::now();
583 let t0 = if let TscMode::NoRdtsc = tsc_mode { Ticks::default() } else { Ticks::now() };
584 Tsc {
585 t0: (t0, now),
586 tsc_mode,
587 time_mode,
588 }
589 }
590
591 fn resync_clocks(&self, current_freq: &Freq, frequency_learning_period: &Duration, max_acceptable_drift: &Duration, max_sync_interval: &Duration) -> Result<(T, Duration, Freq), ResyncError<T>> {
592 let system_now = T::now();
594
595 let diff_system = system_now.abs_diff(&self.t0.1);
597 if diff_system < *frequency_learning_period {
598 return Err(ResyncError::WithinFrequencyLearningPeriod(system_now));
599 }
600 let tsc_now = Ticks::now();
601 let estimated_freq = Freq::estimate(self.t0.0.abs_diff(&tsc_now), diff_system);
602
603 let now = self.now_ex(current_freq);
605 let error = system_now.abs_diff(&now);
606
607 let max_sync_interval = if error.is_zero() {
609 max_sync_interval.to_owned()
611 } else {
612 diff_system * max_acceptable_drift.div_duration_f64(error) as u32
614 };
615
616 Ok((system_now, max_sync_interval, estimated_freq))
617 }
618
619 fn now_ex(&self, freq: &Freq) -> T {
620 self.t0.1 + self.t0.0.elapsed(freq)
621 }
622
623 pub fn now(&self) -> T {
624 let now = match &self.tsc_mode {
625 TscMode::NoRdtsc => T::now(),
626 TscMode::Learn { frequency_learning_period, max_acceptable_drift, max_sync_interval, next_sync, frequency } => {
627 let (tsc_now, f) = if !frequency.is_zero() {
628 (Ticks::now(), Freq(frequency.as_u64().into()))
629 } else {
630 let system_now = T::now();
635 let tsc_now = Ticks::now();
636 let diff_system = system_now.abs_diff(&self.t0.1);
637 if diff_system < *frequency_learning_period {
638 return system_now;
640 }
641
642 let estimated_freq = Freq::estimate(self.t0.0.abs_diff(&tsc_now), diff_system);
643 frequency.set(&estimated_freq);
644 (tsc_now, estimated_freq)
645 };
646
647 let now = self.now_ex(&f);
648
649 if tsc_now < *next_sync {
650 now
651 } else {
652 match self.resync_clocks(&f, &frequency_learning_period, &max_acceptable_drift, &max_sync_interval) {
654 Ok((now, next_sync_interval, estimated_freq)) => {
655 let duration = Ticks::from_duration(next_sync_interval, &estimated_freq);
659 if let Ok(next_tsc) = tsc_now + duration.unwrap_or(Ticks::max()) {
660 next_sync.set(next_tsc);
661 }
662 frequency.set(&estimated_freq);
663 now
664 },
665 Err(ResyncError::WithinFrequencyLearningPeriod(now)) => now,
666 }
667 }
668 }
669 TscMode::Fixed { frequency } => {
670 self.now_ex(&frequency)
671 }
672 };
673
674 self.time_mode.observe(now)
675 }
676}
677
678#[cfg(test)]
679mod tests {
680 #[cfg(feature = "rdtsc_tests")]
681 use super::LearningFreqTscBuilder;
682 use super::{NativeTime, NoRdtscTscBuilder, TscBuilder};
683 #[cfg(not(target_env = "sgx"))]
684 use super::{FixedFreqTscBuilder, Freq, Tsc, Ticks};
685
686 use core::ops::Add;
687 use core::time::Duration;
688 #[cfg(all(feature = "std", feature = "rdtsc_tests", not(target_env = "sgx")))]
689 use std::borrow::ToOwned;
690 #[cfg(feature = "std")]
691 use {
692 std::time::SystemTime,
693 std::thread,
694 };
695
696 #[cfg(not(target_env = "sgx"))]
697 fn diff_system_time(t0: SystemTime, t1: SystemTime) -> Duration {
698 let diff = if t0 < t1 { t1.duration_since(t0) } else { t0.duration_since(t1) };
699 diff.unwrap()
700 }
701
702 fn test_duration() -> Duration {
703 if cfg!(feature = "long_duration_tests") {
704 Duration::from_secs(60)
705 } else {
706 Duration::from_secs(5)
707 }
708 }
709
710 const ADDITIONAL_DRIFT: Duration = Duration::from_millis(100);
711
712 #[test]
713 #[cfg(feature = "std")]
714 fn max_difference() {
715 let end = SystemTime::now() + test_duration();
716 let mut max = Duration::from_nanos(0);
717
718 while SystemTime::now() < end {
719 let t0 = SystemTime::now();
720 let t1 = SystemTime::now();
721 let diff = t1.duration_since(t0).unwrap();
722 if max < diff {
723 max = diff;
724 }
725
726 assert!(diff < ADDITIONAL_DRIFT, "{:?} difference between calls", diff);
727 }
728 }
731
732 #[test]
733 #[cfg(target_os = "linux")]
734 fn verify_frequency_reported() {
735 if let Ok(freq) = Freq::get() {
736 let t0 = (SystemTime::now(), Ticks::now());
737 std::thread::sleep(Duration::from_secs(2));
738 let t1 = (SystemTime::now(), Ticks::now());
739
740 let estimated_freq = Freq::estimate(t1.1.abs_diff(&t0.1), t1.0.duration_since(t0.0).unwrap());
741 let low_estimate = Freq::from_u64(estimated_freq.as_u64() * 99 / 100);
742 let high_estimate = Freq::from_u64(estimated_freq.as_u64() * 101 / 100);
743 assert!(low_estimate.as_u64() <= freq.as_u64() && freq.as_u64() <= high_estimate.as_u64(), "Expected frequency between {:?} and {:?}, got {:?}", low_estimate, high_estimate, freq);
744 }
745 }
746
747 #[test]
748 #[cfg(all(feature = "std", target_os = "linux"))]
749 fn verify_cpu_reported_freq() {
750 if let Ok(freq) = Freq::get() {
751 let tsc: Tsc<SystemTime> = FixedFreqTscBuilder::new(freq)
752 .build();
753 let t0 = (SystemTime::now(), tsc.now());
754 loop {
755 let t1 = (SystemTime::now(), tsc.now());
756 let diff = diff_system_time(t1.0, t1.1);
757 assert!(diff < ADDITIONAL_DRIFT, "Found diff between clocks of {:?} after {:?}", diff, diff_system_time(t0.0, t1.0));
758
759 std::thread::sleep(Duration::from_secs(2));
760 if test_duration() < diff_system_time(t1.1, t0.1) {
761 break;
762 }
763 }
764 }
765 }
766
767 fn clock_drift<T: NativeTime, R: NativeTime>(builder: impl TscBuilder<T>, test_duration: Duration, max_acceptable_drift: &Duration, monotonic_time: bool) {
768 let tsc = builder.build();
769 let reference_start = R::now();
770 let tsc_start = tsc.now();
771 let end = R::now() + test_duration;
772 let mut last = None;
773
774 while R::now() < end {
775 let reference_now = R::now();
776 let tsc_now = tsc.now();
777 let reference_duration = reference_now.abs_diff(&reference_start);
778 let tsc_duration = tsc_now.abs_diff(&tsc_start);
779 let drift = reference_duration.abs_diff(tsc_duration);
780 assert!(drift < *max_acceptable_drift, "Found {:?} drift, (max drift was {:?} after {}ms)", drift, max_acceptable_drift, reference_duration.as_millis());
781 if monotonic_time {
782 assert!(last.unwrap_or(tsc_now) <= tsc_now, "Time ran backwards (last: {:?}, now: {:?})", last, tsc_now);
783 last = Some(tsc_now);
784 }
785
786 #[cfg(feature = "std")]
787 std::thread::sleep(Duration::from_micros(10));
788 }
789 }
790
791 #[test]
792 #[cfg(all(feature = "std", feature = "rdtsc_tests"))]
793 #[cfg(not(target_env = "sgx"))]
794 fn clock_drift_default_learning_freq_builder() {
795 let tsc_builder: LearningFreqTscBuilder<SystemTime> = LearningFreqTscBuilder::new();
796 let max_drift = tsc_builder.max_acceptable_drift().to_owned();
797 clock_drift::<SystemTime, SystemTime>(tsc_builder, test_duration(), &(ADDITIONAL_DRIFT + max_drift), false);
798 }
799
800 #[test]
801 #[cfg(all(feature = "std", feature = "rdtsc_tests"))]
802 #[cfg(not(target_env = "sgx"))]
803 fn clock_drift_learning_freq_monotonic() {
804 let tsc_builder: LearningFreqTscBuilder<SystemTime> = LearningFreqTscBuilder::new()
805 .set_monotonic_time();
806 let max_drift = tsc_builder.max_acceptable_drift().to_owned();
807 clock_drift::<SystemTime, SystemTime>(tsc_builder, test_duration(), &(ADDITIONAL_DRIFT + max_drift), false);
808 }
809
810 #[test]
811 #[cfg(feature = "std")]
812 fn clock_drift_no_rdtsc_monotonic() {
813 let tsc_builder: NoRdtscTscBuilder<SystemTime> = NoRdtscTscBuilder::new()
814 .set_monotonic_time();
815 clock_drift::<SystemTime, SystemTime>(tsc_builder, test_duration(), &ADDITIONAL_DRIFT, true);
816 }
817
818 #[test]
819 #[cfg(target_os = "linux")]
820 #[cfg(all(feature = "std", feature = "rdtsc_tests"))]
821 fn clock_drift_fix_freq_monotonic() {
822 if let Ok(freq) = Freq::get() {
823 let tsc_builder = FixedFreqTscBuilder::<SystemTime>::new(freq)
824 .set_monotonic_time();
825 clock_drift::<SystemTime, SystemTime>(tsc_builder, test_duration(), &ADDITIONAL_DRIFT, true);
826 }
827 }
828
829 #[cfg(all(target_env = "sgx", feature = "rdtsc_tests"))]
830 #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
831 struct SgxTime(u64);
833
834 #[cfg(all(target_env = "sgx", feature = "rdtsc_tests"))]
835 impl Add<Duration> for SgxTime {
836 type Output = SgxTime;
837
838 fn add(self, other: Duration) -> Self::Output {
839 let t = self.0 + other.as_secs() * super::NANOS_PER_SEC + other.subsec_nanos() as u64;
840 SgxTime(t)
841 }
842 }
843
844 #[cfg(all(target_env = "sgx", feature = "rdtsc_tests"))]
845 impl NativeTime for SgxTime {
846 fn minimum() -> SgxTime {
847 SgxTime(0)
848 }
849
850 fn abs_diff(&self, other: &Self) -> Duration {
851 Duration::from_nanos(self.0.abs_diff(other.0))
852 }
853
854 fn now() -> Self {
855 let t = unsafe { std::os::fortanix_sgx::usercalls::raw::insecure_time() };
856 SgxTime(t)
857 }
858 }
859
860 #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
861 struct LaggingSystemTime(SystemTime);
865
866 impl LaggingSystemTime {
867 const fn system_lag() -> Duration {
868 Duration::from_secs(3)
869 }
870 }
871
872 #[cfg(feature = "std")]
873 impl Add<Duration> for LaggingSystemTime {
874 type Output = LaggingSystemTime;
875
876 fn add(self, other: Duration) -> Self::Output {
877 LaggingSystemTime(self.0 + other)
878 }
879 }
880
881 #[cfg(feature = "std")]
882 impl NativeTime for LaggingSystemTime {
883 fn minimum() -> Self {
884 LaggingSystemTime(SystemTime::minimum())
885 }
886
887 fn abs_diff(&self, earlier: &Self) -> Duration {
888 self.0.abs_diff(&earlier.0)
889 }
890
891 fn now() -> Self {
892 let now = SystemTime::now();
893 thread::sleep(Self::system_lag());
894 LaggingSystemTime(now)
895 }
896 }
897
898 #[test]
899 #[cfg(all(feature = "std", feature = "rdtsc_tests"))]
900 fn very_lagging_system_time() {
901 let tsc_builder: LearningFreqTscBuilder<LaggingSystemTime> = LearningFreqTscBuilder::new()
902 .set_monotonic_time();
903 clock_drift::<_, SystemTime>(tsc_builder, test_duration(), &(LaggingSystemTime::system_lag() * 3), true);
904 }
905
906 #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
907 struct HighVariationSystemTime(SystemTime);
911
912 impl HighVariationSystemTime {
913 pub fn variation() -> Duration {
914 Duration::from_secs(10)
915 }
916 }
917
918 #[cfg(feature = "std")]
919 impl Add<Duration> for HighVariationSystemTime {
920 type Output = HighVariationSystemTime ;
921
922 fn add(self, other: Duration) -> Self::Output {
923 HighVariationSystemTime(self.0 + other)
924 }
925 }
926
927 #[cfg(feature = "std")]
928 impl NativeTime for HighVariationSystemTime {
929 fn minimum() -> Self {
930 HighVariationSystemTime(SystemTime::minimum())
931 }
932
933 fn abs_diff(&self, earlier: &Self) -> Duration {
934 self.0.abs_diff(&earlier.0)
935 }
936
937 fn now() -> Self {
938 let now = SystemTime::now();
939 let variation = Duration::from_secs(rand::random::<u64>() % HighVariationSystemTime::variation().as_secs());
940 if rand::random::<bool>() {
941 HighVariationSystemTime(now + variation)
942 } else {
943 HighVariationSystemTime(now - variation)
944 }
945 }
946 }
947
948 #[test]
949 #[cfg(all(feature = "std", feature = "rdtsc_tests"))]
950 #[cfg(not(target_env = "sgx"))]
951 fn high_variation_system_time_lag() {
952 for monotonic in [false, true] {
953 for _run in 0..30 {
954 let freq = Freq::get().unwrap();
955 let tsc_builder: LearningFreqTscBuilder<HighVariationSystemTime> = LearningFreqTscBuilder::new()
956 .set_monotonic_time()
957 .set_initial_frequency(freq);
958 clock_drift::<_, SystemTime>(tsc_builder, Duration::from_secs(1), &(2 * HighVariationSystemTime::variation()), monotonic);
959 }
960 }
961 }
962
963 #[test]
964 #[cfg(all(feature = "std", feature = "rdtsc_tests", feature = "long_duration_tests"))]
965 #[cfg(not(target_env = "sgx"))]
966 fn high_variation_system_time_drift() {
967 let tsc_builder: LearningFreqTscBuilder<HighVariationSystemTime> = LearningFreqTscBuilder::new()
968 .set_initial_frequency(Freq::get().unwrap())
969 .set_frequency_learning_period(Duration::from_secs(120))
970 .set_max_acceptable_drift(Duration::from_millis(1))
971 .set_max_sync_interval(Duration::from_secs(60))
972 .set_monotonic_time();
973
974 clock_drift::<_, SystemTime>(tsc_builder, Duration::from_secs(180), &(2 * HighVariationSystemTime::variation() + Duration::from_millis(1)), true);
978 }
979
980 #[test]
981 #[cfg(all(feature = "std", feature = "rdtsc_tests"))]
982 fn build_time_learning_freq_tsc_builder() {
983 let tsc_builder: LearningFreqTscBuilder<HighVariationSystemTime> = LearningFreqTscBuilder::new()
984 .set_monotonic_time();
985 let t0 = SystemTime::now();
986 let tsc = tsc_builder.build();
987 let build_time = SystemTime::now().duration_since(t0).unwrap();
988 assert!(build_time < Duration::from_millis(10), "Building tsc took {} ms", build_time.as_millis());
989
990 let t0 = SystemTime::now();
991 let _t = tsc.now();
992 let now_time = SystemTime::now().duration_since(t0).unwrap();
993 assert!(now_time < Duration::from_millis(10), "tsc.now() took {} ms", now_time.as_millis());
994 }
995
996 #[test]
997 #[cfg(all(target_env = "sgx", feature = "rdtsc_tests"))]
998 fn sgx_time() {
999 let tsc_builder: LearningFreqTscBuilder<SgxTime> = LearningFreqTscBuilder::new()
1000 .set_monotonic_time();
1001 clock_drift::<_, SystemTime>(tsc_builder, test_duration(), &ADDITIONAL_DRIFT, true);
1004 }
1005}