1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::{LazyLock, Mutex};
3use std::time::SystemTime;
4
5type CurrentTimeFn = Box<dyn Fn() -> Option<SystemTime> + Send + Sync>;
7
8type EventTimeFn = Box<dyn Fn(i32) -> Option<SystemTime> + Send + Sync>;
10
11pub const EPICS_EPOCH_UNIX_SECS: u64 = 631_152_000;
14
15pub fn epics_epoch() -> SystemTime {
31 SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(EPICS_EPOCH_UNIX_SECS)
32}
33
34struct CurrentTimeProvider {
35 name: String,
36 priority: i32,
37 get_time: CurrentTimeFn,
38 interrupt_safe: bool,
42 is_os_default: bool,
45}
46
47struct EventTimeProvider {
48 name: String,
49 priority: i32,
50 get_event: EventTimeFn,
51 interrupt_safe: bool,
53}
54
55struct GeneralTimeInner {
56 current_providers: Vec<CurrentTimeProvider>,
57 event_providers: Vec<EventTimeProvider>,
58 last_provided_time: SystemTime,
60 event_times: [SystemTime; 256],
62 last_best_time: SystemTime,
64 last_current_name: Option<String>,
66 last_event_name: Option<String>,
68 use_osd_get_current: bool,
76 sync_hooks: Vec<Box<dyn Fn(SystemTime) + Send + Sync>>,
79}
80
81impl GeneralTimeInner {
82 fn new() -> Self {
83 let mut inner = Self {
84 current_providers: Vec::new(),
85 event_providers: Vec::new(),
86 last_provided_time: epics_epoch(),
87 event_times: [epics_epoch(); 256],
88 last_best_time: epics_epoch(),
89 last_current_name: None,
90 last_event_name: None,
91 use_osd_get_current: true,
92 sync_hooks: Vec::new(),
93 };
94 inner.current_providers.push(CurrentTimeProvider {
96 name: "OS Clock".to_string(),
97 priority: 999,
98 get_time: Box::new(|| Some(SystemTime::now())),
99 interrupt_safe: true,
100 is_os_default: true,
101 });
102 inner
103 }
104}
105
106static GENERAL_TIME: LazyLock<Mutex<GeneralTimeInner>> =
107 LazyLock::new(|| Mutex::new(GeneralTimeInner::new()));
108
109static ERROR_COUNTS: AtomicU64 = AtomicU64::new(0);
110
111pub fn register_current_provider(
113 name: impl Into<String>,
114 priority: i32,
115 get_time: impl Fn() -> Option<SystemTime> + Send + Sync + 'static,
116) {
117 register_current_provider_impl(name.into(), priority, Box::new(get_time), false);
118}
119
120pub fn register_int_current_provider(
127 name: impl Into<String>,
128 priority: i32,
129 get_time: impl Fn() -> Option<SystemTime> + Send + Sync + 'static,
130) {
131 register_current_provider_impl(name.into(), priority, Box::new(get_time), true);
132}
133
134fn register_current_provider_impl(
135 name: String,
136 priority: i32,
137 get_time: CurrentTimeFn,
138 interrupt_safe: bool,
139) {
140 let mut inner = GENERAL_TIME.lock().unwrap();
141 let provider = CurrentTimeProvider {
142 name,
143 priority,
144 get_time,
145 interrupt_safe,
146 is_os_default: false,
147 };
148 let pos = inner
149 .current_providers
150 .iter()
151 .position(|p| p.priority > priority)
152 .unwrap_or(inner.current_providers.len());
153 inner.current_providers.insert(pos, provider);
154 inner.use_osd_get_current = false;
158}
159
160pub fn register_event_provider(
162 name: impl Into<String>,
163 priority: i32,
164 get_event: impl Fn(i32) -> Option<SystemTime> + Send + Sync + 'static,
165) {
166 register_event_provider_impl(name.into(), priority, Box::new(get_event), false);
167}
168
169pub fn register_int_event_provider(
175 name: impl Into<String>,
176 priority: i32,
177 get_event: impl Fn(i32) -> Option<SystemTime> + Send + Sync + 'static,
178) {
179 register_event_provider_impl(name.into(), priority, Box::new(get_event), true);
180}
181
182fn register_event_provider_impl(
183 name: String,
184 priority: i32,
185 get_event: EventTimeFn,
186 interrupt_safe: bool,
187) {
188 let mut inner = GENERAL_TIME.lock().unwrap();
189 let provider = EventTimeProvider {
190 name,
191 priority,
192 get_event,
193 interrupt_safe,
194 };
195 let pos = inner
196 .event_providers
197 .iter()
198 .position(|p| p.priority > priority)
199 .unwrap_or(inner.event_providers.len());
200 inner.event_providers.insert(pos, provider);
201}
202
203pub fn register_clock_sync_hook<F>(hook: F)
219where
220 F: Fn(SystemTime) + Send + Sync + 'static,
221{
222 let mut inner = GENERAL_TIME.lock().unwrap();
223 inner.sync_hooks.push(Box::new(hook));
224}
225
226pub fn notify_clock_sync(t_synced: SystemTime) {
238 let inner = GENERAL_TIME.lock().unwrap();
239 for hook in &inner.sync_hooks {
240 hook(t_synced);
241 }
242}
243
244pub fn get_current() -> SystemTime {
257 let mut inner = GENERAL_TIME.lock().unwrap();
258
259 if inner.use_osd_get_current {
261 if let Some(idx) = inner.current_providers.iter().position(|p| p.is_os_default) {
262 if let Some(t) = (inner.current_providers[idx].get_time)() {
263 let name = inner.current_providers[idx].name.clone();
264 inner.last_provided_time = t;
265 inner.last_current_name = Some(name);
266 return t;
267 }
268 }
269 }
272
273 for i in 0..inner.current_providers.len() {
274 if let Some(t) = (inner.current_providers[i].get_time)() {
275 let name = inner.current_providers[i].name.clone();
276 if t >= inner.last_provided_time {
277 inner.last_provided_time = t;
278 inner.last_current_name = Some(name);
279 return t;
280 } else {
281 ERROR_COUNTS.fetch_add(1, Ordering::Relaxed);
282 inner.last_current_name = Some(name);
283 return inner.last_provided_time;
284 }
285 }
286 }
287 ERROR_COUNTS.fetch_add(1, Ordering::Relaxed);
289 inner.last_provided_time
290}
291
292pub fn get_current_except_priority(ignore_priority: i32) -> Option<(SystemTime, i32)> {
310 let inner = GENERAL_TIME.lock().unwrap();
311 for p in &inner.current_providers {
312 if (ignore_priority > 0 && p.priority == ignore_priority)
313 || (ignore_priority < 0 && p.priority != -ignore_priority)
314 {
315 continue;
316 }
317 if let Some(t) = (p.get_time)() {
318 return Some((t, p.priority));
319 }
320 }
321 None
322}
323
324pub fn get_current_int() -> Option<SystemTime> {
334 let inner = GENERAL_TIME.lock().unwrap();
335 for p in &inner.current_providers {
336 if !p.interrupt_safe {
337 continue;
338 }
339 if let Some(t) = (p.get_time)() {
340 return Some(t);
341 }
342 }
343 None
344}
345
346pub fn get_event_int(event: i32) -> Option<SystemTime> {
353 let inner = GENERAL_TIME.lock().unwrap();
354 for p in &inner.event_providers {
355 if !p.interrupt_safe {
356 continue;
357 }
358 if let Some(t) = (p.get_event)(event) {
359 return Some(t);
360 }
361 }
362 None
363}
364
365pub fn highest_current_name() -> Option<String> {
372 GENERAL_TIME
373 .lock()
374 .unwrap()
375 .current_providers
376 .first()
377 .map(|p| p.name.clone())
378}
379
380pub fn get_event(event: i32) -> SystemTime {
387 if event == 0 {
388 return get_current();
389 }
390
391 let mut inner = GENERAL_TIME.lock().unwrap();
392
393 if event == -1 {
394 for i in 0..inner.current_providers.len() {
396 if let Some(t) = (inner.current_providers[i].get_time)() {
397 let name = inner.current_providers[i].name.clone();
398 if t >= inner.last_best_time {
399 inner.last_best_time = t;
400 inner.last_event_name = Some(name);
401 return t;
402 } else {
403 ERROR_COUNTS.fetch_add(1, Ordering::Relaxed);
404 inner.last_event_name = Some(name);
405 return inner.last_best_time;
406 }
407 }
408 }
409 ERROR_COUNTS.fetch_add(1, Ordering::Relaxed);
410 return inner.last_best_time;
411 }
412
413 for i in 0..inner.event_providers.len() {
415 if let Some(t) = (inner.event_providers[i].get_event)(event) {
416 let name = inner.event_providers[i].name.clone();
417 inner.last_event_name = Some(name);
418
419 if (1..=255).contains(&event) {
420 let slot = event as usize;
421 if t >= inner.event_times[slot] {
422 inner.event_times[slot] = t;
423 return t;
424 } else {
425 ERROR_COUNTS.fetch_add(1, Ordering::Relaxed);
426 return inner.event_times[slot];
427 }
428 }
429 return t;
431 }
432 }
433
434 drop(inner);
436 get_current()
437}
438
439pub fn install_last_resort_event_provider() {
441 register_event_provider("OS Clock", 999, |_| Some(SystemTime::now()));
442}
443
444pub fn error_counts() -> u64 {
446 ERROR_COUNTS.load(Ordering::Relaxed)
447}
448
449pub fn reset_error_counts() {
451 ERROR_COUNTS.store(0, Ordering::Relaxed);
452}
453
454pub fn current_provider_name() -> Option<String> {
456 GENERAL_TIME.lock().unwrap().last_current_name.clone()
457}
458
459pub fn event_provider_name() -> Option<String> {
461 GENERAL_TIME.lock().unwrap().last_event_name.clone()
462}
463
464fn format_time_sample(t: SystemTime) -> String {
471 let dt: chrono::DateTime<chrono::Local> = t.into();
472 dt.format("%Y-%m-%d %H:%M:%S.%6f").to_string()
473}
474
475pub fn report(level: i32) -> String {
491 let inner = GENERAL_TIME.lock().unwrap();
492 let mut out = String::new();
493
494 out.push_str(&format!(
496 "Backwards time errors prevented {} times.\n\n",
497 error_counts()
498 ));
499
500 out.push_str("Current Time Providers:\n");
502 if inner.current_providers.is_empty() {
503 out.push_str("\tNo Providers registered.\n");
504 } else {
505 for p in &inner.current_providers {
506 out.push_str(&format!(" \"{}\", priority = {}\n", p.name, p.priority));
507 if level != 0 {
508 match (p.get_time)() {
509 Some(t) => {
510 out.push_str(&format!("\tCurrent Time is {}.\n", format_time_sample(t)))
511 }
512 None => out.push_str("\tCurrent Time not available\n"),
513 }
514 }
515 }
516 out.push('\n');
518 }
519
520 out.push_str("Event Time Providers:\n");
522 if inner.event_providers.is_empty() {
523 out.push_str("\tNo Providers registered.\n");
524 } else {
525 for p in &inner.event_providers {
526 out.push_str(&format!(" \"{}\", priority = {}\n", p.name, p.priority));
527 }
528 out.push('\n');
529 }
530
531 out
532}
533
534#[cfg(test)]
536fn _reset_for_testing() {
537 let mut inner = GENERAL_TIME.lock().unwrap();
538 *inner = GeneralTimeInner::new();
539 ERROR_COUNTS.store(0, Ordering::Relaxed);
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545 use std::time::Duration;
546
547 static TEST_LOCK: Mutex<()> = Mutex::new(());
549
550 #[test]
551 fn os_clock_default_returns_reasonable_time() {
552 let _g = TEST_LOCK.lock().unwrap();
553 _reset_for_testing();
554 let t = get_current();
555 let secs = t.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
556 assert!(secs > 1_577_836_800, "time should be after 2020");
558 }
559
560 #[test]
561 fn custom_provider_overrides_os_clock() {
562 let _g = TEST_LOCK.lock().unwrap();
563 _reset_for_testing();
564 let fixed = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
565 register_current_provider("Test Clock", 10, move || Some(fixed));
566
567 let t = get_current();
568 assert_eq!(t, fixed);
569 assert_eq!(current_provider_name().as_deref(), Some("Test Clock"));
570 }
571
572 #[test]
573 fn provider_returning_none_falls_through() {
574 let _g = TEST_LOCK.lock().unwrap();
575 _reset_for_testing();
576 let fixed = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
577 register_current_provider("Broken", 1, || None);
579 register_current_provider("Fallback", 50, move || Some(fixed));
581
582 let t = get_current();
583 assert_eq!(t, fixed);
584 assert_eq!(current_provider_name().as_deref(), Some("Fallback"));
585 }
586
587 #[test]
588 fn monotonic_enforcement() {
589 let _g = TEST_LOCK.lock().unwrap();
590 _reset_for_testing();
591 let t1 = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
592 let t2 = SystemTime::UNIX_EPOCH + Duration::from_secs(1_999_999_000); let call = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
595 let call_c = call.clone();
596
597 register_current_provider("Stepper", 10, move || {
598 let n = call_c.fetch_add(1, Ordering::Relaxed);
599 match n {
600 0 => Some(t1),
601 _ => Some(t2),
602 }
603 });
604
605 reset_error_counts();
606 let first = get_current();
607 assert_eq!(first, t1);
608 assert_eq!(error_counts(), 0);
609
610 let second = get_current();
611 assert_eq!(second, t1);
613 assert_eq!(error_counts(), 1);
614 }
615
616 #[test]
617 fn event_zero_delegates_to_get_current() {
618 let _g = TEST_LOCK.lock().unwrap();
619 _reset_for_testing();
620 let fixed = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
621 register_current_provider("Fixed", 10, move || Some(fixed));
622
623 let t = get_event(0);
624 assert_eq!(t, fixed);
625 }
626
627 #[test]
628 fn event_per_slot_ratcheting() {
629 let _g = TEST_LOCK.lock().unwrap();
630 _reset_for_testing();
631 let t1 = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
632 let t2 = SystemTime::UNIX_EPOCH + Duration::from_secs(1_999_999_000); let t3 = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_001_000); let call = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
636 let call_c = call.clone();
637
638 register_event_provider("EventSrc", 10, move |_ev| {
639 let n = call_c.fetch_add(1, Ordering::Relaxed);
640 match n {
641 0 => Some(t1),
642 1 => Some(t2),
643 _ => Some(t3),
644 }
645 });
646
647 reset_error_counts();
648 let first = get_event(42);
649 assert_eq!(first, t1);
650
651 let second = get_event(42);
652 assert_eq!(second, t1);
654 assert_eq!(error_counts(), 1);
655
656 let third = get_event(42);
657 assert_eq!(third, t3);
658 }
659
660 #[test]
661 fn event_best_time_ratcheting() {
662 let _g = TEST_LOCK.lock().unwrap();
663 _reset_for_testing();
664 let t1 = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
665 let t2 = SystemTime::UNIX_EPOCH + Duration::from_secs(1_999_999_000);
666
667 let call = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
668 let call_c = call.clone();
669
670 register_current_provider("BestSrc", 10, move || {
671 let n = call_c.fetch_add(1, Ordering::Relaxed);
672 match n {
673 0 => Some(t1),
674 _ => Some(t2),
675 }
676 });
677
678 reset_error_counts();
679 let first = get_event(-1);
680 assert_eq!(first, t1);
681
682 let second = get_event(-1);
683 assert_eq!(second, t1); assert_eq!(error_counts(), 1);
685 }
686
687 #[test]
688 fn error_counts_reset() {
689 let _g = TEST_LOCK.lock().unwrap();
690 _reset_for_testing();
691 let t_back = SystemTime::UNIX_EPOCH + Duration::from_secs(1);
692 register_current_provider("AlwaysBack", 10, move || Some(t_back));
693
694 reset_error_counts();
702 assert_eq!(error_counts(), 0);
703
704 let t_high = SystemTime::UNIX_EPOCH + Duration::from_secs(3_000_000_000);
706 {
707 let mut inner = GENERAL_TIME.lock().unwrap();
708 inner.last_best_time = t_high;
709 }
710 let _ = get_event(-1);
712 assert!(error_counts() > 0);
713
714 reset_error_counts();
715 assert_eq!(error_counts(), 0);
716 }
717
718 #[test]
725 fn sync_hooks_fire_in_registration_order() {
726 use std::sync::{Arc, Mutex};
727 let captured: Arc<Mutex<Vec<(usize, SystemTime)>>> = Arc::new(Mutex::new(Vec::new()));
728
729 let cap1 = captured.clone();
730 register_clock_sync_hook(move |t| {
731 cap1.lock().unwrap().push((1, t));
732 });
733 let cap2 = captured.clone();
734 register_clock_sync_hook(move |t| {
735 cap2.lock().unwrap().push((2, t));
736 });
737
738 let synced = SystemTime::UNIX_EPOCH + Duration::from_secs(5_000_000_000);
739 notify_clock_sync(synced);
740
741 let log = captured.lock().unwrap();
742 let ours: Vec<_> = log.iter().filter(|(id, _)| *id == 1 || *id == 2).collect();
744 assert!(
745 ours.len() >= 2,
746 "both hooks must have fired at least once: {ours:?}"
747 );
748 let last1_idx = ours
752 .iter()
753 .rposition(|(id, t)| *id == 1 && *t == synced)
754 .expect("hook 1 fired with our synced timestamp");
755 let last2_idx = ours
756 .iter()
757 .rposition(|(id, t)| *id == 2 && *t == synced)
758 .expect("hook 2 fired with our synced timestamp");
759 assert!(
760 last1_idx < last2_idx,
761 "hook 1 must fire before hook 2 (registration order)"
762 );
763 }
764
765 #[test]
769 fn os_clock_only_bypasses_ratchet() {
770 let _g = TEST_LOCK.lock().unwrap();
771 _reset_for_testing();
772
773 let t_high = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
777 let t_low = SystemTime::UNIX_EPOCH + Duration::from_secs(1_999_999_000);
778 let call = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
779 let call_c = call.clone();
780 {
781 let mut inner = GENERAL_TIME.lock().unwrap();
782 inner.current_providers.clear();
783 inner.current_providers.push(CurrentTimeProvider {
784 name: "OS Clock".to_string(),
785 priority: 999,
786 get_time: Box::new(move || {
787 let n = call_c.fetch_add(1, Ordering::Relaxed);
788 Some(if n == 0 { t_high } else { t_low })
789 }),
790 interrupt_safe: true,
791 is_os_default: true,
792 });
793 inner.use_osd_get_current = true;
794 }
795
796 reset_error_counts();
797 let first = get_current();
798 assert_eq!(first, t_high);
799 let second = get_current();
801 assert_eq!(
802 second, t_low,
803 "OS-clock-only path must follow a backward step (C useOsdGetCurrent)"
804 );
805 assert_eq!(
806 error_counts(),
807 0,
808 "OS-clock-only backward step must not count an error"
809 );
810 }
811
812 #[test]
815 fn registering_provider_enables_ratchet() {
816 let _g = TEST_LOCK.lock().unwrap();
817 _reset_for_testing();
818 let t_high = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
819 let t_low = SystemTime::UNIX_EPOCH + Duration::from_secs(1_999_999_000);
820 let call = std::sync::Arc::new(std::sync::atomic::AtomicU32::new(0));
821 let call_c = call.clone();
822 register_current_provider("Stepper", 10, move || {
823 let n = call_c.fetch_add(1, Ordering::Relaxed);
824 Some(if n == 0 { t_high } else { t_low })
825 });
826
827 reset_error_counts();
828 assert_eq!(get_current(), t_high);
829 assert_eq!(get_current(), t_high);
832 assert_eq!(error_counts(), 1);
833 }
834
835 #[test]
838 fn except_priority_skips_named_provider() {
839 let _g = TEST_LOCK.lock().unwrap();
840 _reset_for_testing();
841 let t10 = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
842 let t20 = SystemTime::UNIX_EPOCH + Duration::from_secs(1_900_000_000);
843 register_current_provider("P10", 10, move || Some(t10));
844 register_current_provider("P20", 20, move || Some(t20));
845
846 let (t, prio) = get_current_except_priority(10).expect("P20 answers");
848 assert_eq!(t, t20);
849 assert_eq!(prio, 20);
850
851 let (t, prio) = get_current_except_priority(-10).expect("P10 answers");
853 assert_eq!(t, t10);
854 assert_eq!(prio, 10);
855 }
856
857 #[test]
860 fn int_providers_only_seen_by_int_queries() {
861 let _g = TEST_LOCK.lock().unwrap();
862 _reset_for_testing();
863 let t_int = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_000);
864 register_int_current_provider("IntClock", 5, move || Some(t_int));
865 assert_eq!(get_current_int(), Some(t_int));
866
867 let t_evt = SystemTime::UNIX_EPOCH + Duration::from_secs(2_000_000_500);
868 register_int_event_provider("IntEvent", 5, move |_| Some(t_evt));
869 assert_eq!(get_event_int(7), Some(t_evt));
870 }
871
872 #[test]
875 fn int_event_query_none_without_int_provider() {
876 let _g = TEST_LOCK.lock().unwrap();
877 _reset_for_testing();
878 register_event_provider("Plain", 10, |_| {
880 Some(SystemTime::UNIX_EPOCH + Duration::from_secs(1))
881 });
882 assert_eq!(get_event_int(3), None);
883 }
884
885 #[test]
888 fn highest_current_name_is_top_priority() {
889 let _g = TEST_LOCK.lock().unwrap();
890 _reset_for_testing();
891 assert_eq!(highest_current_name().as_deref(), Some("OS Clock"));
893 register_current_provider("Primary", 1, || None);
894 assert_eq!(highest_current_name().as_deref(), Some("Primary"));
895 }
896
897 #[test]
899 fn report_format_matches_general_time_report() {
900 let _g = TEST_LOCK.lock().unwrap();
901 _reset_for_testing();
902 let r = report(0);
903 assert!(
905 r.starts_with("Backwards time errors prevented 0 times.\n\n"),
906 "report must lead with the backwards-error line: {r:?}"
907 );
908 assert!(r.contains("Current Time Providers:\n"));
909 assert!(
911 r.contains(" \"OS Clock\", priority = 999\n"),
912 "provider line must use C `\"name\", priority = N` form: {r:?}"
913 );
914 assert!(
916 r.contains("Event Time Providers:\n\tNo Providers registered.\n"),
917 "empty event list must print the C placeholder: {r:?}"
918 );
919 assert!(!r.contains("\" priority "));
921 assert!(!r.contains("(none)"));
922 }
923
924 #[test]
927 fn report_level_one_prints_time_sample() {
928 let _g = TEST_LOCK.lock().unwrap();
929 _reset_for_testing();
930 let r = report(1);
931 assert!(
932 r.contains("\tCurrent Time is "),
933 "level>0 report must print a per-provider time sample: {r:?}"
934 );
935 }
936
937 #[test]
940 fn ratchet_seeds_at_epics_epoch() {
941 let secs = epics_epoch()
943 .duration_since(SystemTime::UNIX_EPOCH)
944 .unwrap()
945 .as_secs();
946 assert_eq!(secs, EPICS_EPOCH_UNIX_SECS);
947 assert_eq!(secs, 631_152_000);
948
949 let _g = TEST_LOCK.lock().unwrap();
950 _reset_for_testing();
951 let inner = GENERAL_TIME.lock().unwrap();
952 assert_eq!(inner.last_provided_time, epics_epoch());
953 assert_eq!(inner.last_best_time, epics_epoch());
954 assert!(inner.event_times.iter().all(|t| *t == epics_epoch()));
955 }
956}