1pub use crate::storage::faulty::Config as FaultConfig;
46use crate::{
47 child_label,
48 network::{
49 audited::Network as AuditedNetwork, deterministic::Network as DeterministicNetwork,
50 metered::Network as MeteredNetwork,
51 },
52 prefixed_name,
53 storage::{
54 audited::Storage as AuditedStorage, faulty::Storage as FaultyStorage,
55 memory::Storage as MemStorage, metered::Storage as MeteredStorage,
56 },
57 telemetry::metrics::{
58 add_attribute, raw, task::Label, validate_label, Counter, CounterFamily, GaugeFamily,
59 Metric, Register, Registered, Registry,
60 },
61 utils::{
62 signal::{Signal, Stopper},
63 supervision::Tree,
64 Panicker,
65 },
66 BufferPool, BufferPoolConfig, Clock, Error, Execution, Handle, IoBufs, ListenerOf, Name,
67 Panicked, METRICS_PREFIX,
68};
69#[cfg(feature = "external")]
70use crate::{Blocker, Pacer};
71use commonware_codec::Encode;
72use commonware_formatting::hex;
73use commonware_macros::select;
74use commonware_parallel::{Rayon, ThreadPool};
75use commonware_utils::{
76 sync::{Mutex, RwLock},
77 time::SYSTEM_TIME_PRECISION,
78 Cached, SystemTimeExt,
79};
80#[cfg(feature = "external")]
81use futures::task::noop_waker;
82use futures::{
83 task::{waker, ArcWake},
84 Future,
85};
86use governor::clock::{Clock as GClock, ReasonablyRealtime};
87#[cfg(feature = "external")]
88use pin_project::pin_project;
89use rand::{prelude::SliceRandom, rngs::StdRng, CryptoRng, Rng, SeedableRng, TryCryptoRng, TryRng};
90use rayon::{ThreadPoolBuildError, ThreadPoolBuilder};
91use sha2::{Digest as _, Sha256};
92use std::{
93 collections::{BTreeMap, BinaryHeap, HashMap},
94 convert::Infallible,
95 mem::{replace, take},
96 net::{IpAddr, SocketAddr},
97 num::NonZeroUsize,
98 panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
99 pin::Pin,
100 sync::{Arc, Weak},
101 task::{self, Poll, Waker},
102 time::{Duration, SystemTime, UNIX_EPOCH},
103};
104use tracing::trace;
105
106#[derive(Debug)]
107struct Metrics {
108 iterations: Counter,
109 tasks_spawned: CounterFamily<Label>,
110 tasks_running: GaugeFamily<Label>,
111 task_polls: CounterFamily<Label>,
112}
113
114impl Metrics {
115 pub fn init(registry: &mut impl Register) -> Self {
116 Self {
117 iterations: registry.register(
118 "iterations",
119 "Total number of iterations",
120 raw::Counter::default(),
121 ),
122 tasks_spawned: registry.register(
123 "tasks_spawned",
124 "Total number of tasks spawned",
125 raw::Family::default(),
126 ),
127 tasks_running: registry.register(
128 "tasks_running",
129 "Number of tasks currently running",
130 raw::Family::default(),
131 ),
132 task_polls: registry.register(
133 "task_polls",
134 "Total number of task polls",
135 raw::Family::default(),
136 ),
137 }
138 }
139}
140
141type Digest = [u8; 32];
143
144pub(crate) struct AuditHasher(Sha256);
146
147impl AuditHasher {
148 pub(crate) fn new() -> Self {
150 Self(Sha256::new())
151 }
152
153 pub(crate) fn update(&mut self, value: impl AsRef<[u8]>) {
155 let value = value.as_ref();
156 self.0.update((value.len() as u64).to_be_bytes());
157 self.0.update(value);
158 }
159
160 pub(crate) fn update_bufs(&mut self, bufs: &IoBufs) {
165 self.0.update((bufs.len() as u64).to_be_bytes());
166 bufs.for_each_chunk(|chunk| self.0.update(chunk));
167 }
168
169 pub(crate) fn finalize(self) -> Digest {
171 self.0.finalize().into()
172 }
173}
174
175pub struct Auditor {
177 digest: Mutex<Digest>,
178}
179
180impl Default for Auditor {
181 fn default() -> Self {
182 Self {
183 digest: Digest::default().into(),
184 }
185 }
186}
187
188impl Auditor {
189 pub(crate) fn event<F>(&self, label: &'static [u8], payload: F)
193 where
194 F: FnOnce(&mut AuditHasher),
195 {
196 let mut digest = self.digest.lock();
197
198 let mut hasher = AuditHasher::new();
199 hasher.update(digest.as_ref());
200 hasher.update(label);
201 payload(&mut hasher);
202
203 *digest = hasher.finalize();
204 }
205
206 pub fn state(&self) -> String {
211 let hash = self.digest.lock();
212 hex(hash.as_ref())
213 }
214}
215
216pub type BoxDynRng = Box<dyn CryptoRng + Send + 'static>;
218
219pub struct Config {
221 rng: BoxDynRng,
223
224 cycle: Duration,
227
228 start_time: SystemTime,
230
231 timeout: Option<Duration>,
233
234 catch_panics: bool,
236
237 storage_fault_cfg: FaultConfig,
240
241 network_buffer_pool_cfg: BufferPoolConfig,
243
244 storage_buffer_pool_cfg: BufferPoolConfig,
246}
247
248impl Config {
249 pub fn new() -> Self {
251 cfg_if::cfg_if! {
252 if #[cfg(miri)] {
253 let network_buffer_pool_cfg = BufferPoolConfig::for_network()
255 .with_max_per_class(commonware_utils::NZU32!(32))
256 .with_thread_cache_disabled();
257 let storage_buffer_pool_cfg = BufferPoolConfig::for_storage()
258 .with_max_per_class(commonware_utils::NZU32!(32))
259 .with_thread_cache_disabled();
260 } else {
261 let network_buffer_pool_cfg =
262 BufferPoolConfig::for_network().with_thread_cache_disabled();
263 let storage_buffer_pool_cfg =
264 BufferPoolConfig::for_storage().with_thread_cache_disabled();
265 }
266 }
267
268 Self {
269 rng: Box::new(StdRng::seed_from_u64(42)),
270 cycle: Duration::from_millis(1),
271 start_time: UNIX_EPOCH,
272 timeout: None,
273 catch_panics: false,
274 storage_fault_cfg: FaultConfig::default(),
275 network_buffer_pool_cfg,
276 storage_buffer_pool_cfg,
277 }
278 }
279
280 pub fn with_seed(self, seed: u64) -> Self {
283 self.with_rng(Box::new(StdRng::seed_from_u64(seed)))
284 }
285
286 pub fn with_rng(mut self, rng: BoxDynRng) -> Self {
292 self.rng = rng;
293 self
294 }
295
296 pub const fn with_cycle(mut self, cycle: Duration) -> Self {
298 self.cycle = cycle;
299 self
300 }
301 pub const fn with_start_time(mut self, start_time: SystemTime) -> Self {
303 self.start_time = start_time;
304 self
305 }
306 pub const fn with_timeout(mut self, timeout: Option<Duration>) -> Self {
308 self.timeout = timeout;
309 self
310 }
311 pub const fn with_catch_panics(mut self, catch_panics: bool) -> Self {
313 self.catch_panics = catch_panics;
314 self
315 }
316 pub const fn with_network_buffer_pool_config(mut self, cfg: BufferPoolConfig) -> Self {
318 self.network_buffer_pool_cfg = cfg;
319 self
320 }
321 pub const fn with_storage_buffer_pool_config(mut self, cfg: BufferPoolConfig) -> Self {
323 self.storage_buffer_pool_cfg = cfg;
324 self
325 }
326
327 pub const fn with_storage_fault_config(mut self, faults: FaultConfig) -> Self {
333 self.storage_fault_cfg = faults;
334 self
335 }
336
337 pub const fn cycle(&self) -> Duration {
340 self.cycle
341 }
342 pub const fn start_time(&self) -> SystemTime {
344 self.start_time
345 }
346 pub const fn timeout(&self) -> Option<Duration> {
348 self.timeout
349 }
350 pub const fn catch_panics(&self) -> bool {
352 self.catch_panics
353 }
354 pub const fn network_buffer_pool_config(&self) -> &BufferPoolConfig {
356 &self.network_buffer_pool_cfg
357 }
358 pub const fn storage_buffer_pool_config(&self) -> &BufferPoolConfig {
360 &self.storage_buffer_pool_cfg
361 }
362
363 pub fn assert(&self) {
365 assert!(
366 self.cycle != Duration::default() || self.timeout.is_none(),
367 "cycle duration must be non-zero when timeout is set",
368 );
369 assert!(
370 self.cycle >= SYSTEM_TIME_PRECISION,
371 "cycle duration must be greater than or equal to system time precision"
372 );
373 assert!(
374 self.start_time >= UNIX_EPOCH,
375 "start time must be greater than or equal to unix epoch"
376 );
377 }
378}
379
380impl Default for Config {
381 fn default() -> Self {
382 Self::new()
383 }
384}
385
386pub struct Executor {
388 registry: Registry,
389 cycle: Duration,
390 deadline: Option<SystemTime>,
391 metrics: Arc<Metrics>,
392 auditor: Arc<Auditor>,
393 rng: Arc<Mutex<BoxDynRng>>,
394 time: Mutex<SystemTime>,
395 tasks: Arc<Tasks>,
396 sleeping: Mutex<BinaryHeap<Alarm>>,
397 shutdown: Mutex<Stopper>,
398 panicker: Panicker,
399 dns: Mutex<HashMap<String, Vec<IpAddr>>>,
400}
401
402impl Executor {
403 fn advance_time(&self) -> SystemTime {
408 #[cfg(feature = "external")]
409 std::thread::sleep(self.cycle);
410
411 let mut time = self.time.lock();
412 *time = time
413 .checked_add(self.cycle)
414 .expect("executor time overflowed");
415 let now = *time;
416 trace!(now = now.epoch_millis(), "time advanced");
417 now
418 }
419
420 fn skip_idle_time(&self, current: SystemTime) -> SystemTime {
425 if cfg!(feature = "external") || self.tasks.ready() != 0 {
426 return current;
427 }
428
429 let mut skip_until = None;
430 {
431 let sleeping = self.sleeping.lock();
432 if let Some(next) = sleeping.peek() {
433 if next.time > current {
434 skip_until = Some(next.time);
435 }
436 }
437 }
438
439 skip_until.map_or(current, |deadline| {
440 let mut time = self.time.lock();
441 *time = deadline;
442 let now = *time;
443 trace!(now = now.epoch_millis(), "time skipped");
444 now
445 })
446 }
447
448 fn wake_ready_sleepers(&self, current: SystemTime) {
450 let mut sleeping = self.sleeping.lock();
451 while let Some(next) = sleeping.peek() {
452 if next.time <= current {
453 let sleeper = sleeping.pop().unwrap();
454 sleeper.waker.wake();
455 } else {
456 break;
457 }
458 }
459 }
460
461 fn assert_liveness(&self) {
465 if cfg!(feature = "external") || self.tasks.ready() != 0 {
466 return;
467 }
468
469 panic!("runtime stalled");
470 }
471}
472
473pub struct Checkpoint {
477 cycle: Duration,
478 deadline: Option<SystemTime>,
479 auditor: Arc<Auditor>,
480 rng: Arc<Mutex<BoxDynRng>>,
481 time: Mutex<SystemTime>,
482 storage: Arc<Storage>,
483 dns: Mutex<HashMap<String, Vec<IpAddr>>>,
484 catch_panics: bool,
485 network_buffer_pool_cfg: BufferPoolConfig,
486 storage_buffer_pool_cfg: BufferPoolConfig,
487}
488
489impl Checkpoint {
490 pub fn auditor(&self) -> Arc<Auditor> {
492 self.auditor.clone()
493 }
494}
495
496#[allow(clippy::large_enum_variant)]
497enum State {
498 Config(Config),
499 Checkpoint(Checkpoint),
500}
501
502pub struct Runner {
504 state: State,
505}
506
507impl From<Config> for Runner {
508 fn from(cfg: Config) -> Self {
509 Self::new(cfg)
510 }
511}
512
513impl From<Checkpoint> for Runner {
514 fn from(checkpoint: Checkpoint) -> Self {
515 Self {
516 state: State::Checkpoint(checkpoint),
517 }
518 }
519}
520
521impl Runner {
522 pub fn new(cfg: Config) -> Self {
524 cfg.assert();
526 Self {
527 state: State::Config(cfg),
528 }
529 }
530
531 pub fn seeded(seed: u64) -> Self {
534 Self::new(Config::default().with_seed(seed))
535 }
536
537 pub fn timed(timeout: Duration) -> Self {
540 let cfg = Config {
541 timeout: Some(timeout),
542 ..Config::default()
543 };
544 Self::new(cfg)
545 }
546
547 pub fn start_and_recover<F, Fut>(self, f: F) -> (Fut::Output, Checkpoint)
550 where
551 F: FnOnce(Context) -> Fut,
552 Fut: Future,
553 {
554 let (context, executor, panicked) = match self.state {
556 State::Config(config) => Context::new(config),
557 State::Checkpoint(checkpoint) => Context::recover(checkpoint),
558 };
559
560 let storage = context.storage.clone();
562 let network_buffer_pool_cfg = context.network_buffer_pool.config().clone();
563 let storage_buffer_pool_cfg = context.storage_buffer_pool.config().clone();
564 let mut root = Box::pin(panicked.interrupt(f(context)));
565
566 Tasks::register_root(&executor.tasks);
568
569 let result = catch_unwind(AssertUnwindSafe(|| loop {
572 {
574 let current = executor.time.lock();
575 if let Some(deadline) = executor.deadline {
576 if *current >= deadline {
577 drop(current);
578 panic!("runtime timeout");
579 }
580 }
581 }
582
583 let mut queue = executor.tasks.drain();
585
586 if queue.len() > 1 {
588 let mut rng = executor.rng.lock();
589 queue.shuffle(&mut *rng);
590 }
591
592 trace!(
598 iter = executor.metrics.iterations.get(),
599 tasks = queue.len(),
600 "starting loop"
601 );
602 let mut output = None;
603 for id in queue {
604 let Some(task) = executor.tasks.get(id) else {
606 trace!(id, "skipping missing task");
607 continue;
608 };
609
610 executor.auditor.event(b"process_task", |hasher| {
612 hasher.update(task.id.to_be_bytes());
613 hasher.update(task.label.name().as_bytes());
614 });
615 executor.metrics.task_polls.get_or_create(&task.label).inc();
616 trace!(id, "processing task");
617
618 let waker = waker(Arc::new(TaskWaker {
620 id,
621 tasks: Arc::downgrade(&executor.tasks),
622 }));
623 let mut cx = task::Context::from_waker(&waker);
624
625 match &task.mode {
627 Mode::Root => {
628 if let Poll::Ready(result) = root.as_mut().poll(&mut cx) {
630 trace!(id, "root task is complete");
631 output = Some(result);
632 break;
633 }
634 }
635 Mode::Work(future) => {
636 let mut fut_opt = future.lock();
638 let Some(fut) = fut_opt.as_mut() else {
639 trace!(id, "skipping already complete task");
640
641 executor.tasks.remove(id);
643 continue;
644 };
645
646 if fut.as_mut().poll(&mut cx).is_ready() {
648 trace!(id, "task is complete");
649
650 executor.tasks.remove(id);
652 *fut_opt = None;
653 continue;
654 }
655 }
656 }
657
658 trace!(id, "task is still pending");
660 }
661
662 if let Some(output) = output {
664 break output;
665 }
666
667 let mut current = executor.advance_time();
669 current = executor.skip_idle_time(current);
670
671 executor.wake_ready_sleepers(current);
673 executor.assert_liveness();
674
675 executor.metrics.iterations.inc();
677 }));
678
679 executor.sleeping.lock().clear(); let tasks = executor.tasks.clear();
687 for task in tasks {
688 let Mode::Work(future) = &task.mode else {
689 continue;
690 };
691 *future.lock() = None;
692 }
693
694 drop(root);
698
699 assert!(
702 Arc::weak_count(&executor) == 0,
703 "executor still has weak references"
704 );
705
706 let output = match result {
708 Ok(output) => output,
709 Err(payload) => resume_unwind(payload),
710 };
711
712 let executor = Arc::into_inner(executor).expect("executor still has strong references");
714
715 let checkpoint = Checkpoint {
717 cycle: executor.cycle,
718 deadline: executor.deadline,
719 auditor: executor.auditor,
720 rng: executor.rng,
721 time: executor.time,
722 storage,
723 dns: executor.dns,
724 catch_panics: executor.panicker.catch(),
725 network_buffer_pool_cfg,
726 storage_buffer_pool_cfg,
727 };
728
729 (output, checkpoint)
730 }
731}
732
733impl Default for Runner {
734 fn default() -> Self {
735 Self::new(Config::default())
736 }
737}
738
739impl crate::Runner for Runner {
740 type Context = Context;
741
742 fn start<F, Fut>(self, f: F) -> Fut::Output
743 where
744 F: FnOnce(Self::Context) -> Fut,
745 Fut: Future,
746 {
747 let (output, _) = self.start_and_recover(f);
748 output
749 }
750}
751
752enum Mode {
754 Root,
755 Work(Mutex<Option<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>>),
756}
757
758struct Task {
760 id: u128,
761 label: Label,
762
763 mode: Mode,
764}
765
766struct TaskWaker {
768 id: u128,
769
770 tasks: Weak<Tasks>,
771}
772
773impl ArcWake for TaskWaker {
774 fn wake_by_ref(arc_self: &Arc<Self>) {
775 if let Some(tasks) = arc_self.tasks.upgrade() {
780 tasks.queue(arc_self.id);
781 }
782 }
783}
784
785struct Tasks {
787 counter: Mutex<u128>,
789 ready: Mutex<Vec<u128>>,
791 running: Mutex<BTreeMap<u128, Arc<Task>>>,
793}
794
795impl Tasks {
796 const fn new() -> Self {
798 Self {
799 counter: Mutex::new(0),
800 ready: Mutex::new(Vec::new()),
801 running: Mutex::new(BTreeMap::new()),
802 }
803 }
804
805 fn increment(&self) -> u128 {
807 let mut counter = self.counter.lock();
808 let old = *counter;
809 *counter = counter.checked_add(1).expect("task counter overflow");
810 old
811 }
812
813 fn register_root(arc_self: &Arc<Self>) {
817 let id = arc_self.increment();
818 let task = Arc::new(Task {
819 id,
820 label: Label::root(),
821 mode: Mode::Root,
822 });
823 arc_self.register(id, task);
824 }
825
826 fn register_work(
828 arc_self: &Arc<Self>,
829 label: Label,
830 future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
831 ) {
832 let id = arc_self.increment();
833 let task = Arc::new(Task {
834 id,
835 label,
836 mode: Mode::Work(Mutex::new(Some(future))),
837 });
838 arc_self.register(id, task);
839 }
840
841 fn register(&self, id: u128, task: Arc<Task>) {
843 self.running.lock().insert(id, task);
845
846 self.queue(id);
848 }
849
850 fn queue(&self, id: u128) {
852 let mut ready = self.ready.lock();
853 ready.push(id);
854 }
855
856 fn drain(&self) -> Vec<u128> {
858 let mut queue = self.ready.lock();
859 let len = queue.len();
860 replace(&mut *queue, Vec::with_capacity(len))
861 }
862
863 fn ready(&self) -> usize {
865 self.ready.lock().len()
866 }
867
868 fn get(&self, id: u128) -> Option<Arc<Task>> {
873 let running = self.running.lock();
874 running.get(&id).cloned()
875 }
876
877 fn remove(&self, id: u128) {
879 self.running.lock().remove(&id);
880 }
881
882 fn clear(&self) -> Vec<Arc<Task>> {
884 self.ready.lock().clear();
886
887 let running: BTreeMap<u128, Arc<Task>> = {
889 let mut running = self.running.lock();
890 take(&mut *running)
891 };
892 running.into_values().collect()
893 }
894}
895
896type Network = MeteredNetwork<AuditedNetwork<DeterministicNetwork>>;
897type Storage = MeteredStorage<AuditedStorage<FaultyStorage<MemStorage>>>;
898
899pub struct Context {
903 name: String,
904 attributes: Vec<(String, String)>,
905 executor: Weak<Executor>,
906 network: Arc<Network>,
907 storage: Arc<Storage>,
908 network_buffer_pool: BufferPool,
909 storage_buffer_pool: BufferPool,
910 tree: Arc<Tree>,
911 execution: Execution,
912}
913
914impl Context {
915 fn new(cfg: Config) -> (Self, Arc<Executor>, Panicked) {
916 let mut registry = Registry::new();
918 let mut runtime_registry = registry.sub_registry(METRICS_PREFIX);
919
920 let metrics = Arc::new(Metrics::init(&mut runtime_registry));
922 let start_time = cfg.start_time;
923 let deadline = cfg
924 .timeout
925 .map(|timeout| start_time.checked_add(timeout).expect("timeout overflowed"));
926 let auditor = Arc::new(Auditor::default());
927
928 let rng = Arc::new(Mutex::new(cfg.rng));
930
931 let network_buffer_pool = BufferPool::new(
933 cfg.network_buffer_pool_cfg.clone(),
934 &mut runtime_registry.sub_registry("network_buffer_pool"),
935 );
936 let storage_buffer_pool = BufferPool::new(
937 cfg.storage_buffer_pool_cfg.clone(),
938 &mut runtime_registry.sub_registry("storage_buffer_pool"),
939 );
940
941 let storage_fault_config = Arc::new(RwLock::new(cfg.storage_fault_cfg));
943 let storage = MeteredStorage::new(
944 AuditedStorage::new(
945 FaultyStorage::new(
946 MemStorage::new(storage_buffer_pool.clone()),
947 rng.clone(),
948 storage_fault_config,
949 ),
950 auditor.clone(),
951 ),
952 &mut runtime_registry,
953 );
954
955 let network = AuditedNetwork::new(DeterministicNetwork::default(), auditor.clone());
957 let network = MeteredNetwork::new(network, &mut runtime_registry);
958
959 let (panicker, panicked) = Panicker::new(cfg.catch_panics);
961
962 let executor = Arc::new(Executor {
963 registry,
964 cycle: cfg.cycle,
965 deadline,
966 metrics,
967 auditor,
968 rng,
969 time: Mutex::new(start_time),
970 tasks: Arc::new(Tasks::new()),
971 sleeping: Mutex::new(BinaryHeap::new()),
972 shutdown: Mutex::new(Stopper::default()),
973 panicker,
974 dns: Mutex::new(HashMap::new()),
975 });
976
977 (
978 Self {
979 name: String::new(),
980 attributes: Vec::new(),
981 executor: Arc::downgrade(&executor),
982 network: Arc::new(network),
983 storage: Arc::new(storage),
984 network_buffer_pool,
985 storage_buffer_pool,
986 tree: Tree::root(),
987 execution: Execution::default(),
988 },
989 executor,
990 panicked,
991 )
992 }
993
994 fn recover(checkpoint: Checkpoint) -> (Self, Arc<Executor>, Panicked) {
1006 let mut registry = Registry::new();
1008 let mut runtime_registry = registry.sub_registry(METRICS_PREFIX);
1009 let metrics = Arc::new(Metrics::init(&mut runtime_registry));
1010
1011 let network =
1013 AuditedNetwork::new(DeterministicNetwork::default(), checkpoint.auditor.clone());
1014 let network = MeteredNetwork::new(network, &mut runtime_registry);
1015
1016 let network_buffer_pool = BufferPool::new(
1018 checkpoint.network_buffer_pool_cfg.clone(),
1019 &mut runtime_registry.sub_registry("network_buffer_pool"),
1020 );
1021 let storage_buffer_pool = BufferPool::new(
1022 checkpoint.storage_buffer_pool_cfg.clone(),
1023 &mut runtime_registry.sub_registry("storage_buffer_pool"),
1024 );
1025
1026 let (panicker, panicked) = Panicker::new(checkpoint.catch_panics);
1028
1029 let executor = Arc::new(Executor {
1030 cycle: checkpoint.cycle,
1032 deadline: checkpoint.deadline,
1033 auditor: checkpoint.auditor,
1034 rng: checkpoint.rng,
1035 time: checkpoint.time,
1036 dns: checkpoint.dns,
1037
1038 registry,
1040 metrics,
1041 tasks: Arc::new(Tasks::new()),
1042 sleeping: Mutex::new(BinaryHeap::new()),
1043 shutdown: Mutex::new(Stopper::default()),
1044 panicker,
1045 });
1046 (
1047 Self {
1048 name: String::new(),
1049 attributes: Vec::new(),
1050 executor: Arc::downgrade(&executor),
1051 network: Arc::new(network),
1052 storage: checkpoint.storage,
1053 network_buffer_pool,
1054 storage_buffer_pool,
1055 tree: Tree::root(),
1056 execution: Execution::default(),
1057 },
1058 executor,
1059 panicked,
1060 )
1061 }
1062
1063 fn executor(&self) -> Arc<Executor> {
1065 self.executor.upgrade().expect("executor already dropped")
1066 }
1067
1068 fn metrics(&self) -> Arc<Metrics> {
1070 self.executor().metrics.clone()
1071 }
1072
1073 pub fn auditor(&self) -> Arc<Auditor> {
1075 self.executor().auditor.clone()
1076 }
1077
1078 pub fn storage_audit(&self) -> Digest {
1080 self.storage.inner().inner().inner().audit()
1081 }
1082
1083 pub fn storage_fault_config(&self) -> Arc<RwLock<FaultConfig>> {
1089 self.storage.inner().inner().config()
1090 }
1091
1092 pub fn resolver_register(&self, host: impl Into<String>, addrs: Option<Vec<IpAddr>>) {
1097 let executor = self.executor();
1099 let host = host.into();
1100 executor.auditor.event(b"resolver_register", |hasher| {
1101 hasher.update(host.as_bytes());
1102 hasher.update(addrs.encode());
1103 });
1104
1105 let mut dns = executor.dns.lock();
1107 match addrs {
1108 Some(addrs) => {
1109 dns.insert(host, addrs);
1110 }
1111 None => {
1112 dns.remove(&host);
1113 }
1114 }
1115 }
1116}
1117
1118impl crate::Spawner for Context {
1119 fn dedicated(mut self) -> Self {
1120 self.execution = Execution::Dedicated;
1121 self
1122 }
1123
1124 fn shared(mut self, blocking: bool) -> Self {
1125 self.execution = Execution::Shared(blocking);
1126 self
1127 }
1128
1129 fn spawn<F, Fut, T>(mut self, f: F) -> Handle<T>
1130 where
1131 F: FnOnce(Self) -> Fut + Send + 'static,
1132 Fut: Future<Output = T> + Send + 'static,
1133 T: Send + 'static,
1134 {
1135 let (label, metric) = spawn_metrics!(self);
1137
1138 let parent = Arc::clone(&self.tree);
1140 self.execution = Execution::default();
1141 let (child, aborted) = Tree::child(&parent);
1142 if aborted {
1143 return Handle::closed(metric);
1144 }
1145 self.tree = child;
1146
1147 let executor = self.executor();
1149 let future = f(self);
1150 let (f, handle) = Handle::init(
1151 future,
1152 metric,
1153 executor.panicker.clone(),
1154 Arc::clone(&parent),
1155 );
1156 Tasks::register_work(&executor.tasks, label, Box::pin(f));
1157
1158 if let Some(aborter) = handle.aborter() {
1160 parent.register(aborter);
1161 }
1162
1163 handle
1164 }
1165
1166 async fn stop(self, value: i32, timeout: Option<Duration>) -> Result<(), Error> {
1167 let executor = self.executor();
1168 executor.auditor.event(b"stop", |hasher| {
1169 hasher.update(value.to_be_bytes());
1170 });
1171 let stop_resolved = {
1172 let mut shutdown = executor.shutdown.lock();
1173 shutdown.stop(value)
1174 };
1175
1176 let timeout_future = timeout.map_or_else(
1178 || futures::future::Either::Right(futures::future::pending()),
1179 |duration| futures::future::Either::Left(self.sleep(duration)),
1180 );
1181 select! {
1182 result = stop_resolved => {
1183 result.map_err(|_| Error::Closed)?;
1184 Ok(())
1185 },
1186 _ = timeout_future => Err(Error::Timeout),
1187 }
1188 }
1189
1190 fn stopped(&self) -> Signal {
1191 let executor = self.executor();
1192 executor.auditor.event(b"stopped", |_| {});
1193 let stopped = executor.shutdown.lock().stopped();
1194 stopped
1195 }
1196}
1197
1198commonware_utils::thread_local_cache!(static THREAD_POOL: ThreadPool);
1201
1202fn shared_thread_pool() -> Result<ThreadPool, ThreadPoolBuildError> {
1207 let pool = Cached::take(
1208 &THREAD_POOL,
1209 || {
1210 ThreadPoolBuilder::new()
1211 .num_threads(1)
1212 .use_current_thread()
1213 .build()
1214 .map(Arc::new)
1215 },
1216 |_| Ok(()),
1217 )?;
1218 Ok(Arc::clone(&pool))
1219}
1220
1221impl crate::Strategizer for Context {
1231 fn strategy(&self, parallelism: NonZeroUsize) -> Rayon {
1232 Rayon::with_pool(
1233 shared_thread_pool().expect("failed to create deterministic Rayon thread pool"),
1234 )
1235 .with_parallelism(parallelism)
1236 }
1237}
1238
1239impl crate::Supervisor for Context {
1240 fn child(&self, label: &'static str) -> Self {
1241 let (tree, _) = Tree::child(&self.tree);
1242 Self {
1243 name: child_label(&self.name, label),
1244 attributes: self.attributes.clone(),
1245 executor: self.executor.clone(),
1246 network: self.network.clone(),
1247 storage: self.storage.clone(),
1248 network_buffer_pool: self.network_buffer_pool.clone(),
1249 storage_buffer_pool: self.storage_buffer_pool.clone(),
1250 tree,
1251 execution: Execution::default(),
1252 }
1253 }
1254
1255 fn with_attribute(mut self, key: &'static str, value: impl std::fmt::Display) -> Self {
1256 validate_label(key);
1258
1259 add_attribute(&mut self.attributes, key, value);
1261 self
1262 }
1263
1264 fn name(&self) -> Name {
1265 Name {
1266 label: self.name.clone(),
1267 attributes: self.attributes.clone(),
1268 }
1269 }
1270}
1271
1272impl crate::Metrics for Context {
1273 fn register<N: Into<String>, H: Into<String>, M: Metric>(
1274 &self,
1275 name: N,
1276 help: H,
1277 metric: M,
1278 ) -> Registered<M> {
1279 let name = name.into();
1280 let help = help.into();
1281 let executor = self.executor();
1282 executor.auditor.event(b"register", |hasher| {
1283 hasher.update(name.as_bytes());
1284 hasher.update(help.as_bytes());
1285 for (k, v) in &self.attributes {
1286 hasher.update(k.as_bytes());
1287 hasher.update(v.as_bytes());
1288 }
1289 });
1290 let metric = Arc::new(metric);
1291 executor.registry.register(
1292 prefixed_name(&self.name, &name),
1293 help,
1294 self.attributes.clone(),
1295 metric,
1296 )
1297 }
1298
1299 fn encode(&self) -> String {
1300 let executor = self.executor();
1301 executor.auditor.event(b"encode", |_| {});
1302 executor.registry.encode()
1303 }
1304}
1305
1306struct Sleeper {
1307 executor: Weak<Executor>,
1308 time: SystemTime,
1309 registered: bool,
1310}
1311
1312impl Sleeper {
1313 fn executor(&self) -> Arc<Executor> {
1315 self.executor.upgrade().expect("executor already dropped")
1316 }
1317}
1318
1319struct Alarm {
1320 time: SystemTime,
1321 waker: Waker,
1322}
1323
1324impl PartialEq for Alarm {
1325 fn eq(&self, other: &Self) -> bool {
1326 self.time.eq(&other.time)
1327 }
1328}
1329
1330impl Eq for Alarm {}
1331
1332impl PartialOrd for Alarm {
1333 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1334 Some(self.cmp(other))
1335 }
1336}
1337
1338impl Ord for Alarm {
1339 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1340 other.time.cmp(&self.time)
1342 }
1343}
1344
1345impl Future for Sleeper {
1346 type Output = ();
1347
1348 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
1349 let executor = self.executor();
1350 {
1351 let current_time = *executor.time.lock();
1352 if current_time >= self.time {
1353 return Poll::Ready(());
1354 }
1355 }
1356 if !self.registered {
1357 self.registered = true;
1358 executor.sleeping.lock().push(Alarm {
1359 time: self.time,
1360 waker: cx.waker().clone(),
1361 });
1362 }
1363 Poll::Pending
1364 }
1365}
1366
1367impl Clock for Context {
1368 fn current(&self) -> SystemTime {
1369 *self.executor().time.lock()
1370 }
1371
1372 fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send + 'static {
1373 let deadline = self
1374 .current()
1375 .checked_add(duration)
1376 .expect("overflow when setting wake time");
1377 self.sleep_until(deadline)
1378 }
1379
1380 fn sleep_until(&self, deadline: SystemTime) -> impl Future<Output = ()> + Send + 'static {
1381 Sleeper {
1382 executor: self.executor.clone(),
1383
1384 time: deadline,
1385 registered: false,
1386 }
1387 }
1388}
1389
1390#[cfg(feature = "external")]
1394#[pin_project]
1395struct Waiter<F: Future> {
1396 executor: Weak<Executor>,
1397 target: SystemTime,
1398 #[pin]
1399 future: F,
1400 ready: Option<F::Output>,
1401 started: bool,
1402 registered: bool,
1403}
1404
1405#[cfg(feature = "external")]
1406impl<F> Future for Waiter<F>
1407where
1408 F: Future + Send,
1409{
1410 type Output = F::Output;
1411
1412 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
1413 let mut this = self.project();
1414
1415 if !*this.started {
1419 *this.started = true;
1420 let waker = noop_waker();
1421 let mut cx_noop = task::Context::from_waker(&waker);
1422 if let Poll::Ready(value) = this.future.as_mut().poll(&mut cx_noop) {
1423 *this.ready = Some(value);
1424 }
1425 }
1426
1427 let executor = this.executor.upgrade().expect("executor already dropped");
1429 let current_time = *executor.time.lock();
1430 if current_time < *this.target {
1431 if !*this.registered {
1434 *this.registered = true;
1435 executor.sleeping.lock().push(Alarm {
1436 time: *this.target,
1437 waker: cx.waker().clone(),
1438 });
1439 }
1440 return Poll::Pending;
1441 }
1442
1443 if let Some(value) = this.ready.take() {
1445 return Poll::Ready(value);
1446 }
1447
1448 let blocker = Blocker::new();
1451 loop {
1452 let waker = waker(blocker.clone());
1453 let mut cx_block = task::Context::from_waker(&waker);
1454 match this.future.as_mut().poll(&mut cx_block) {
1455 Poll::Ready(value) => {
1456 break Poll::Ready(value);
1457 }
1458 Poll::Pending => blocker.wait(),
1459 }
1460 }
1461 }
1462}
1463
1464#[cfg(feature = "external")]
1465impl Pacer for Context {
1466 fn pace<'a, F, T>(&'a self, latency: Duration, future: F) -> impl Future<Output = T> + Send + 'a
1467 where
1468 F: Future<Output = T> + Send + 'a,
1469 T: Send + 'a,
1470 {
1471 let target = self
1473 .executor()
1474 .time
1475 .lock()
1476 .checked_add(latency)
1477 .expect("overflow when setting wake time");
1478
1479 Waiter {
1480 executor: self.executor.clone(),
1481 target,
1482 future,
1483 ready: None,
1484 started: false,
1485 registered: false,
1486 }
1487 }
1488}
1489
1490impl GClock for Context {
1491 type Instant = SystemTime;
1492
1493 fn now(&self) -> Self::Instant {
1494 self.current()
1495 }
1496}
1497
1498impl ReasonablyRealtime for Context {}
1499
1500impl crate::Network for Context {
1501 type Listener = ListenerOf<Network>;
1502
1503 async fn bind(&self, socket: SocketAddr) -> Result<Self::Listener, Error> {
1504 self.network.bind(socket).await
1505 }
1506
1507 async fn dial(
1508 &self,
1509 socket: SocketAddr,
1510 ) -> Result<(crate::SinkOf<Self>, crate::StreamOf<Self>), Error> {
1511 self.network.dial(socket).await
1512 }
1513}
1514
1515impl crate::Resolver for Context {
1516 async fn resolve(&self, host: &str) -> Result<Vec<IpAddr>, Error> {
1517 let executor = self.executor();
1519 let dns = executor.dns.lock();
1520 let result = dns.get(host).cloned();
1521 drop(dns);
1522
1523 executor.auditor.event(b"resolve", |hasher| {
1525 hasher.update(host.as_bytes());
1526 hasher.update(result.encode());
1527 });
1528 result.ok_or_else(|| Error::ResolveFailed(host.to_string()))
1529 }
1530}
1531
1532impl TryRng for Context {
1533 type Error = Infallible;
1534
1535 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
1536 let executor = self.executor();
1537 executor.auditor.event(b"rand", |hasher| {
1538 hasher.update(b"next_u32");
1539 });
1540 let result = executor.rng.lock().next_u32();
1541 Ok(result)
1542 }
1543
1544 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
1545 let executor = self.executor();
1546 executor.auditor.event(b"rand", |hasher| {
1547 hasher.update(b"next_u64");
1548 });
1549 let result = executor.rng.lock().next_u64();
1550 Ok(result)
1551 }
1552
1553 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
1554 let executor = self.executor();
1555 executor.auditor.event(b"rand", |hasher| {
1556 hasher.update(b"fill_bytes");
1557 });
1558 executor.rng.lock().fill_bytes(dest);
1559 Ok(())
1560 }
1561}
1562
1563impl TryCryptoRng for Context {}
1564
1565impl crate::Storage for Context {
1566 type Blob = <Storage as crate::Storage>::Blob;
1567
1568 async fn open_versioned(
1569 &self,
1570 partition: &str,
1571 name: &[u8],
1572 versions: std::ops::RangeInclusive<u16>,
1573 ) -> Result<(Self::Blob, u64, u16), Error> {
1574 self.storage.open_versioned(partition, name, versions).await
1575 }
1576
1577 async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
1578 self.storage.remove(partition, name).await
1579 }
1580
1581 async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
1582 self.storage.scan(partition).await
1583 }
1584}
1585
1586impl crate::BufferPooler for Context {
1587 fn network_buffer_pool(&self) -> &crate::BufferPool {
1588 &self.network_buffer_pool
1589 }
1590
1591 fn storage_buffer_pool(&self) -> &crate::BufferPool {
1592 &self.storage_buffer_pool
1593 }
1594}
1595
1596#[cfg(test)]
1597mod tests {
1598 use super::*;
1599 #[cfg(feature = "external")]
1600 use crate::FutureExt;
1601 use crate::{
1602 deterministic, reschedule, Blob, Metrics as _, Resolver, Runner as _, Spawner as _,
1603 Storage, Supervisor as _,
1604 };
1605 use commonware_macros::test_traced;
1606 #[cfg(feature = "external")]
1607 use commonware_utils::channel::mpsc;
1608 use commonware_utils::channel::oneshot;
1609 #[cfg(not(feature = "external"))]
1610 use futures::future::pending;
1611 #[cfg(not(feature = "external"))]
1612 use futures::stream::StreamExt as _;
1613 #[cfg(feature = "external")]
1614 use futures::StreamExt;
1615 use futures::{stream::FuturesUnordered, task::noop_waker};
1616
1617 async fn task(i: usize) -> usize {
1618 for _ in 0..5 {
1619 reschedule().await;
1620 }
1621 i
1622 }
1623
1624 fn run_tasks(tasks: usize, runner: deterministic::Runner) -> (String, Vec<usize>) {
1625 runner.start(|context| async move {
1626 let mut handles = FuturesUnordered::new();
1627 for i in 0..=tasks - 1 {
1628 handles.push(context.child("task").spawn(move |_| task(i)));
1629 }
1630
1631 let mut outputs = Vec::new();
1632 while let Some(result) = handles.next().await {
1633 outputs.push(result.unwrap());
1634 }
1635 assert_eq!(outputs.len(), tasks);
1636 (context.auditor().state(), outputs)
1637 })
1638 }
1639
1640 fn run_with_seed(seed: u64) -> (String, Vec<usize>) {
1641 let executor = deterministic::Runner::seeded(seed);
1642 run_tasks(5, executor)
1643 }
1644
1645 fn run_with_metric(name: &'static str, help: &'static str) -> String {
1646 deterministic::Runner::default().start(|context| async move {
1647 let _: Registered<raw::Counter> = context.register(name, help, raw::Counter::default());
1648 context.auditor().state()
1649 })
1650 }
1651
1652 #[test]
1653 fn test_auditor_separates_metric_fields() {
1654 let state_a = run_with_metric("a", "bc");
1655 let state_b = run_with_metric("ab", "c");
1656
1657 assert_ne!(state_a, state_b);
1658 }
1659
1660 #[test]
1661 fn test_same_seed_same_order() {
1662 let mut outputs = Vec::new();
1664 for seed in 0..1000 {
1665 let output = run_with_seed(seed);
1666 outputs.push(output);
1667 }
1668
1669 for seed in 0..1000 {
1671 let output = run_with_seed(seed);
1672 assert_eq!(output, outputs[seed as usize]);
1673 }
1674 }
1675
1676 #[test_traced("TRACE")]
1677 fn test_different_seeds_different_order() {
1678 let output1 = run_with_seed(12345);
1679 let output2 = run_with_seed(54321);
1680 assert_ne!(output1, output2);
1681 }
1682
1683 #[test]
1684 fn test_alarm_min_heap() {
1685 let now = SystemTime::now();
1687 let alarms = vec![
1688 Alarm {
1689 time: now + Duration::new(10, 0),
1690 waker: noop_waker(),
1691 },
1692 Alarm {
1693 time: now + Duration::new(5, 0),
1694 waker: noop_waker(),
1695 },
1696 Alarm {
1697 time: now + Duration::new(15, 0),
1698 waker: noop_waker(),
1699 },
1700 Alarm {
1701 time: now + Duration::new(5, 0),
1702 waker: noop_waker(),
1703 },
1704 ];
1705 let mut heap = BinaryHeap::new();
1706 for alarm in alarms {
1707 heap.push(alarm);
1708 }
1709
1710 let mut sorted_times = Vec::new();
1712 while let Some(alarm) = heap.pop() {
1713 sorted_times.push(alarm.time);
1714 }
1715 assert_eq!(
1716 sorted_times,
1717 vec![
1718 now + Duration::new(5, 0),
1719 now + Duration::new(5, 0),
1720 now + Duration::new(10, 0),
1721 now + Duration::new(15, 0),
1722 ]
1723 );
1724 }
1725
1726 #[test]
1727 #[should_panic(expected = "runtime timeout")]
1728 fn test_timeout() {
1729 let executor = deterministic::Runner::timed(Duration::from_secs(10));
1730 executor.start(|context| async move {
1731 loop {
1732 context.sleep(Duration::from_secs(1)).await;
1733 }
1734 });
1735 }
1736
1737 #[test]
1738 #[should_panic(expected = "cycle duration must be non-zero when timeout is set")]
1739 fn test_bad_timeout() {
1740 let cfg = Config {
1741 timeout: Some(Duration::default()),
1742 cycle: Duration::default(),
1743 ..Config::default()
1744 };
1745 deterministic::Runner::new(cfg);
1746 }
1747
1748 #[test]
1749 #[should_panic(
1750 expected = "cycle duration must be greater than or equal to system time precision"
1751 )]
1752 fn test_bad_cycle() {
1753 let cfg = Config {
1754 cycle: SYSTEM_TIME_PRECISION - Duration::from_nanos(1),
1755 ..Config::default()
1756 };
1757 deterministic::Runner::new(cfg);
1758 }
1759
1760 #[test]
1761 fn test_recover_synced_storage_persists() {
1762 let executor1 = deterministic::Runner::default();
1764 let partition = "test_partition";
1765 let name = b"test_blob";
1766 let data = b"Hello, world!";
1767
1768 let (state, checkpoint) = executor1.start_and_recover(|context| async move {
1770 let (blob, _) = context.open(partition, name).await.unwrap();
1771 blob.write_at(0, data).await.unwrap();
1772 blob.sync().await.unwrap();
1773 context.auditor().state()
1774 });
1775
1776 assert_eq!(state, checkpoint.auditor.state());
1778
1779 let executor = Runner::from(checkpoint);
1781 executor.start(|context| async move {
1782 let (blob, len) = context.open(partition, name).await.unwrap();
1783 assert_eq!(len, data.len() as u64);
1784 let read = blob.read_at(0, data.len()).await.unwrap();
1785 assert_eq!(read.coalesce(), data);
1786 });
1787 }
1788
1789 #[test]
1790 #[should_panic(expected = "goodbye")]
1791 fn test_recover_panic_handling() {
1792 let executor1 = deterministic::Runner::default();
1794 let (_, checkpoint) = executor1.start_and_recover(|_| async move {
1795 reschedule().await;
1796 });
1797
1798 let executor = Runner::from(checkpoint);
1800 executor.start(|_| async move {
1801 panic!("goodbye");
1802 });
1803 }
1804
1805 #[test]
1806 fn test_recover_unsynced_storage_does_not_persist() {
1807 let executor = deterministic::Runner::default();
1809 let partition = "test_partition";
1810 let name = b"test_blob";
1811 let data = b"Hello, world!";
1812
1813 let (_, checkpoint) = executor.start_and_recover(|context| async move {
1815 let (blob, _) = context.open(partition, name).await.unwrap();
1816 blob.write_at(0, data).await.unwrap();
1817 });
1818
1819 let executor = Runner::from(checkpoint);
1821
1822 executor.start(|context| async move {
1824 let (_, len) = context.open(partition, name).await.unwrap();
1825 assert_eq!(len, 0);
1826 });
1827 }
1828
1829 #[test]
1830 fn test_recover_dns_mappings_persist() {
1831 let executor = deterministic::Runner::default();
1833 let host = "example.com";
1834 let addrs = vec![
1835 IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 1)),
1836 IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 2)),
1837 ];
1838
1839 let (state, checkpoint) = executor.start_and_recover({
1841 let addrs = addrs.clone();
1842 |context| async move {
1843 context.resolver_register(host, Some(addrs));
1844 context.auditor().state()
1845 }
1846 });
1847
1848 assert_eq!(state, checkpoint.auditor.state());
1850
1851 let executor = Runner::from(checkpoint);
1853 executor.start(move |context| async move {
1854 let resolved = context.resolve(host).await.unwrap();
1855 assert_eq!(resolved, addrs);
1856 });
1857 }
1858
1859 #[test]
1860 fn test_recover_time_persists() {
1861 let executor = deterministic::Runner::default();
1863 let duration_to_sleep = Duration::from_secs(10);
1864
1865 let (time_before_recovery, checkpoint) = executor.start_and_recover(|context| async move {
1867 context.sleep(duration_to_sleep).await;
1868 context.current()
1869 });
1870
1871 assert_eq!(
1873 time_before_recovery.duration_since(UNIX_EPOCH).unwrap(),
1874 duration_to_sleep
1875 );
1876
1877 let executor2 = Runner::from(checkpoint);
1879 executor2.start(move |context| async move {
1880 assert_eq!(context.current(), time_before_recovery);
1881
1882 context.sleep(duration_to_sleep).await;
1884 assert_eq!(
1885 context.current().duration_since(UNIX_EPOCH).unwrap(),
1886 duration_to_sleep * 2
1887 );
1888 });
1889 }
1890
1891 #[test]
1892 #[should_panic(expected = "executor still has weak references")]
1893 fn test_context_return() {
1894 let executor = deterministic::Runner::default();
1896
1897 let context = executor.start(|context| async move {
1899 context
1901 });
1902
1903 drop(context);
1905 }
1906
1907 #[test]
1908 fn test_default_time_zero() {
1909 let executor = deterministic::Runner::default();
1911
1912 executor.start(|context| async move {
1913 assert_eq!(
1915 context.current().duration_since(UNIX_EPOCH).unwrap(),
1916 Duration::ZERO
1917 );
1918 });
1919 }
1920
1921 #[test]
1922 fn test_start_time() {
1923 let executor_default = deterministic::Runner::default();
1925 executor_default.start(|context| async move {
1926 assert_eq!(context.current(), UNIX_EPOCH);
1927 });
1928
1929 let start_time = UNIX_EPOCH + Duration::from_secs(100);
1931 let cfg = Config::default().with_start_time(start_time);
1932 let executor = deterministic::Runner::new(cfg);
1933
1934 executor.start(move |context| async move {
1935 assert_eq!(context.current(), start_time);
1937 });
1938 }
1939
1940 #[test]
1941 #[should_panic(expected = "start time must be greater than or equal to unix epoch")]
1942 fn test_bad_start_time() {
1943 let cfg = Config::default().with_start_time(UNIX_EPOCH - Duration::from_secs(1));
1944 deterministic::Runner::new(cfg);
1945 }
1946
1947 #[cfg(not(feature = "external"))]
1948 #[test]
1949 #[should_panic(expected = "runtime stalled")]
1950 fn test_stall() {
1951 let executor = deterministic::Runner::default();
1953
1954 executor.start(|_| async move {
1956 pending::<()>().await;
1957 });
1958 }
1959
1960 #[cfg(not(feature = "external"))]
1961 #[test]
1962 #[should_panic(expected = "runtime stalled")]
1963 fn test_external_simulated() {
1964 let executor = deterministic::Runner::default();
1966
1967 let (tx, rx) = oneshot::channel();
1969 std::thread::spawn(move || {
1970 std::thread::sleep(Duration::from_secs(1));
1971 tx.send(()).unwrap();
1972 });
1973
1974 executor.start(|_| async move {
1976 rx.await.unwrap();
1977 });
1978 }
1979
1980 #[cfg(feature = "external")]
1981 #[test]
1982 fn test_external_realtime() {
1983 let executor = deterministic::Runner::default();
1985
1986 let (tx, rx) = oneshot::channel();
1988 std::thread::spawn(move || {
1989 std::thread::sleep(Duration::from_secs(1));
1990 tx.send(()).unwrap();
1991 });
1992
1993 executor.start(|_| async move {
1995 rx.await.unwrap();
1996 });
1997 }
1998
1999 #[cfg(feature = "external")]
2000 #[test]
2001 fn test_external_realtime_variable() {
2002 let executor = deterministic::Runner::default();
2004
2005 executor.start(|context| async move {
2007 let start_real = SystemTime::now();
2009 let start_sim = context.current();
2010 let (first_tx, first_rx) = oneshot::channel();
2011 let (second_tx, second_rx) = oneshot::channel();
2012 let (results_tx, mut results_rx) = mpsc::channel(2);
2013
2014 let first_wait = Duration::from_secs(1);
2016 std::thread::spawn(move || {
2017 std::thread::sleep(first_wait);
2018 first_tx.send(()).unwrap();
2019 });
2020
2021 std::thread::spawn(move || {
2023 std::thread::sleep(Duration::ZERO);
2024 second_tx.send(()).unwrap();
2025 });
2026
2027 let first = context.child("sample_before_send").spawn({
2029 let results_tx = results_tx.clone();
2030 move |context| async move {
2031 first_rx.pace(&context, Duration::ZERO).await.unwrap();
2032 let elapsed_real = SystemTime::now().duration_since(start_real).unwrap();
2033 assert!(elapsed_real > first_wait);
2034 let elapsed_sim = context.current().duration_since(start_sim).unwrap();
2035 assert!(elapsed_sim < first_wait);
2036 results_tx.send(1).await.unwrap();
2037 }
2038 });
2039
2040 let second = context
2042 .child("sample_after_send")
2043 .spawn(move |context| async move {
2044 second_rx.pace(&context, first_wait).await.unwrap();
2045 let elapsed_real = SystemTime::now().duration_since(start_real).unwrap();
2046 assert!(elapsed_real >= first_wait);
2047 let elapsed_sim = context.current().duration_since(start_sim).unwrap();
2048 assert!(elapsed_sim >= first_wait);
2049 results_tx.send(2).await.unwrap();
2050 });
2051
2052 second.await.unwrap();
2054 first.await.unwrap();
2055
2056 let mut results = Vec::new();
2058 for _ in 0..2 {
2059 results.push(results_rx.recv().await.unwrap());
2060 }
2061 assert_eq!(results, vec![1, 2]);
2062 });
2063 }
2064
2065 #[cfg(not(feature = "external"))]
2066 #[test]
2067 fn test_simulated_skip() {
2068 let executor = deterministic::Runner::default();
2070
2071 executor.start(|context| async move {
2073 context.sleep(Duration::from_secs(1)).await;
2074
2075 let metrics = context.encode();
2077 let iterations = metrics
2078 .lines()
2079 .find_map(|line| {
2080 line.strip_prefix("runtime_iterations_total ")
2081 .and_then(|value| value.trim().parse::<u64>().ok())
2082 })
2083 .expect("missing runtime_iterations_total metric");
2084 assert!(iterations < 10);
2085 });
2086 }
2087
2088 #[cfg(feature = "external")]
2089 #[test]
2090 fn test_realtime_no_skip() {
2091 let executor = deterministic::Runner::default();
2093
2094 executor.start(|context| async move {
2096 context.sleep(Duration::from_secs(1)).await;
2097
2098 let metrics = context.encode();
2100 let iterations = metrics
2101 .lines()
2102 .find_map(|line| {
2103 line.strip_prefix("runtime_iterations_total ")
2104 .and_then(|value| value.trim().parse::<u64>().ok())
2105 })
2106 .expect("missing runtime_iterations_total metric");
2107 assert!(iterations > 500);
2108 });
2109 }
2110
2111 #[test]
2112 #[should_panic(expected = "label must start with [a-zA-Z]")]
2113 fn test_metrics_label_empty() {
2114 let executor = deterministic::Runner::default();
2115 executor.start(|context| async move {
2116 let _ = context.child("");
2117 });
2118 }
2119
2120 #[test]
2121 #[should_panic(expected = "label must start with [a-zA-Z]")]
2122 fn test_metrics_label_invalid_first_char() {
2123 let executor = deterministic::Runner::default();
2124 executor.start(|context| async move {
2125 let _ = context.child("1invalid");
2126 });
2127 }
2128
2129 #[test]
2130 #[should_panic(expected = "label must only contain [a-zA-Z0-9_]")]
2131 fn test_metrics_label_invalid_char() {
2132 let executor = deterministic::Runner::default();
2133 executor.start(|context| async move {
2134 let _ = context.child("invalid-label");
2135 });
2136 }
2137
2138 #[test]
2139 #[should_panic(expected = "using runtime label is not allowed")]
2140 fn test_metrics_label_reserved_prefix() {
2141 let executor = deterministic::Runner::default();
2142 executor.start(|context| async move {
2143 let _ = context.child(METRICS_PREFIX);
2144 });
2145 }
2146
2147 #[test]
2148 fn test_metrics_duplicate_attribute_overwrites() {
2149 let executor = deterministic::Runner::default();
2150 executor.start(|context| async move {
2151 let context = context
2152 .child("test")
2153 .with_attribute("epoch", "old")
2154 .with_attribute("epoch", "new");
2155 assert_eq!(
2156 context.name().attributes,
2157 vec![("epoch".to_string(), "new".to_string())]
2158 );
2159 });
2160 }
2161
2162 #[test]
2163 fn test_storage_fault_injection_and_recovery() {
2164 let cfg = deterministic::Config::default().with_storage_fault_config(FaultConfig {
2166 sync_rate: Some(1.0),
2167 ..Default::default()
2168 });
2169
2170 let (result, checkpoint) =
2171 deterministic::Runner::new(cfg).start_and_recover(|ctx| async move {
2172 let (blob, _) = ctx.open("test_fault", b"blob").await.unwrap();
2173 blob.write_at(0, b"data".to_vec()).await.unwrap();
2174 blob.sync().await });
2176
2177 assert!(result.is_err());
2179
2180 deterministic::Runner::from(checkpoint).start(|ctx| async move {
2182 *ctx.storage_fault_config().write() = FaultConfig::default();
2184
2185 let (blob, len) = ctx.open("test_fault", b"blob").await.unwrap();
2187 assert_eq!(len, 0, "unsynced data should be lost after recovery");
2188
2189 blob.write_at(0, b"recovered".to_vec()).await.unwrap();
2191 blob.sync()
2192 .await
2193 .expect("sync should succeed with faults disabled");
2194
2195 let read_buf = blob.read_at(0, 9).await.unwrap();
2197 assert_eq!(read_buf.coalesce(), b"recovered");
2198 });
2199 }
2200
2201 #[test]
2202 fn test_storage_fault_dynamic_config() {
2203 let executor = deterministic::Runner::default();
2204 executor.start(|ctx| async move {
2205 let (blob, _) = ctx.open("test_dynamic", b"blob").await.unwrap();
2206
2207 blob.write_at(0, b"initial".to_vec()).await.unwrap();
2209 blob.sync().await.expect("initial sync should succeed");
2210
2211 let storage_fault_cfg = ctx.storage_fault_config();
2213 storage_fault_cfg.write().sync_rate = Some(1.0);
2214
2215 blob.write_at(0, b"updated".to_vec()).await.unwrap();
2217 let result = blob.sync().await;
2218 assert!(result.is_err(), "sync should fail with faults enabled");
2219
2220 storage_fault_cfg.write().sync_rate = Some(0.0);
2222
2223 blob.sync()
2225 .await
2226 .expect("sync should succeed with faults disabled");
2227 });
2228 }
2229
2230 #[test]
2231 fn test_storage_fault_determinism() {
2232 fn run_with_seed(seed: u64) -> Vec<bool> {
2234 let cfg = deterministic::Config::default()
2235 .with_seed(seed)
2236 .with_storage_fault_config(FaultConfig {
2237 open_rate: Some(0.5),
2238 ..Default::default()
2239 });
2240
2241 let runner = deterministic::Runner::new(cfg);
2242 runner.start(|ctx| async move {
2243 let mut results = Vec::new();
2244 for i in 0..20 {
2245 let name = format!("blob{i}");
2246 let result = ctx.open("test_determinism", name.as_bytes()).await;
2247 results.push(result.is_ok());
2248 }
2249 results
2250 })
2251 }
2252
2253 let results1 = run_with_seed(12345);
2254 let results2 = run_with_seed(12345);
2255 assert_eq!(
2256 results1, results2,
2257 "same seed should produce same failure pattern"
2258 );
2259
2260 let results3 = run_with_seed(99999);
2261 assert_ne!(
2262 results1, results3,
2263 "different seeds should produce different patterns"
2264 );
2265 }
2266
2267 #[test]
2268 fn test_storage_fault_determinism_multi_task() {
2269 fn run_with_seed(seed: u64) -> Vec<u32> {
2272 let cfg = deterministic::Config::default()
2273 .with_seed(seed)
2274 .with_storage_fault_config(FaultConfig {
2275 open_rate: Some(0.5),
2276 write_rate: Some(0.3),
2277 sync_rate: Some(0.2),
2278 ..Default::default()
2279 });
2280
2281 let runner = deterministic::Runner::new(cfg);
2282 runner.start(|ctx| async move {
2283 let mut handles = Vec::new();
2285 for i in 0..5 {
2286 let ctx = ctx.child("task");
2287 handles.push(ctx.spawn(move |ctx| async move {
2288 let mut successes = 0u32;
2289 for j in 0..4 {
2290 let name = format!("task{i}_blob{j}");
2291 if let Ok((blob, _)) = ctx.open("partition", name.as_bytes()).await {
2292 successes += 1;
2293 if blob.write_at(0, b"data".to_vec()).await.is_ok() {
2294 successes += 1;
2295 }
2296 if blob.sync().await.is_ok() {
2297 successes += 1;
2298 }
2299 }
2300 }
2301 successes
2302 }));
2303 }
2304
2305 let mut results = Vec::new();
2307 for handle in handles {
2308 results.push(handle.await.unwrap());
2309 }
2310 results
2311 })
2312 }
2313
2314 let results1 = run_with_seed(42);
2315 let results2 = run_with_seed(42);
2316 assert_eq!(
2317 results1, results2,
2318 "same seed should produce same multi-task pattern"
2319 );
2320
2321 let results3 = run_with_seed(99999);
2322 assert_ne!(
2323 results1, results3,
2324 "different seeds should produce different patterns"
2325 );
2326 }
2327}