1use std::collections::HashMap;
10use std::fmt;
11use std::ops::Add;
12use std::ops::Sub;
13use std::time::Duration;
14
15use chrono::DateTime;
16use chrono::Utc;
17use reverie_syscalls::Timespec;
18use reverie_syscalls::Timeval;
19use serde::Deserialize;
20use serde::Serialize;
21use tracing::trace;
22
23use crate::config::Config;
24use crate::pid::DetTid;
25
26const NANOS_PER_SEC: u64 = 1_000_000_000;
28const NANOS_PER_MILLI: u64 = 1_000_000;
29const NANOS_PER_MICRO: u64 = 1_000;
30const MILLIS_PER_SEC: u64 = 1_000;
31const MICROS_PER_SEC: u64 = 1_000_000;
32
33pub const NANOS_PER_SYSCALL: f64 = 10000.0;
37
38pub const NANOS_PER_RCB: f64 = 10.0;
40
41const RCB_TIME_MULTIPLIER_SCALE: u64 = 1_u64 << 32;
46
47#[derive(
51 Debug,
52 Clone,
53 Copy,
54 Serialize,
55 Deserialize,
56 Ord,
57 PartialOrd,
58 Eq,
59 PartialEq,
60 Hash
61)]
62pub struct RcbTimeMultiplier(u64);
63
64impl RcbTimeMultiplier {
65 pub const ONE: Self = Self(RCB_TIME_MULTIPLIER_SCALE);
67
68 pub const MAX: f64 = u64::MAX as f64 / RCB_TIME_MULTIPLIER_SCALE as f64;
70
71 pub fn from_f64(value: f64) -> Self {
73 assert!(value.is_finite() && value > 0.0 && value <= Self::MAX);
74 let scaled = (value * RCB_TIME_MULTIPLIER_SCALE as f64).round() as u64;
75 Self(scaled.max(1))
76 }
77
78 pub fn as_f64(self) -> f64 {
80 self.0 as f64 / RCB_TIME_MULTIPLIER_SCALE as f64
81 }
82
83 fn units(self) -> u64 {
84 self.0
85 }
86}
87
88impl Default for RcbTimeMultiplier {
89 fn default() -> Self {
90 Self::ONE
91 }
92}
93
94pub const NANOS_PER_NONDET_INSTR: f64 = 25.0;
96
97pub const NANOS_PER_SCHED: f64 = 500_000.0;
99
100#[derive(
109 Default,
110 Debug,
111 Clone,
112 Copy,
113 Serialize,
114 Deserialize,
115 Ord,
116 PartialOrd,
117 Eq,
118 PartialEq,
119 Hash
120)]
121pub struct LogicalTime(u64);
122
123pub type LogicalDuration = LogicalTime;
126
127impl LogicalTime {
128 pub const ZERO: LogicalTime = LogicalTime(0);
130 pub const MAX: LogicalTime = LogicalTime(u64::MAX);
132
133 pub const INDEFINITE: LogicalTime = LogicalTime::MAX;
146
147 pub fn is_indefinite(&self) -> bool {
149 *self == LogicalTime::INDEFINITE
150 }
151
152 pub fn as_micros(&self) -> u64 {
154 self.0 / NANOS_PER_MICRO
155 }
156
157 pub fn as_millis(&self) -> u64 {
159 self.0 / NANOS_PER_MILLI
160 }
161
162 pub fn as_nanos(&self) -> u64 {
164 self.0
165 }
166
167 pub fn as_secs(&self) -> u64 {
171 self.0 / NANOS_PER_SEC
172 }
173
174 pub fn from_micros(micros: u64) -> Self {
176 LogicalTime(micros * NANOS_PER_MICRO)
177 }
178
179 pub fn from_millis(millis: u64) -> Self {
181 LogicalTime(millis * NANOS_PER_MILLI)
182 }
183
184 pub fn from_nanos(nanos: u64) -> Self {
186 LogicalTime(nanos)
187 }
188
189 pub fn from_big_nanos(nanos: u128) -> Self {
191 LogicalTime(nanos as u64)
193 }
194
195 pub fn from_secs(secs: u64) -> Self {
197 LogicalTime(secs * NANOS_PER_SEC)
198 }
199
200 pub fn subsec_micros(&self) -> u32 {
204 ((self.0 / NANOS_PER_MICRO) % MICROS_PER_SEC) as u32
205 }
206
207 pub fn subsec_millis(&self) -> u32 {
211 ((self.0 / NANOS_PER_MILLI) % MILLIS_PER_SEC) as u32
212 }
213
214 pub fn subsec_nanos(&self) -> u32 {
218 (self.0 % NANOS_PER_SEC) as u32
219 }
220
221 pub fn from_rcbs(n: u64) -> Self {
223 LogicalTime((n as f64 * NANOS_PER_RCB) as u64)
224 }
225
226 pub fn into_rcbs(self) -> u64 {
229 (self.0 as f64 / NANOS_PER_RCB) as u64
230 }
231
232 pub fn into_rcbs_with_multiplier(self, multiplier: f64) -> u64 {
234 debug_assert!(multiplier > 0.0);
235 (self.0 as f64 / (NANOS_PER_RCB * multiplier)).floor() as u64
236 }
237
238 pub fn is_zero(&self) -> bool {
240 self.0 == 0
241 }
242
243 pub fn duration_since(&self, from: LogicalTime) -> Duration {
245 if from.0 > self.0 {
246 panic!(
247 "LogicalTime::duration_since cannot take duration since a time in the *future* ({}), relative to {}",
248 from, self
249 );
250 }
251 Duration::from_nanos(self.0 - from.0)
252 }
253}
254
255impl std::fmt::Display for LogicalTime {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 let chars = format!("{}", self.0);
259 let mut remain = chars.len();
260 let mut first_char = true;
261 for ch in chars.chars() {
262 if !first_char && remain % 3 == 0 {
263 if remain == 9 {
264 write!(f, ".")?;
265 } else {
266 write!(f, "_")?;
269 }
270 }
271 first_char = false;
272 remain -= 1;
273 write!(f, "{}", ch)?;
274 }
275 if chars.len() <= 9 {
276 write!(f, "ns")
277 } else {
278 write!(f, "s")
279 }
280 }
281}
282
283impl Add for LogicalTime {
284 type Output = Self;
285 fn add(self, rhs: Self) -> Self {
286 LogicalTime(self.0.saturating_add(rhs.0))
289 }
290}
291
292impl Add<Duration> for LogicalTime {
293 type Output = Self;
294 fn add(self, rhs: Duration) -> Self {
295 let nanos = u64::try_from(rhs.as_nanos()).unwrap_or(u64::MAX);
300 LogicalTime(self.0.saturating_add(nanos))
301 }
302}
303
304impl Add<u128> for LogicalTime {
305 type Output = Self;
306 fn add(self, rhs: u128) -> Self {
307 LogicalTime(self.0.saturating_add(rhs.min(u64::MAX as u128) as u64))
309 }
310}
311
312impl Sub for LogicalTime {
313 type Output = Self;
314 fn sub(self, rhs: Self) -> Self {
315 LogicalTime(self.0 - rhs.0)
316 }
317}
318
319impl From<LogicalTime> for Timespec {
320 fn from(logical_time: LogicalTime) -> Timespec {
321 Timespec {
322 tv_sec: logical_time.as_secs() as i64,
323 tv_nsec: logical_time.subsec_nanos() as i64,
324 }
325 }
326}
327
328impl From<LogicalTime> for Timeval {
329 fn from(logical_time: LogicalTime) -> Timeval {
330 Timeval {
331 tv_sec: logical_time.as_secs() as i64,
332 tv_usec: logical_time.subsec_micros() as i64,
333 }
334 }
335}
336
337#[test]
338fn print_nanoseconds() {
339 let ns1 = LogicalTime(946_684_799_000_000_000);
340 let ns2 = LogicalTime(729_860_000);
341 assert_eq!(format!("{}", ns1), "946_684_799.000_000_000s");
342 assert_eq!(format!("{}", ns1 + ns2), "946_684_799.729_860_000s");
343 assert_eq!(format!("{}", ns2), "729_860_000ns");
344}
345
346#[test]
347fn subsecond_units_are_converted_from_nanoseconds() {
348 let time = LogicalTime::from_secs(2) + LogicalTime::from_nanos(345_678_901);
349
350 assert_eq!(time.subsec_millis(), 345);
351 assert_eq!(time.subsec_micros(), 345_678);
352 assert_eq!(time.subsec_nanos(), 345_678_901);
353
354 let timeval: Timeval = time.into();
355 assert_eq!(timeval.tv_sec, 2);
356 assert_eq!(timeval.tv_usec, 345_678);
357}
358
359#[test]
360fn indefinite_is_recognized_and_nothing_else_is() {
361 assert!(LogicalTime::INDEFINITE.is_indefinite());
362 assert!(!LogicalTime::ZERO.is_indefinite());
363 assert!(!LogicalTime::from_secs(1_767_225_600).is_indefinite());
364 assert!(!LogicalTime(u64::MAX - 1).is_indefinite());
365
366 assert!((LogicalTime(u64::MAX - 10) + LogicalTime::from_secs(1)).is_indefinite());
369}
370
371#[test]
372fn add_saturates_instead_of_overflowing() {
373 let near_max = LogicalTime(u64::MAX - 10);
377
378 assert_eq!(near_max + LogicalTime::from_secs(1), LogicalTime::MAX);
380
381 assert_eq!(near_max + Duration::from_secs(1), LogicalTime::MAX);
383 assert_eq!(
384 LogicalTime::ZERO + Duration::from_secs(u64::MAX / 1_000_000_000 + 1),
385 LogicalTime::MAX
386 );
387
388 assert_eq!(near_max + 100u128, LogicalTime::MAX);
390 assert_eq!(
391 LogicalTime::ZERO + (u128::from(u64::MAX) + 5),
392 LogicalTime::MAX
393 );
394
395 assert_eq!(
397 LogicalTime::from_secs(2) + Duration::from_secs(3),
398 LogicalTime::from_secs(5)
399 );
400}
401
402pub type Microseconds = u64;
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct DetTime {
432 syscalls: u64,
434
435 #[serde(default)]
440 syscall_nanos: Option<u64>,
441
442 rcbs: u64,
445
446 #[serde(default)]
454 weighted_rcbs: Option<u128>,
455
456 nondet_instrs: u64,
458
459 #[serde(default)]
461 extra_nanos: u64,
462
463 starting_micros: Microseconds,
465
466 multiplier: f64,
468
469 #[serde(default)]
473 inherited_nanos: LogicalDuration,
474}
475
476impl Default for DetTime {
478 fn default() -> Self {
479 DetTime {
480 syscalls: 0,
481 syscall_nanos: Some(0),
482 rcbs: 0,
483 weighted_rcbs: None,
484 nondet_instrs: 0,
485 extra_nanos: 0,
486 starting_micros: 0,
487 multiplier: 1.0,
488 inherited_nanos: LogicalTime::ZERO,
489 }
490 }
491}
492
493impl Eq for DetTime {}
494
495impl Ord for DetTime {
497 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
498 let nanos = other.as_nanos();
499 self.as_nanos().cmp(&nanos)
500 }
501}
502
503#[allow(clippy::non_canonical_partial_ord_impl)]
504impl PartialOrd for DetTime {
505 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
506 let nanos = other.as_nanos();
507 self.as_nanos().partial_cmp(&nanos)
508 }
509}
510
511impl PartialEq for DetTime {
512 fn eq(&self, other: &Self) -> bool {
513 self.as_nanos() == other.as_nanos()
514 }
515}
516
517impl From<&DateTime<Utc>> for DetTime {
518 fn from(dt: &DateTime<Utc>) -> Self {
519 DetTime {
520 syscalls: 0,
521 syscall_nanos: Some(0),
522 rcbs: 0,
523 weighted_rcbs: None,
524 nondet_instrs: 0,
525 extra_nanos: 0,
526 starting_micros: micros_from_utc(dt),
527 multiplier: 1.0,
528 inherited_nanos: LogicalTime::ZERO,
529 }
530 }
531}
532
533fn micros_from_utc(dt: &DateTime<Utc>) -> Microseconds {
534 dt.timestamp() as Microseconds * 1_000_000 + dt.timestamp_subsec_micros() as Microseconds
535}
536
537#[allow(clippy::from_over_into)]
539impl Into<Timespec> for DetTime {
540 fn into(self) -> Timespec {
541 self.as_nanos().into()
542 }
543}
544
545impl DetTime {
546 pub fn new(cfg: &Config) -> Self {
548 let additional_multiplier = if cfg.sequentialize_threads && !cfg.use_rcb_time() {
553 500.0
554 } else {
555 1.0
558 };
559 match cfg.clock_multiplier {
560 Some(m) => DetTime::from(&cfg.epoch).with_multiplier(m * additional_multiplier),
561 None => DetTime::from(&cfg.epoch).with_multiplier(additional_multiplier),
562 }
563 }
564
565 pub fn zero() -> Self {
567 DetTime {
568 syscalls: 0,
569 syscall_nanos: Some(0),
570 rcbs: 0,
571 weighted_rcbs: None,
572 nondet_instrs: 0,
573 extra_nanos: 0,
574 starting_micros: 0,
575 multiplier: 1.0,
576 inherited_nanos: LogicalTime::ZERO,
577 }
578 }
579
580 pub fn clone_for_child(&self) -> Self {
584 let mut child = self.clone();
585 child.inherited_nanos = self.without_starting();
586 child
587 }
588
589 pub fn inherited_nanos(&self) -> LogicalDuration {
591 self.inherited_nanos
592 }
593
594 pub fn add_syscall(&mut self) {
596 self.add_syscall_with_cost(NANOS_PER_SYSCALL as u64);
597 }
598
599 pub fn add_syscall_with_cost(&mut self, nanos: u64) {
601 let previous_uniform_nanos = (self.syscalls as f64 * NANOS_PER_SYSCALL) as u64;
602 self.syscalls += 1;
603 match &mut self.syscall_nanos {
604 Some(syscall_nanos) => *syscall_nanos += nanos,
605 None => self.syscall_nanos = Some(previous_uniform_nanos + nanos),
606 }
607 trace!(
608 "[detcore] added syscall cost of {}ns to logical time, yielding: {:?}",
609 nanos, self
610 );
611 }
612
613 pub fn add_rdtsc(&mut self) {
615 self.nondet_instrs += 1;
616 }
617
618 pub fn add_cpuid(&mut self) {
620 self.nondet_instrs += 1;
621 }
622
623 pub fn add_rcbs(&mut self, count: u64) {
625 if let Some(weighted_rcbs) = &mut self.weighted_rcbs {
626 *weighted_rcbs += u128::from(count) * u128::from(RCB_TIME_MULTIPLIER_SCALE);
627 }
628 self.rcbs += count;
629 }
630
631 pub fn add_rcbs_with_multiplier(&mut self, count: u64, factor: RcbTimeMultiplier) {
638 let weighted_rcbs = self
639 .weighted_rcbs
640 .get_or_insert_with(|| u128::from(self.rcbs) * u128::from(RCB_TIME_MULTIPLIER_SCALE));
641 *weighted_rcbs += u128::from(count) * u128::from(factor.units());
642 self.rcbs += count;
643 }
644
645 pub fn advance_to(&mut self, deadline: LogicalTime) {
647 let current = self.as_nanos();
648 assert!(deadline >= current);
649 self.extra_nanos += (deadline - current).as_nanos();
650 }
651
652 pub fn rcbs(&self) -> u64 {
654 self.rcbs
655 }
656
657 pub fn as_nanos(&self) -> LogicalTime {
659 let syscall_nanos = self
662 .syscall_nanos
663 .unwrap_or((self.syscalls as f64 * NANOS_PER_SYSCALL) as u64);
664 let rcb_nanos = self.weighted_rcbs.map_or_else(
665 || self.rcbs as f64 * NANOS_PER_RCB,
666 |weighted| weighted as f64 * NANOS_PER_RCB / RCB_TIME_MULTIPLIER_SCALE as f64,
667 );
668 LogicalTime(
669 (self.starting_micros * 1000)
670 + self.extra_nanos
671 + ((syscall_nanos as f64 * self.multiplier) as u64)
672 + ((rcb_nanos * self.multiplier) as u64)
673 + ((self.nondet_instrs as f64 * NANOS_PER_NONDET_INSTR * self.multiplier) as u64),
674 )
675 }
676
677 pub fn without_starting(&self) -> LogicalDuration {
679 let LogicalTime(t1) = self.as_nanos();
680 LogicalTime(t1 - (self.starting_micros * 1000))
681 }
682
683 pub fn user_cpu_time(&self) -> LogicalDuration {
686 let rcb_nanos = self.weighted_rcbs.map_or_else(
687 || self.rcbs as f64 * NANOS_PER_RCB,
688 |weighted| weighted as f64 * NANOS_PER_RCB / RCB_TIME_MULTIPLIER_SCALE as f64,
689 );
690 LogicalTime(
691 ((rcb_nanos + (self.nondet_instrs as f64 * NANOS_PER_NONDET_INSTR)) * self.multiplier)
692 as u64,
693 )
694 }
695
696 pub fn system_cpu_time(&self) -> LogicalDuration {
699 let syscall_nanos = self
700 .syscall_nanos
701 .unwrap_or((self.syscalls as f64 * NANOS_PER_SYSCALL) as u64);
702 LogicalTime((syscall_nanos as f64 * self.multiplier) as u64)
703 }
704
705 pub fn as_micros(&self) -> Microseconds {
707 self.as_nanos().0 / 1000
708 }
709
710 pub fn with_multiplier(mut self, m: f64) -> Self {
712 self.multiplier = m;
713 self
714 }
715
716 pub fn as_duration(&self) -> std::time::Duration {
718 std::time::Duration::from_nanos(self.as_nanos().0 - self.starting_micros * 1000)
719 }
720}
721
722#[cfg(test)]
723mod rcb_multiplier_tests {
724 use super::*;
725
726 #[test]
729 fn weighted_rcb_time_is_batching_independent() {
730 let factor = RcbTimeMultiplier::from_f64(2.5);
731 let mut one_batch = DetTime::zero();
732 one_batch.add_rcbs_with_multiplier(10, factor);
733
734 let mut split_batches = DetTime::zero();
735 split_batches.add_rcbs_with_multiplier(4, factor);
736 split_batches.add_rcbs_with_multiplier(6, factor);
737
738 assert_eq!(one_batch.rcbs(), 10);
739 assert_eq!(split_batches.rcbs(), 10);
740 assert_eq!(one_batch.as_nanos(), LogicalTime::from_nanos(250));
741 assert_eq!(one_batch.as_nanos(), split_batches.as_nanos());
742 assert_eq!(one_batch.user_cpu_time(), split_batches.user_cpu_time());
743 }
744
745 #[test]
748 fn uniform_rcbs_after_weighted_rcbs_keep_continuity() {
749 let mut time = DetTime::zero();
750 time.add_rcbs_with_multiplier(10, RcbTimeMultiplier::from_f64(0.5));
751 assert_eq!(time.as_nanos(), LogicalTime::from_nanos(50));
752
753 time.add_rcbs(5);
754 assert_eq!(time.rcbs(), 15);
755 assert_eq!(time.as_nanos(), LogicalTime::from_nanos(100));
756 }
757}
758
759#[derive(Default, Debug, Clone, Serialize, Deserialize)]
761pub struct GlobalTime {
762 starting_nanos: LogicalTime,
764
765 time_vector: HashMap<DetTid, LogicalTime>,
768
769 #[serde(default)]
772 inherited_time: HashMap<DetTid, LogicalDuration>,
773
774 extra_time: LogicalTime,
776
777 total: LogicalTime,
780
781 multiplier: f64,
783}
784
785impl GlobalTime {
786 pub fn new(cfg: &Config) -> Self {
788 let base = DetTime::new(cfg);
789 GlobalTime {
790 starting_nanos: LogicalTime::from_micros(micros_from_utc(&cfg.epoch)),
791 time_vector: HashMap::new(),
792 inherited_time: HashMap::new(),
793 extra_time: LogicalTime::from_nanos(0),
794 total: base.as_nanos(),
795 multiplier: cfg.clock_multiplier.unwrap_or(1.0),
796 }
797 }
798
799 pub fn update_global_time(
801 &mut self,
802 tid: DetTid,
803 newtime: LogicalTime,
804 inherited: LogicalDuration,
805 ) {
806 if newtime < self.starting_nanos {
807 panic!(
808 "update_global_time: Cannot set thread {} time to {}, which is before start of container execution {}",
809 tid, newtime, self.starting_nanos
810 );
811 }
812
813 let newtime = newtime - self.starting_nanos;
815 trace!(
816 "[tid {}] ticked its global time component to {}",
817 tid, newtime,
818 );
819 if let Some(old) = self.time_vector.get_mut(&tid) {
820 if *old > newtime {
821 panic!(
822 "Attempted to update tid {} time to {}, but was already {}",
823 tid, newtime, old
824 );
825 }
826 let LogicalTime(diff) = newtime - *old;
828 *old = newtime;
829 self.bump_total(Duration::from_nanos(diff));
833 } else {
834 assert!(
835 inherited <= newtime,
836 "thread {tid} inherited time {inherited} beyond its local duration {newtime}"
837 );
838 self.time_vector.insert(tid, newtime);
839 self.inherited_time.insert(tid, inherited);
840 self.bump_total(Duration::from_nanos((newtime - inherited).0));
844 }
845 }
846
847 fn sanity(&self) {
848 debug_assert_eq!(self.sum_up(), self.total);
849 }
850
851 fn bump_total(&mut self, delta: Duration) {
852 self.total = self.total + delta;
853 self.sanity();
854 }
855
856 fn sum_up(&self) -> LogicalTime {
858 let mut sum = self.starting_nanos;
859 for (tid, tm) in &self.time_vector {
860 sum = sum + (*tm - self.inherited_duration(*tid));
861 }
862 sum + self.extra_time
863 }
864
865 pub fn add_scheduler_time(&mut self) -> LogicalTime {
869 let delta = Duration::from_nanos((NANOS_PER_SCHED * self.multiplier) as u64);
870 self.add_extra_time(delta)
871 }
872
873 pub fn add_extra_time(&mut self, delta: Duration) -> LogicalTime {
880 self.extra_time = self.extra_time + delta;
881 self.bump_total(delta);
883 self.as_nanos()
884 }
885
886 pub fn threads_time(&self, dtid: DetTid) -> LogicalTime {
889 self.starting_nanos + self.threads_duration(dtid)
890 }
891
892 pub fn contains_thread(&self, dtid: DetTid) -> bool {
896 self.time_vector.contains_key(&dtid)
897 }
898
899 pub fn reassign_thread(&mut self, from: DetTid, to: DetTid) {
907 if from == to {
908 return;
909 }
910
911 let survivor_time = self
912 .time_vector
913 .remove(&from)
914 .unwrap_or_else(|| panic!("cannot reassign missing thread clock {from}"));
915 let survivor_inherited = self.inherited_time.remove(&from).unwrap_or_default();
916 let retired_inherited = self.inherited_time.remove(&to).unwrap_or_default();
917 if let Some(retired_leader_time) = self.time_vector.remove(&to) {
918 self.extra_time = self.extra_time + (retired_leader_time - retired_inherited);
919 }
920 self.time_vector.insert(to, survivor_time);
921 self.inherited_time.insert(to, survivor_inherited);
922 self.sanity();
923 }
924
925 fn inherited_duration(&self, dtid: DetTid) -> LogicalDuration {
926 self.inherited_time.get(&dtid).copied().unwrap_or_default()
927 }
928
929 pub fn threads_duration(&self, dtid: DetTid) -> LogicalDuration {
933 *self.time_vector.get(&dtid).unwrap_or_else(|| {
934 panic!(
935 "Trying to extract time for thread {}, but no entry found!",
936 dtid
937 )
938 })
939 }
940
941 pub fn as_nanos(&self) -> LogicalTime {
946 self.total
947 }
948
949 pub fn elapsed_nanos(&self) -> Option<LogicalDuration> {
957 self.total
958 .as_nanos()
959 .checked_sub(self.starting_nanos.as_nanos())
960 .map(LogicalTime::from_nanos)
961 }
962}
963
964#[cfg(test)]
965mod global_time_tests {
966 use super::*;
967
968 fn publish(time: &mut GlobalTime, tid: DetTid, clock: &DetTime) {
969 time.update_global_time(tid, clock.as_nanos(), clock.inherited_nanos());
970 }
971
972 #[test]
973 fn submicrosecond_epoch_starts_with_zero_elapsed_time() {
974 let config = Config {
975 epoch: "2026-09-24T23:52:07.760605385Z".parse().unwrap(),
976 ..Config::default()
977 };
978 let mut time = GlobalTime::new(&config);
979 assert_eq!(time.elapsed_nanos(), Some(LogicalTime::ZERO));
980 time.add_extra_time(Duration::from_nanos(1));
981 assert_eq!(time.elapsed_nanos(), Some(LogicalTime::from_nanos(1)));
982 }
983
984 #[test]
985 fn global_time_behind_its_baseline_is_refused_not_wrapped() {
986 let mut time = GlobalTime::new(&Config::default());
987 time.total = LogicalTime::from_nanos(time.starting_nanos.as_nanos() - 1);
988 assert_eq!(time.elapsed_nanos(), None);
989 }
990
991 #[test]
992 fn descendants_preserve_absolute_clocks_and_charge_only_their_own_work() {
993 let config = Config {
994 clock_multiplier: Some(2.0),
995 ..Config::default()
996 };
997 let root = DetTid::from_raw(3);
998 let child = DetTid::from_raw(4);
999 let grandchild = DetTid::from_raw(5);
1000 let mut time = GlobalTime::new(&config);
1001 let start = time.as_nanos();
1002 let mut root_clock = DetTime::new(&config);
1003 root_clock.add_syscall_with_cost(11);
1004 root_clock.add_rcbs(3);
1005 root_clock.add_rdtsc();
1006 root_clock.advance_to(root_clock.as_nanos() + LogicalTime::from_nanos(1));
1007 assert_eq!(root_clock.without_starting(), LogicalTime::from_nanos(133));
1008 publish(&mut time, root, &root_clock);
1009
1010 let mut child_clock = root_clock.clone_for_child();
1011 assert_eq!(child_clock.as_nanos(), root_clock.as_nanos());
1012 publish(&mut time, child, &child_clock);
1013 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(133));
1014 assert_eq!(time.threads_time(child), child_clock.as_nanos());
1015 child_clock.add_syscall_with_cost(7);
1016 let mut split_rcbs = child_clock.clone();
1017 split_rcbs.add_rcbs_with_multiplier(1, RcbTimeMultiplier::from_f64(0.5));
1018 split_rcbs.add_rcbs_with_multiplier(3, RcbTimeMultiplier::from_f64(0.5));
1019 child_clock.add_rcbs_with_multiplier(4, RcbTimeMultiplier::from_f64(0.5));
1020 assert_eq!(split_rcbs.as_nanos(), child_clock.as_nanos());
1021 assert_eq!(split_rcbs.inherited_nanos(), child_clock.inherited_nanos());
1022 child_clock.add_cpuid();
1023 child_clock.advance_to(child_clock.as_nanos() + LogicalTime::from_nanos(1));
1024 assert_eq!(child_clock.without_starting(), LogicalTime::from_nanos(238));
1025 let snapshot = child_clock.clone();
1026 assert_eq!(snapshot.inherited_nanos(), LogicalTime::from_nanos(133));
1027 publish(&mut time, child, &snapshot);
1028 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(238));
1029 publish(&mut time, child, &snapshot);
1030 publish(&mut time, child, &snapshot);
1031 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(238));
1032
1033 let mut grandchild_clock = child_clock.clone_for_child();
1034 assert_eq!(
1035 grandchild_clock.inherited_nanos(),
1036 LogicalTime::from_nanos(238)
1037 );
1038 publish(&mut time, grandchild, &grandchild_clock);
1039 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(238));
1040 grandchild_clock.add_syscall_with_cost(3);
1041 grandchild_clock.advance_to(grandchild_clock.as_nanos() + LogicalTime::from_nanos(1));
1042 publish(&mut time, grandchild, &grandchild_clock);
1043 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(245));
1044 assert_eq!(time.threads_time(child), child_clock.as_nanos());
1045 assert_eq!(time.threads_time(grandchild), grandchild_clock.as_nanos());
1046 assert_eq!(
1047 time.threads_duration(grandchild),
1048 LogicalTime::from_nanos(245)
1049 );
1050
1051 time.add_scheduler_time();
1052 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_000_245));
1053 }
1054
1055 #[test]
1056 fn exec_preserves_inherited_baselines_and_each_retired_threads_work() {
1057 let config = Config::default();
1058 let ancestor = DetTid::from_raw(3);
1059 let leader = DetTid::from_raw(4);
1060 let worker = DetTid::from_raw(5);
1061 let child_after_exec = DetTid::from_raw(6);
1062 let mut time = GlobalTime::new(&config);
1063 let start = time.as_nanos();
1064 let mut ancestor_clock = DetTime::new(&config);
1065 ancestor_clock.advance_to(start + LogicalTime::from_nanos(1_000));
1066 publish(&mut time, ancestor, &ancestor_clock);
1067 let mut leader_clock = ancestor_clock.clone_for_child();
1068 leader_clock.advance_to(start + LogicalTime::from_nanos(1_100));
1069 publish(&mut time, leader, &leader_clock);
1070 let mut worker_clock = leader_clock.clone_for_child();
1071 worker_clock.advance_to(start + LogicalTime::from_nanos(1_350));
1072 publish(&mut time, worker, &worker_clock);
1073 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_350));
1074
1075 time.reassign_thread(worker, leader);
1076 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_350));
1077 assert_eq!(time.threads_time(leader), worker_clock.as_nanos());
1078 assert!(!time.contains_thread(worker));
1079
1080 let mut reloaded = DetTime::new(&config);
1084 reloaded.advance_to(time.threads_time(leader));
1085 assert_eq!(reloaded.inherited_nanos(), LogicalTime::ZERO);
1086 publish(&mut time, leader, &reloaded);
1087 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_350));
1088 reloaded.advance_to(reloaded.as_nanos() + LogicalTime::from_nanos(1));
1089 publish(&mut time, leader, &reloaded);
1090 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_351));
1091 time.reassign_thread(leader, leader);
1093 let mut leader_reload = DetTime::new(&config);
1094 leader_reload.advance_to(time.threads_time(leader));
1095 publish(&mut time, leader, &leader_reload);
1096 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_351));
1097 let mut after_exec = reloaded.clone_for_child();
1098 publish(&mut time, child_after_exec, &after_exec);
1099 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_351));
1100 after_exec.advance_to(after_exec.as_nanos() + LogicalTime::from_nanos(1));
1101 publish(&mut time, child_after_exec, &after_exec);
1102 assert_eq!(time.as_nanos(), start + LogicalTime::from_nanos(1_352));
1103 }
1104
1105 #[test]
1106 fn inherited_clock_survives_rpc_serialization_and_legacy_json_defaults() {
1107 let mut parent = DetTime::zero();
1108 parent.add_syscall_with_cost(13);
1109 let child = parent.clone_for_child();
1110 let wire = bincode::serde::encode_to_vec(
1113 (child.clone(), 0x1234_5678_u64),
1114 bincode::config::legacy(),
1115 )
1116 .unwrap();
1117 let ((restored, following), consumed): ((DetTime, u64), usize) =
1118 bincode::serde::decode_from_slice(&wire, bincode::config::legacy()).unwrap();
1119 assert_eq!(consumed, wire.len());
1120 assert_eq!(following, 0x1234_5678);
1121 assert_eq!(restored.as_nanos(), child.as_nanos());
1122 assert_eq!(restored.inherited_nanos(), LogicalTime::from_nanos(13));
1123
1124 let mut legacy = serde_json::to_value(parent.clone()).unwrap();
1125 legacy.as_object_mut().unwrap().remove("inherited_nanos");
1126 let restored: DetTime = serde_json::from_value(legacy).unwrap();
1127 assert_eq!(restored.as_nanos(), parent.as_nanos());
1128 assert_eq!(restored.inherited_nanos(), LogicalTime::ZERO);
1129 }
1130
1131 #[test]
1132 #[should_panic(expected = "beyond its local duration")]
1133 fn inherited_baseline_cannot_exceed_the_first_local_duration() {
1134 let config = Config::default();
1135 let mut time = GlobalTime::new(&config);
1136 time.update_global_time(
1137 DetTid::from_raw(3),
1138 time.as_nanos(),
1139 LogicalTime::from_nanos(1),
1140 );
1141 }
1142
1143 #[test]
1144 fn exec_reassigns_survivor_clock_without_losing_aggregate_time() {
1145 let config = Config::default();
1146 let mut time = GlobalTime::new(&config);
1147 let leader = DetTid::from_raw(17);
1148 let worker = DetTid::from_raw(18);
1149 let start = DetTime::new(&config).as_nanos();
1150 let leader_time = start + LogicalTime::from_nanos(100);
1151 let worker_time = start + LogicalTime::from_nanos(250);
1152 time.update_global_time(leader, leader_time, LogicalTime::ZERO);
1153 time.update_global_time(worker, worker_time, LogicalTime::ZERO);
1154 let total_before = time.as_nanos();
1155
1156 time.reassign_thread(worker, leader);
1157
1158 assert_eq!(time.as_nanos(), total_before);
1159 assert_eq!(time.threads_time(leader), worker_time);
1160 assert!(time.contains_thread(leader));
1161 assert!(!time.contains_thread(worker));
1162 }
1163}