1pub use crate::storage::faulty::{
46 Config as FaultConfig, PartialWriteMode, ResizeConfig, WriteConfig,
47};
48use crate::{
49 BlobVersion, BufferPool, BufferPoolConfig, Clock, Error, Execution, Handle, IoBufs, ListenerOf,
50 METRICS_PREFIX, Name, Panicked, child_label,
51 network::{
52 audited::Network as AuditedNetwork, deterministic::Network as DeterministicNetwork,
53 metered::Network as MeteredNetwork,
54 },
55 prefixed_name,
56 storage::{
57 audited::Storage as AuditedStorage,
58 faulty::Storage as FaultyStorage,
59 memory::{Snapshot as MemStorageSnapshot, Storage as MemStorage},
60 metered::Storage as MeteredStorage,
61 },
62 telemetry::metrics::{
63 Counter, CounterFamily, GaugeFamily, Metric, Register, Registered, Registry, add_attribute,
64 raw, task::Label, validate_label,
65 },
66 utils::{
67 Panicker,
68 signal::{Signal, Stopper},
69 supervision::Tree,
70 },
71};
72#[cfg(feature = "external")]
73use crate::{Blocker, Pacer};
74use commonware_codec::Encode;
75use commonware_formatting::hex;
76use commonware_macros::select;
77use commonware_parallel::{Rayon, ThreadPool};
78use commonware_utils::{
79 Cached, SystemTimeExt,
80 sync::{Mutex, RwLock},
81 time::SYSTEM_TIME_PRECISION,
82};
83#[cfg(feature = "external")]
84use futures::task::noop_waker;
85use futures::{
86 Future,
87 task::{ArcWake, waker},
88};
89use governor::clock::{Clock as GClock, ReasonablyRealtime};
90#[cfg(feature = "external")]
91use pin_project::pin_project;
92use rand::{CryptoRng, Rng, SeedableRng, TryCryptoRng, TryRng, prelude::SliceRandom, rngs::StdRng};
93use rayon::{ThreadPoolBuildError, ThreadPoolBuilder};
94use sha2::{Digest as _, Sha256};
95use std::{
96 collections::{BTreeMap, BinaryHeap, HashMap},
97 convert::Infallible,
98 mem::{replace, take},
99 net::{IpAddr, SocketAddr},
100 num::NonZeroUsize,
101 panic::{AssertUnwindSafe, catch_unwind, resume_unwind},
102 pin::Pin,
103 sync::{Arc, Weak},
104 task::{self, Poll, Waker},
105 time::{Duration, SystemTime, UNIX_EPOCH},
106};
107use tracing::trace;
108
109#[derive(Debug)]
110struct Metrics {
111 iterations: Counter,
112 tasks_spawned: CounterFamily<Label>,
113 tasks_running: GaugeFamily<Label>,
114 task_polls: CounterFamily<Label>,
115}
116
117impl Metrics {
118 pub fn init(registry: &mut impl Register) -> Self {
119 Self {
120 iterations: registry.register(
121 "iterations",
122 "Total number of iterations",
123 raw::Counter::default(),
124 ),
125 tasks_spawned: registry.register(
126 "tasks_spawned",
127 "Total number of tasks spawned",
128 raw::Family::default(),
129 ),
130 tasks_running: registry.register(
131 "tasks_running",
132 "Number of tasks currently running",
133 raw::Family::default(),
134 ),
135 task_polls: registry.register(
136 "task_polls",
137 "Total number of task polls",
138 raw::Family::default(),
139 ),
140 }
141 }
142}
143
144type Digest = [u8; 32];
146
147pub(crate) struct AuditHasher(Sha256);
149
150impl AuditHasher {
151 pub(crate) fn new() -> Self {
153 Self(Sha256::new())
154 }
155
156 pub(crate) fn update(&mut self, value: impl AsRef<[u8]>) {
158 let value = value.as_ref();
159 self.0.update((value.len() as u64).to_be_bytes());
160 self.0.update(value);
161 }
162
163 pub(crate) fn update_bufs(&mut self, bufs: &IoBufs) {
168 self.0.update((bufs.len() as u64).to_be_bytes());
169 bufs.for_each_chunk(|chunk| self.0.update(chunk));
170 }
171
172 pub(crate) fn finalize(self) -> Digest {
174 self.0.finalize().into()
175 }
176}
177
178pub struct Auditor {
180 digest: Mutex<Digest>,
181}
182
183impl Default for Auditor {
184 fn default() -> Self {
185 Self {
186 digest: Digest::default().into(),
187 }
188 }
189}
190
191impl Auditor {
192 pub(crate) fn event<F>(&self, label: &'static [u8], payload: F)
196 where
197 F: FnOnce(&mut AuditHasher),
198 {
199 let mut digest = self.digest.lock();
200
201 let mut hasher = AuditHasher::new();
202 hasher.update(digest.as_ref());
203 hasher.update(label);
204 payload(&mut hasher);
205
206 *digest = hasher.finalize();
207 }
208
209 pub fn state(&self) -> String {
214 let hash = self.digest.lock();
215 hex(hash.as_ref())
216 }
217}
218
219pub type BoxDynRng = Box<dyn CryptoRng + Send + 'static>;
221
222pub struct Config {
224 rng: BoxDynRng,
226
227 cycle: Duration,
230
231 start_time: SystemTime,
233
234 timeout: Option<Duration>,
236
237 catch_panics: bool,
239
240 storage_fault_cfg: FaultConfig,
243
244 network_buffer_pool_cfg: BufferPoolConfig,
246
247 storage_buffer_pool_cfg: BufferPoolConfig,
249}
250
251impl Config {
252 pub fn new() -> Self {
254 cfg_if::cfg_if! {
255 if #[cfg(miri)] {
256 let network_buffer_pool_cfg = BufferPoolConfig::for_network()
258 .with_max_per_class(commonware_utils::NZU32!(32))
259 .with_thread_cache_disabled();
260 let storage_buffer_pool_cfg = BufferPoolConfig::for_storage()
261 .with_max_per_class(commonware_utils::NZU32!(32))
262 .with_thread_cache_disabled();
263 } else {
264 let network_buffer_pool_cfg =
265 BufferPoolConfig::for_network().with_thread_cache_disabled();
266 let storage_buffer_pool_cfg =
267 BufferPoolConfig::for_storage().with_thread_cache_disabled();
268 }
269 }
270
271 Self {
272 rng: Box::new(StdRng::seed_from_u64(42)),
273 cycle: Duration::from_millis(1),
274 start_time: UNIX_EPOCH,
275 timeout: None,
276 catch_panics: false,
277 storage_fault_cfg: FaultConfig::default(),
278 network_buffer_pool_cfg,
279 storage_buffer_pool_cfg,
280 }
281 }
282
283 pub fn with_seed(self, seed: u64) -> Self {
286 let rng: BoxDynRng = Box::new(StdRng::seed_from_u64(seed));
287 self.with_rng(rng)
288 }
289
290 pub fn with_rng(mut self, rng: impl Into<BoxDynRng>) -> Self {
296 self.rng = rng.into();
297 self
298 }
299
300 pub const fn with_cycle(mut self, cycle: Duration) -> Self {
302 self.cycle = cycle;
303 self
304 }
305 pub const fn with_start_time(mut self, start_time: SystemTime) -> Self {
307 self.start_time = start_time;
308 self
309 }
310 pub const fn with_timeout(mut self, timeout: Option<Duration>) -> Self {
312 self.timeout = timeout;
313 self
314 }
315 pub const fn with_catch_panics(mut self, catch_panics: bool) -> Self {
317 self.catch_panics = catch_panics;
318 self
319 }
320 pub fn with_network_buffer_pool_config(mut self, cfg: BufferPoolConfig) -> Self {
322 self.network_buffer_pool_cfg = cfg;
323 self
324 }
325 pub fn with_storage_buffer_pool_config(mut self, cfg: BufferPoolConfig) -> Self {
327 self.storage_buffer_pool_cfg = cfg;
328 self
329 }
330
331 pub const fn with_storage_fault_config(mut self, faults: FaultConfig) -> Self {
337 self.storage_fault_cfg = faults;
338 self
339 }
340
341 pub const fn cycle(&self) -> Duration {
344 self.cycle
345 }
346 pub const fn start_time(&self) -> SystemTime {
348 self.start_time
349 }
350 pub const fn timeout(&self) -> Option<Duration> {
352 self.timeout
353 }
354 pub const fn catch_panics(&self) -> bool {
356 self.catch_panics
357 }
358 pub const fn network_buffer_pool_config(&self) -> &BufferPoolConfig {
360 &self.network_buffer_pool_cfg
361 }
362 pub const fn storage_buffer_pool_config(&self) -> &BufferPoolConfig {
364 &self.storage_buffer_pool_cfg
365 }
366
367 pub fn assert(&self) {
369 assert!(
370 self.cycle != Duration::default() || self.timeout.is_none(),
371 "cycle duration must be non-zero when timeout is set",
372 );
373 assert!(
374 self.cycle >= SYSTEM_TIME_PRECISION,
375 "cycle duration must be greater than or equal to system time precision"
376 );
377 assert!(
378 self.start_time >= UNIX_EPOCH,
379 "start time must be greater than or equal to unix epoch"
380 );
381 }
382}
383
384impl Default for Config {
385 fn default() -> Self {
386 Self::new()
387 }
388}
389
390pub struct Executor {
392 registry: Registry,
393 cycle: Duration,
394 deadline: Option<SystemTime>,
395 metrics: Arc<Metrics>,
396 auditor: Arc<Auditor>,
397 rng: Arc<Mutex<BoxDynRng>>,
398 time: Mutex<SystemTime>,
399 tasks: Arc<Tasks>,
400 sleeping: Mutex<BinaryHeap<Alarm>>,
401 shutdown: Mutex<Stopper>,
402 panicker: Panicker,
403 dns: Mutex<HashMap<String, Vec<IpAddr>>>,
404}
405
406impl Executor {
407 fn advance_time(&self) -> SystemTime {
412 #[cfg(feature = "external")]
413 std::thread::sleep(self.cycle);
414
415 let mut time = self.time.lock();
416 *time = time
417 .checked_add(self.cycle)
418 .expect("executor time overflowed");
419 let now = *time;
420 trace!(now = now.epoch_millis(), "time advanced");
421 now
422 }
423
424 fn assert_deadline(&self, current: SystemTime) {
426 if self.deadline.is_some_and(|deadline| current >= deadline) {
427 panic!("runtime timeout");
428 }
429 }
430
431 fn skip_idle_time(&self, current: SystemTime) -> SystemTime {
436 if cfg!(feature = "external") || self.tasks.ready() != 0 {
437 return current;
438 }
439
440 let mut skip_until = None;
441 {
442 let sleeping = self.sleeping.lock();
443 if let Some(next) = sleeping.peek()
444 && next.time > current
445 {
446 skip_until = Some(next.time);
447 }
448 }
449
450 skip_until.map_or(current, |deadline| {
451 let mut time = self.time.lock();
452 *time = deadline;
453 let now = *time;
454 trace!(now = now.epoch_millis(), "time skipped");
455 now
456 })
457 }
458
459 fn wake_ready_sleepers(&self, current: SystemTime) {
461 let mut sleeping = self.sleeping.lock();
462 while let Some(next) = sleeping.peek() {
463 if next.time <= current {
464 let sleeper = sleeping.pop().unwrap();
465 sleeper.waker.wake();
466 } else {
467 break;
468 }
469 }
470 }
471
472 fn wake_until_progress(&self, mut current: SystemTime) {
479 loop {
480 current = self.skip_idle_time(current);
483 self.assert_deadline(current);
484 self.wake_ready_sleepers(current);
485
486 if cfg!(feature = "external") || self.tasks.ready() != 0 {
489 return;
490 }
491 if self.sleeping.lock().is_empty() {
492 panic!("runtime stalled");
493 }
494 }
495 }
496}
497
498pub struct Checkpoint {
502 cycle: Duration,
503 deadline: Option<SystemTime>,
504 auditor: Arc<Auditor>,
505 rng: Arc<Mutex<BoxDynRng>>,
506 time: Mutex<SystemTime>,
507 storage: MemStorageSnapshot,
508 storage_fault_cfg: FaultConfig,
509 dns: Mutex<HashMap<String, Vec<IpAddr>>>,
510 catch_panics: bool,
511 network_buffer_pool_cfg: BufferPoolConfig,
512 storage_buffer_pool_cfg: BufferPoolConfig,
513}
514
515impl Checkpoint {
516 pub fn auditor(&self) -> Arc<Auditor> {
518 self.auditor.clone()
519 }
520}
521
522#[allow(clippy::large_enum_variant)]
523enum State {
524 Config(Config),
525 Checkpoint(Checkpoint),
526}
527
528pub struct Runner {
530 state: State,
531}
532
533impl From<Config> for Runner {
534 fn from(cfg: Config) -> Self {
535 Self::new(cfg)
536 }
537}
538
539impl From<Checkpoint> for Runner {
540 fn from(checkpoint: Checkpoint) -> Self {
541 Self {
542 state: State::Checkpoint(checkpoint),
543 }
544 }
545}
546
547impl Runner {
548 pub fn new(cfg: Config) -> Self {
550 cfg.assert();
552 Self {
553 state: State::Config(cfg),
554 }
555 }
556
557 pub fn seeded(seed: u64) -> Self {
560 Self::new(Config::default().with_seed(seed))
561 }
562
563 pub fn timed(timeout: Duration) -> Self {
566 let cfg = Config {
567 timeout: Some(timeout),
568 ..Config::default()
569 };
570 Self::new(cfg)
571 }
572
573 pub fn start_and_recover<F, Fut>(self, f: F) -> (Fut::Output, Checkpoint)
576 where
577 F: FnOnce(Context) -> Fut,
578 Fut: Future,
579 {
580 let (context, executor, panicked) = match self.state {
582 State::Config(config) => Context::new(config),
583 State::Checkpoint(checkpoint) => Context::recover(checkpoint),
584 };
585
586 let storage = context.storage.clone();
588 let network_buffer_pool_cfg = context.network_buffer_pool.config().clone();
589 let storage_buffer_pool_cfg = context.storage_buffer_pool.config().clone();
590 let mut root = Box::pin(panicked.interrupt(f(context)));
591
592 Tasks::register_root(&executor.tasks);
594
595 let result = catch_unwind(AssertUnwindSafe(|| {
598 loop {
599 let current = *executor.time.lock();
601 executor.assert_deadline(current);
602
603 let mut queue = executor.tasks.drain();
605
606 if queue.len() > 1 {
608 let mut rng = executor.rng.lock();
609 queue.shuffle(&mut *rng);
610 }
611
612 trace!(
618 iter = executor.metrics.iterations.get(),
619 tasks = queue.len(),
620 "starting loop"
621 );
622 let mut output = None;
623 for id in queue {
624 let Some(task) = executor.tasks.get(id) else {
626 trace!(id, "skipping missing task");
627 continue;
628 };
629
630 executor.auditor.event(b"process_task", |hasher| {
632 hasher.update(task.id.to_be_bytes());
633 hasher.update(task.label.name().as_bytes());
634 });
635 executor.metrics.task_polls.get_or_create(&task.label).inc();
636 trace!(id, "processing task");
637
638 let waker = waker(Arc::new(TaskWaker {
640 id,
641 tasks: Arc::downgrade(&executor.tasks),
642 }));
643 let mut cx = task::Context::from_waker(&waker);
644
645 match &task.mode {
647 Mode::Root => {
648 if let Poll::Ready(result) = root.as_mut().poll(&mut cx) {
650 trace!(id, "root task is complete");
651 output = Some(result);
652 break;
653 }
654 }
655 Mode::Work(future) => {
656 let mut fut_opt = future.lock();
658 let Some(fut) = fut_opt.as_mut() else {
659 trace!(id, "skipping already complete task");
660
661 executor.tasks.remove(id);
663 continue;
664 };
665
666 if fut.as_mut().poll(&mut cx).is_ready() {
668 trace!(id, "task is complete");
669
670 executor.tasks.remove(id);
672 *fut_opt = None;
673 continue;
674 }
675 }
676 }
677
678 trace!(id, "task is still pending");
680 }
681
682 if let Some(output) = output {
684 break output;
685 }
686
687 let current = executor.advance_time();
689 executor.wake_until_progress(current);
690
691 executor.metrics.iterations.inc();
693 }
694 }));
695
696 executor.sleeping.lock().clear(); let tasks = executor.tasks.clear();
704 for task in tasks {
705 let Mode::Work(future) = &task.mode else {
706 continue;
707 };
708 *future.lock() = None;
709 }
710
711 drop(root);
715
716 storage
718 .inner()
719 .inner()
720 .crash()
721 .expect("retaining successful unsynced writes at crash should succeed");
722 let storage_fault_cfg = storage.inner().inner().config().read().clone();
723 let storage = storage.inner().inner().inner().take_snapshot();
724
725 assert!(
728 Arc::weak_count(&executor) == 0,
729 "executor still has weak references"
730 );
731
732 let output = match result {
734 Ok(output) => output,
735 Err(payload) => resume_unwind(payload),
736 };
737
738 let executor = Arc::into_inner(executor).expect("executor still has strong references");
740
741 let checkpoint = Checkpoint {
743 cycle: executor.cycle,
744 deadline: executor.deadline,
745 auditor: executor.auditor,
746 rng: executor.rng,
747 time: executor.time,
748 storage,
749 storage_fault_cfg,
750 dns: executor.dns,
751 catch_panics: executor.panicker.catch(),
752 network_buffer_pool_cfg,
753 storage_buffer_pool_cfg,
754 };
755
756 (output, checkpoint)
757 }
758}
759
760impl Default for Runner {
761 fn default() -> Self {
762 Self::new(Config::default())
763 }
764}
765
766impl crate::Runner for Runner {
767 type Context = Context;
768
769 fn start<F, Fut>(self, f: F) -> Fut::Output
770 where
771 F: FnOnce(Self::Context) -> Fut,
772 Fut: Future,
773 {
774 let (output, _) = self.start_and_recover(f);
775 output
776 }
777}
778
779enum Mode {
781 Root,
782 Work(Mutex<Option<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>>),
783}
784
785struct Task {
787 id: u128,
788 label: Label,
789
790 mode: Mode,
791}
792
793struct TaskWaker {
795 id: u128,
796
797 tasks: Weak<Tasks>,
798}
799
800impl ArcWake for TaskWaker {
801 fn wake_by_ref(arc_self: &Arc<Self>) {
802 if let Some(tasks) = arc_self.tasks.upgrade() {
807 tasks.queue(arc_self.id);
808 }
809 }
810}
811
812struct Tasks {
814 counter: Mutex<u128>,
816 ready: Mutex<Vec<u128>>,
818 running: Mutex<BTreeMap<u128, Arc<Task>>>,
820}
821
822impl Tasks {
823 const fn new() -> Self {
825 Self {
826 counter: Mutex::new(0),
827 ready: Mutex::new(Vec::new()),
828 running: Mutex::new(BTreeMap::new()),
829 }
830 }
831
832 fn increment(&self) -> u128 {
834 let mut counter = self.counter.lock();
835 let old = *counter;
836 *counter = counter.checked_add(1).expect("task counter overflow");
837 old
838 }
839
840 fn register_root(arc_self: &Arc<Self>) {
844 let id = arc_self.increment();
845 let task = Arc::new(Task {
846 id,
847 label: Label::root(),
848 mode: Mode::Root,
849 });
850 arc_self.register(id, task);
851 }
852
853 fn register_work(
855 arc_self: &Arc<Self>,
856 label: Label,
857 future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
858 ) {
859 let id = arc_self.increment();
860 let task = Arc::new(Task {
861 id,
862 label,
863 mode: Mode::Work(Mutex::new(Some(future))),
864 });
865 arc_self.register(id, task);
866 }
867
868 fn register(&self, id: u128, task: Arc<Task>) {
870 self.running.lock().insert(id, task);
872
873 self.queue(id);
875 }
876
877 fn queue(&self, id: u128) {
879 let mut ready = self.ready.lock();
880 ready.push(id);
881 }
882
883 fn drain(&self) -> Vec<u128> {
885 let mut queue = self.ready.lock();
886 let len = queue.len();
887 replace(&mut *queue, Vec::with_capacity(len))
888 }
889
890 fn ready(&self) -> usize {
892 self.ready.lock().len()
893 }
894
895 fn get(&self, id: u128) -> Option<Arc<Task>> {
900 let running = self.running.lock();
901 running.get(&id).cloned()
902 }
903
904 fn remove(&self, id: u128) {
906 self.running.lock().remove(&id);
907 }
908
909 fn clear(&self) -> Vec<Arc<Task>> {
911 self.ready.lock().clear();
913
914 let running: BTreeMap<u128, Arc<Task>> = {
916 let mut running = self.running.lock();
917 take(&mut *running)
918 };
919 running.into_values().collect()
920 }
921}
922
923type Network = MeteredNetwork<AuditedNetwork<DeterministicNetwork>>;
924type Storage = MeteredStorage<AuditedStorage<FaultyStorage<MemStorage>>>;
925
926fn build_storage(
927 inner: MemStorage,
928 rng: Arc<Mutex<BoxDynRng>>,
929 faults: FaultConfig,
930 auditor: Arc<Auditor>,
931 registry: &mut impl Register,
932) -> Storage {
933 MeteredStorage::new(
934 AuditedStorage::new(
935 FaultyStorage::new(inner, rng, Arc::new(RwLock::new(faults))),
936 auditor,
937 ),
938 registry,
939 )
940}
941
942pub struct Context {
946 name: String,
947 attributes: Vec<(String, String)>,
948 executor: Weak<Executor>,
949 network: Arc<Network>,
950 storage: Arc<Storage>,
951 network_buffer_pool: BufferPool,
952 storage_buffer_pool: BufferPool,
953 tree: Arc<Tree>,
954 execution: Execution,
955}
956
957impl Context {
958 fn new(cfg: Config) -> (Self, Arc<Executor>, Panicked) {
959 let mut registry = Registry::new();
961 let mut runtime_registry = registry.sub_registry(METRICS_PREFIX);
962
963 let metrics = Arc::new(Metrics::init(&mut runtime_registry));
965 let start_time = cfg.start_time;
966 let deadline = cfg
967 .timeout
968 .map(|timeout| start_time.checked_add(timeout).expect("timeout overflowed"));
969 let auditor = Arc::new(Auditor::default());
970
971 let rng = Arc::new(Mutex::new(cfg.rng));
973
974 let network_buffer_pool = BufferPool::new(
976 cfg.network_buffer_pool_cfg.clone(),
977 &mut runtime_registry.sub_registry("network_buffer_pool"),
978 );
979 let storage_buffer_pool = BufferPool::new(
980 cfg.storage_buffer_pool_cfg.clone(),
981 &mut runtime_registry.sub_registry("storage_buffer_pool"),
982 );
983
984 let storage = build_storage(
985 MemStorage::new(storage_buffer_pool.clone()),
986 rng.clone(),
987 cfg.storage_fault_cfg,
988 auditor.clone(),
989 &mut runtime_registry,
990 );
991
992 let network = AuditedNetwork::new(DeterministicNetwork::default(), auditor.clone());
994 let network = MeteredNetwork::new(network, &mut runtime_registry);
995
996 let (panicker, panicked) = Panicker::new(cfg.catch_panics);
998
999 let executor = Arc::new(Executor {
1000 registry,
1001 cycle: cfg.cycle,
1002 deadline,
1003 metrics,
1004 auditor,
1005 rng,
1006 time: Mutex::new(start_time),
1007 tasks: Arc::new(Tasks::new()),
1008 sleeping: Mutex::new(BinaryHeap::new()),
1009 shutdown: Mutex::new(Stopper::default()),
1010 panicker,
1011 dns: Mutex::new(HashMap::new()),
1012 });
1013
1014 (
1015 Self {
1016 name: String::new(),
1017 attributes: Vec::new(),
1018 executor: Arc::downgrade(&executor),
1019 network: Arc::new(network),
1020 storage: Arc::new(storage),
1021 network_buffer_pool,
1022 storage_buffer_pool,
1023 tree: Tree::root(),
1024 execution: Execution::default(),
1025 },
1026 executor,
1027 panicked,
1028 )
1029 }
1030
1031 fn recover(checkpoint: Checkpoint) -> (Self, Arc<Executor>, Panicked) {
1044 let mut registry = Registry::new();
1046 let mut runtime_registry = registry.sub_registry(METRICS_PREFIX);
1047 let metrics = Arc::new(Metrics::init(&mut runtime_registry));
1048
1049 let network =
1051 AuditedNetwork::new(DeterministicNetwork::default(), checkpoint.auditor.clone());
1052 let network = MeteredNetwork::new(network, &mut runtime_registry);
1053
1054 let network_buffer_pool = BufferPool::new(
1056 checkpoint.network_buffer_pool_cfg.clone(),
1057 &mut runtime_registry.sub_registry("network_buffer_pool"),
1058 );
1059 let storage_buffer_pool = BufferPool::new(
1060 checkpoint.storage_buffer_pool_cfg.clone(),
1061 &mut runtime_registry.sub_registry("storage_buffer_pool"),
1062 );
1063 let storage = build_storage(
1064 MemStorage::from_snapshot(checkpoint.storage, storage_buffer_pool.clone()),
1065 checkpoint.rng.clone(),
1066 checkpoint.storage_fault_cfg,
1067 checkpoint.auditor.clone(),
1068 &mut runtime_registry,
1069 );
1070
1071 let (panicker, panicked) = Panicker::new(checkpoint.catch_panics);
1073
1074 let executor = Arc::new(Executor {
1075 cycle: checkpoint.cycle,
1077 deadline: checkpoint.deadline,
1078 auditor: checkpoint.auditor,
1079 rng: checkpoint.rng,
1080 time: checkpoint.time,
1081 dns: checkpoint.dns,
1082
1083 registry,
1085 metrics,
1086 tasks: Arc::new(Tasks::new()),
1087 sleeping: Mutex::new(BinaryHeap::new()),
1088 shutdown: Mutex::new(Stopper::default()),
1089 panicker,
1090 });
1091 (
1092 Self {
1093 name: String::new(),
1094 attributes: Vec::new(),
1095 executor: Arc::downgrade(&executor),
1096 network: Arc::new(network),
1097 storage: Arc::new(storage),
1098 network_buffer_pool,
1099 storage_buffer_pool,
1100 tree: Tree::root(),
1101 execution: Execution::default(),
1102 },
1103 executor,
1104 panicked,
1105 )
1106 }
1107
1108 fn executor(&self) -> Arc<Executor> {
1110 self.executor.upgrade().expect("executor already dropped")
1111 }
1112
1113 fn metrics(&self) -> Arc<Metrics> {
1115 self.executor().metrics.clone()
1116 }
1117
1118 pub fn auditor(&self) -> Arc<Auditor> {
1120 self.executor().auditor.clone()
1121 }
1122
1123 pub fn storage_audit(&self) -> Digest {
1125 self.storage.inner().inner().inner().audit()
1126 }
1127
1128 pub fn storage_fault_config(&self) -> Arc<RwLock<FaultConfig>> {
1134 self.storage.inner().inner().config()
1135 }
1136
1137 pub fn resolver_register(&self, host: impl Into<String>, addrs: Option<Vec<IpAddr>>) {
1142 let executor = self.executor();
1144 let host = host.into();
1145 executor.auditor.event(b"resolver_register", |hasher| {
1146 hasher.update(host.as_bytes());
1147 hasher.update(addrs.encode());
1148 });
1149
1150 let mut dns = executor.dns.lock();
1152 match addrs {
1153 Some(addrs) => {
1154 dns.insert(host, addrs);
1155 }
1156 None => {
1157 dns.remove(&host);
1158 }
1159 }
1160 }
1161}
1162
1163impl crate::Spawner for Context {
1164 fn dedicated(mut self) -> Self {
1165 self.execution = Execution::Dedicated;
1166 self
1167 }
1168
1169 fn shared(mut self, blocking: bool) -> Self {
1170 self.execution = Execution::Shared(blocking);
1171 self
1172 }
1173
1174 fn spawn<F, Fut, T>(mut self, f: F) -> Handle<T>
1175 where
1176 F: FnOnce(Self) -> Fut + Send + 'static,
1177 Fut: Future<Output = T> + Send + 'static,
1178 T: Send + 'static,
1179 {
1180 let (label, metric) = spawn_metrics!(self);
1182
1183 let parent = Arc::clone(&self.tree);
1185 self.execution = Execution::default();
1186 let (child, aborted) = Tree::child(&parent);
1187 if aborted {
1188 return Handle::closed(metric);
1189 }
1190 self.tree = child;
1191
1192 let executor = self.executor();
1194 let future = f(self);
1195 let (f, handle) = Handle::init(
1196 future,
1197 metric,
1198 executor.panicker.clone(),
1199 Arc::clone(&parent),
1200 );
1201 Tasks::register_work(&executor.tasks, label, Box::pin(f));
1202
1203 if let Some(aborter) = handle.aborter() {
1205 parent.register(aborter);
1206 }
1207
1208 handle
1209 }
1210
1211 async fn stop(self, value: i32, timeout: Option<Duration>) -> Result<(), Error> {
1212 let executor = self.executor();
1213 executor.auditor.event(b"stop", |hasher| {
1214 hasher.update(value.to_be_bytes());
1215 });
1216 let stop_resolved = {
1217 let mut shutdown = executor.shutdown.lock();
1218 shutdown.stop(value)
1219 };
1220
1221 let timeout_future = timeout.map_or_else(
1223 || futures::future::Either::Right(futures::future::pending()),
1224 |duration| futures::future::Either::Left(self.sleep(duration)),
1225 );
1226 select! {
1227 result = stop_resolved => {
1228 result.map_err(|_| Error::Closed)?;
1229 Ok(())
1230 },
1231 _ = timeout_future => Err(Error::Timeout),
1232 }
1233 }
1234
1235 fn stopped(&self) -> Signal {
1236 let executor = self.executor();
1237 executor.auditor.event(b"stopped", |_| {});
1238
1239 executor.shutdown.lock().stopped()
1240 }
1241}
1242
1243commonware_utils::thread_local_cache!(static THREAD_POOL: ThreadPool);
1246
1247fn shared_thread_pool() -> Result<ThreadPool, ThreadPoolBuildError> {
1252 let pool = Cached::take(
1253 &THREAD_POOL,
1254 || {
1255 ThreadPoolBuilder::new()
1256 .num_threads(1)
1257 .use_current_thread()
1258 .build()
1259 .map(Arc::new)
1260 },
1261 |_| Ok(()),
1262 )?;
1263 Ok(Arc::clone(&pool))
1264}
1265
1266impl crate::Strategizer for Context {
1276 fn strategy(&self, parallelism: NonZeroUsize) -> Rayon {
1277 Rayon::with_pool(
1278 shared_thread_pool().expect("failed to create deterministic Rayon thread pool"),
1279 )
1280 .with_parallelism(parallelism)
1281 }
1282}
1283
1284impl crate::Supervisor for Context {
1285 fn child(&self, label: &'static str) -> Self {
1286 let (tree, _) = Tree::child(&self.tree);
1287 Self {
1288 name: child_label(&self.name, label),
1289 attributes: self.attributes.clone(),
1290 executor: self.executor.clone(),
1291 network: self.network.clone(),
1292 storage: self.storage.clone(),
1293 network_buffer_pool: self.network_buffer_pool.clone(),
1294 storage_buffer_pool: self.storage_buffer_pool.clone(),
1295 tree,
1296 execution: Execution::default(),
1297 }
1298 }
1299
1300 fn with_attribute(mut self, key: &'static str, value: impl std::fmt::Display) -> Self {
1301 validate_label(key);
1303
1304 add_attribute(&mut self.attributes, key, value);
1306 self
1307 }
1308
1309 fn name(&self) -> Name {
1310 Name {
1311 label: self.name.clone(),
1312 attributes: self.attributes.clone(),
1313 }
1314 }
1315}
1316
1317impl crate::Metrics for Context {
1318 fn register<N: Into<String>, H: Into<String>, M: Metric>(
1319 &self,
1320 name: N,
1321 help: H,
1322 metric: M,
1323 ) -> Registered<M> {
1324 let name = name.into();
1325 let help = help.into();
1326 let executor = self.executor();
1327 executor.auditor.event(b"register", |hasher| {
1328 hasher.update(name.as_bytes());
1329 hasher.update(help.as_bytes());
1330 for (k, v) in &self.attributes {
1331 hasher.update(k.as_bytes());
1332 hasher.update(v.as_bytes());
1333 }
1334 });
1335 let metric = Arc::new(metric);
1336 executor.registry.register(
1337 prefixed_name(&self.name, &name),
1338 help,
1339 self.attributes.clone(),
1340 metric,
1341 )
1342 }
1343
1344 fn encode(&self) -> String {
1345 let executor = self.executor();
1346 executor.auditor.event(b"encode", |_| {});
1347 executor.registry.encode()
1348 }
1349}
1350
1351struct Sleeper {
1352 executor: Weak<Executor>,
1353 time: SystemTime,
1354 registered: bool,
1355}
1356
1357impl Sleeper {
1358 fn executor(&self) -> Arc<Executor> {
1360 self.executor.upgrade().expect("executor already dropped")
1361 }
1362}
1363
1364struct Alarm {
1365 time: SystemTime,
1366 waker: Waker,
1367}
1368
1369impl PartialEq for Alarm {
1370 fn eq(&self, other: &Self) -> bool {
1371 self.time.eq(&other.time)
1372 }
1373}
1374
1375impl Eq for Alarm {}
1376
1377impl PartialOrd for Alarm {
1378 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1379 Some(self.cmp(other))
1380 }
1381}
1382
1383impl Ord for Alarm {
1384 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1385 other.time.cmp(&self.time)
1387 }
1388}
1389
1390impl Future for Sleeper {
1391 type Output = ();
1392
1393 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
1394 let executor = self.executor();
1395 {
1396 let current_time = *executor.time.lock();
1397 if current_time >= self.time {
1398 return Poll::Ready(());
1399 }
1400 }
1401 if !self.registered {
1402 self.registered = true;
1403 executor.sleeping.lock().push(Alarm {
1404 time: self.time,
1405 waker: cx.waker().clone(),
1406 });
1407 }
1408 Poll::Pending
1409 }
1410}
1411
1412impl Clock for Context {
1413 fn current(&self) -> SystemTime {
1414 *self.executor().time.lock()
1415 }
1416
1417 fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send + 'static {
1418 let deadline = self
1419 .current()
1420 .checked_add(duration)
1421 .expect("overflow when setting wake time");
1422 self.sleep_until(deadline)
1423 }
1424
1425 fn sleep_until(&self, deadline: SystemTime) -> impl Future<Output = ()> + Send + 'static {
1426 Sleeper {
1427 executor: self.executor.clone(),
1428
1429 time: deadline,
1430 registered: false,
1431 }
1432 }
1433}
1434
1435#[cfg(feature = "external")]
1439#[pin_project]
1440struct Waiter<F: Future> {
1441 executor: Weak<Executor>,
1442 target: SystemTime,
1443 #[pin]
1444 future: F,
1445 ready: Option<F::Output>,
1446 started: bool,
1447 registered: bool,
1448}
1449
1450#[cfg(feature = "external")]
1451impl<F> Future for Waiter<F>
1452where
1453 F: Future + Send,
1454{
1455 type Output = F::Output;
1456
1457 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
1458 let mut this = self.project();
1459
1460 if !*this.started {
1464 *this.started = true;
1465 let waker = noop_waker();
1466 let mut cx_noop = task::Context::from_waker(&waker);
1467 if let Poll::Ready(value) = this.future.as_mut().poll(&mut cx_noop) {
1468 *this.ready = Some(value);
1469 }
1470 }
1471
1472 let executor = this.executor.upgrade().expect("executor already dropped");
1474 let current_time = *executor.time.lock();
1475 if current_time < *this.target {
1476 if !*this.registered {
1479 *this.registered = true;
1480 executor.sleeping.lock().push(Alarm {
1481 time: *this.target,
1482 waker: cx.waker().clone(),
1483 });
1484 }
1485 return Poll::Pending;
1486 }
1487
1488 if let Some(value) = this.ready.take() {
1490 return Poll::Ready(value);
1491 }
1492
1493 let blocker = Blocker::new();
1496 loop {
1497 let waker = waker(blocker.clone());
1498 let mut cx_block = task::Context::from_waker(&waker);
1499 match this.future.as_mut().poll(&mut cx_block) {
1500 Poll::Ready(value) => {
1501 break Poll::Ready(value);
1502 }
1503 Poll::Pending => blocker.wait(),
1504 }
1505 }
1506 }
1507}
1508
1509#[cfg(feature = "external")]
1510impl Pacer for Context {
1511 fn pace<'a, F, T>(&'a self, latency: Duration, future: F) -> impl Future<Output = T> + Send + 'a
1512 where
1513 F: Future<Output = T> + Send + 'a,
1514 T: Send + 'a,
1515 {
1516 let target = self
1518 .executor()
1519 .time
1520 .lock()
1521 .checked_add(latency)
1522 .expect("overflow when setting wake time");
1523
1524 Waiter {
1525 executor: self.executor.clone(),
1526 target,
1527 future,
1528 ready: None,
1529 started: false,
1530 registered: false,
1531 }
1532 }
1533}
1534
1535impl GClock for Context {
1536 type Instant = SystemTime;
1537
1538 fn now(&self) -> Self::Instant {
1539 self.current()
1540 }
1541}
1542
1543impl ReasonablyRealtime for Context {}
1544
1545impl crate::Network for Context {
1546 type Listener = ListenerOf<Network>;
1547
1548 async fn bind(&self, socket: SocketAddr) -> Result<Self::Listener, Error> {
1549 self.network.bind(socket).await
1550 }
1551
1552 async fn dial(
1553 &self,
1554 socket: SocketAddr,
1555 ) -> Result<(crate::SinkOf<Self>, crate::StreamOf<Self>), Error> {
1556 self.network.dial(socket).await
1557 }
1558}
1559
1560impl crate::Resolver for Context {
1561 async fn resolve(&self, host: &str) -> Result<Vec<IpAddr>, Error> {
1562 let executor = self.executor();
1564 let dns = executor.dns.lock();
1565 let result = dns.get(host).cloned();
1566 drop(dns);
1567
1568 executor.auditor.event(b"resolve", |hasher| {
1570 hasher.update(host.as_bytes());
1571 hasher.update(result.encode());
1572 });
1573 result.ok_or_else(|| Error::ResolveFailed(host.to_string()))
1574 }
1575}
1576
1577impl TryRng for Context {
1578 type Error = Infallible;
1579
1580 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
1581 let executor = self.executor();
1582 executor.auditor.event(b"rand", |hasher| {
1583 hasher.update(b"next_u32");
1584 });
1585 let result = executor.rng.lock().next_u32();
1586 Ok(result)
1587 }
1588
1589 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
1590 let executor = self.executor();
1591 executor.auditor.event(b"rand", |hasher| {
1592 hasher.update(b"next_u64");
1593 });
1594 let result = executor.rng.lock().next_u64();
1595 Ok(result)
1596 }
1597
1598 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
1599 let executor = self.executor();
1600 executor.auditor.event(b"rand", |hasher| {
1601 hasher.update(b"fill_bytes");
1602 });
1603 executor.rng.lock().fill_bytes(dest);
1604 Ok(())
1605 }
1606}
1607
1608impl TryCryptoRng for Context {}
1609
1610impl crate::Storage for Context {
1611 type Blob = <Storage as crate::Storage>::Blob;
1612
1613 async fn open_versioned(
1614 &self,
1615 partition: &str,
1616 name: &[u8],
1617 versions: std::ops::RangeInclusive<BlobVersion>,
1618 ) -> Result<(Self::Blob, u64, BlobVersion), Error> {
1619 self.storage.open_versioned(partition, name, versions).await
1620 }
1621
1622 async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
1623 self.storage.remove(partition, name).await
1624 }
1625
1626 async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
1627 self.storage.scan(partition).await
1628 }
1629}
1630
1631impl crate::BufferPooler for Context {
1632 fn network_buffer_pool(&self) -> &crate::BufferPool {
1633 &self.network_buffer_pool
1634 }
1635
1636 fn storage_buffer_pool(&self) -> &crate::BufferPool {
1637 &self.storage_buffer_pool
1638 }
1639}
1640
1641#[cfg(test)]
1642mod tests {
1643 use super::*;
1644 #[cfg(feature = "external")]
1645 use crate::FutureExt;
1646 use crate::{
1647 Blob, Metrics as _, ReadOptions, Resolver, Runner as _, Spawner as _, Storage, Strategizer,
1648 Supervisor as _, WriteOptions, deterministic, reschedule,
1649 };
1650 use commonware_macros::test_traced;
1651 use commonware_parallel::Strategy;
1652 #[cfg(feature = "external")]
1653 use commonware_utils::channel::mpsc;
1654 use commonware_utils::{NZUsize, ScriptedRng, channel::oneshot, probability};
1655 #[cfg(feature = "external")]
1656 use futures::StreamExt;
1657 #[cfg(not(feature = "external"))]
1658 use futures::future::pending;
1659 #[cfg(not(feature = "external"))]
1660 use futures::stream::StreamExt as _;
1661 use futures::{FutureExt as _, stream::FuturesUnordered, task::noop_waker};
1662
1663 async fn task(i: usize) -> usize {
1664 for _ in 0..5 {
1665 reschedule().await;
1666 }
1667 i
1668 }
1669
1670 fn run_tasks(tasks: usize, runner: deterministic::Runner) -> (String, Vec<usize>) {
1671 runner.start(|context| async move {
1672 let mut handles = FuturesUnordered::new();
1673 for i in 0..=tasks - 1 {
1674 handles.push(context.child("task").spawn(move |_| task(i)));
1675 }
1676
1677 let mut outputs = Vec::new();
1678 while let Some(result) = handles.next().await {
1679 outputs.push(result.unwrap());
1680 }
1681 assert_eq!(outputs.len(), tasks);
1682 (context.auditor().state(), outputs)
1683 })
1684 }
1685
1686 fn run_with_seed(seed: u64) -> (String, Vec<usize>) {
1687 let executor = deterministic::Runner::seeded(seed);
1688 run_tasks(5, executor)
1689 }
1690
1691 fn run_with_metric(name: &'static str, help: &'static str) -> String {
1692 deterministic::Runner::default().start(|context| async move {
1693 let _: Registered<raw::Counter> = context.register(name, help, raw::Counter::default());
1694 context.auditor().state()
1695 })
1696 }
1697
1698 #[test]
1699 fn test_auditor_separates_metric_fields() {
1700 let state_a = run_with_metric("a", "bc");
1701 let state_b = run_with_metric("ab", "c");
1702
1703 assert_ne!(state_a, state_b);
1704 }
1705
1706 #[test]
1707 fn test_same_seed_same_order() {
1708 let mut outputs = Vec::new();
1710 for seed in 0..1000 {
1711 let output = run_with_seed(seed);
1712 outputs.push(output);
1713 }
1714
1715 for seed in 0..1000 {
1717 let output = run_with_seed(seed);
1718 assert_eq!(output, outputs[seed as usize]);
1719 }
1720 }
1721
1722 #[test_traced("TRACE")]
1723 fn test_different_seeds_different_order() {
1724 let output1 = run_with_seed(12345);
1725 let output2 = run_with_seed(54321);
1726 assert_ne!(output1, output2);
1727 }
1728
1729 #[test]
1730 fn test_alarm_min_heap() {
1731 let now = SystemTime::now();
1733 let alarms = vec![
1734 Alarm {
1735 time: now + Duration::new(10, 0),
1736 waker: noop_waker(),
1737 },
1738 Alarm {
1739 time: now + Duration::new(5, 0),
1740 waker: noop_waker(),
1741 },
1742 Alarm {
1743 time: now + Duration::new(15, 0),
1744 waker: noop_waker(),
1745 },
1746 Alarm {
1747 time: now + Duration::new(5, 0),
1748 waker: noop_waker(),
1749 },
1750 ];
1751 let mut heap = BinaryHeap::new();
1752 for alarm in alarms {
1753 heap.push(alarm);
1754 }
1755
1756 let mut sorted_times = Vec::new();
1758 while let Some(alarm) = heap.pop() {
1759 sorted_times.push(alarm.time);
1760 }
1761 assert_eq!(
1762 sorted_times,
1763 vec![
1764 now + Duration::new(5, 0),
1765 now + Duration::new(5, 0),
1766 now + Duration::new(10, 0),
1767 now + Duration::new(15, 0),
1768 ]
1769 );
1770 }
1771
1772 #[test]
1773 fn test_dropped_sleeper_before_live_deadline() {
1774 let executor = deterministic::Runner::default();
1775 executor.start(|context| async move {
1776 let (started_sender, started_receiver) = oneshot::channel();
1777 let sleeper = context.child("sleeper").spawn(|context| async move {
1778 let mut sleepers = FuturesUnordered::new();
1779 sleepers.push(context.sleep(Duration::from_secs(1)));
1780 started_sender.send(()).unwrap();
1781 sleepers.next().await;
1782 });
1783
1784 started_receiver.await.unwrap();
1786 sleeper.abort();
1787
1788 context.sleep(Duration::from_secs(2)).await;
1790 });
1791 }
1792
1793 #[cfg(not(feature = "external"))]
1794 #[test]
1795 #[should_panic(expected = "runtime timeout")]
1796 fn test_dropped_sleeper_beyond_timeout() {
1797 let executor = deterministic::Runner::timed(Duration::from_secs(10));
1798 executor.start(|context| async move {
1799 let (started_sender, started_receiver) = oneshot::channel();
1800 let sleeper = context.child("sleeper").spawn(|context| async move {
1801 let mut sleep = Box::pin(context.sleep(Duration::from_secs(20)));
1802 assert!(sleep.as_mut().now_or_never().is_none());
1803 started_sender.send(()).unwrap();
1804 sleep.await;
1805 });
1806
1807 started_receiver.await.unwrap();
1808 sleeper.abort();
1809 pending::<()>().await;
1810 });
1811 }
1812
1813 #[test]
1814 #[should_panic(expected = "runtime timeout")]
1815 fn test_timeout() {
1816 let executor = deterministic::Runner::timed(Duration::from_secs(10));
1817 executor.start(|context| async move {
1818 loop {
1819 context.sleep(Duration::from_secs(1)).await;
1820 }
1821 });
1822 }
1823
1824 #[test]
1825 #[should_panic(expected = "cycle duration must be non-zero when timeout is set")]
1826 fn test_bad_timeout() {
1827 let cfg = Config {
1828 timeout: Some(Duration::default()),
1829 cycle: Duration::default(),
1830 ..Config::default()
1831 };
1832 deterministic::Runner::new(cfg);
1833 }
1834
1835 #[test]
1836 #[should_panic(
1837 expected = "cycle duration must be greater than or equal to system time precision"
1838 )]
1839 fn test_bad_cycle() {
1840 let cfg = Config {
1841 cycle: SYSTEM_TIME_PRECISION - Duration::from_nanos(1),
1842 ..Config::default()
1843 };
1844 deterministic::Runner::new(cfg);
1845 }
1846
1847 #[test]
1848 fn test_recover_synced_storage_persists() {
1849 let executor1 = deterministic::Runner::default();
1851 let partition = "test_partition";
1852 let name = b"test_blob";
1853 let data = b"Hello, world!";
1854
1855 let (state, checkpoint) = executor1.start_and_recover(|context| async move {
1857 let (blob, _) = context.open(partition, name).await.unwrap();
1858 blob.write_at(0, data, WriteOptions::default())
1859 .await
1860 .unwrap();
1861 blob.sync().await.unwrap();
1862 context.auditor().state()
1863 });
1864
1865 assert_eq!(state, checkpoint.auditor.state());
1867
1868 let executor = Runner::from(checkpoint);
1870 executor.start(|context| async move {
1871 let (blob, len) = context.open(partition, name).await.unwrap();
1872 assert_eq!(len, data.len() as u64);
1873 let read = blob
1874 .read_at(0, data.len(), ReadOptions::default())
1875 .await
1876 .unwrap();
1877 assert_eq!(read.coalesce(), data);
1878 });
1879 }
1880
1881 #[test]
1882 #[should_panic(expected = "goodbye")]
1883 fn test_recover_panic_handling() {
1884 let executor1 = deterministic::Runner::default();
1886 let (_, checkpoint) = executor1.start_and_recover(|_| async move {
1887 reschedule().await;
1888 });
1889
1890 let executor = Runner::from(checkpoint);
1892 executor.start(|_| async move {
1893 panic!("goodbye");
1894 });
1895 }
1896
1897 #[test]
1898 fn test_recover_unsynced_storage_does_not_persist() {
1899 let executor = deterministic::Runner::default();
1901 let partition = "test_partition";
1902 let name = b"test_blob";
1903 let data = b"Hello, world!";
1904
1905 let (_, checkpoint) = executor.start_and_recover(|context| async move {
1907 let (blob, _) = context.open(partition, name).await.unwrap();
1908 blob.write_at(0, data, WriteOptions::default())
1909 .await
1910 .unwrap();
1911 });
1912
1913 let executor = Runner::from(checkpoint);
1915
1916 executor.start(|context| async move {
1918 let (_, len) = context.open(partition, name).await.unwrap();
1919 assert_eq!(len, 0);
1920 });
1921 }
1922
1923 #[test]
1924 fn test_recover_snapshots_fault_configuration() {
1925 let (stale_config, checkpoint) =
1926 deterministic::Runner::default().start_and_recover(|context| async move {
1927 let config = context.storage_fault_config();
1928 *config.write() = FaultConfig::default().open(probability!(1.0));
1929 config
1930 });
1931 *stale_config.write() = FaultConfig::default();
1932
1933 deterministic::Runner::from(checkpoint).start(|context| async move {
1934 assert!(context.open("fault_config", b"blob").await.is_err());
1935 });
1936 }
1937
1938 #[test]
1939 fn test_recover_retained_successful_resize() {
1940 let retained_resize = [u64::MAX, 0];
1941 let cfg = deterministic::Config::default()
1942 .with_rng(ScriptedRng::new(retained_resize))
1943 .with_storage_fault_config(FaultConfig::default().resize(ResizeConfig {
1944 failure_rate: probability!(0.5),
1945 partial_rate: probability!(0.0),
1946 }));
1947 let (_, checkpoint) =
1948 deterministic::Runner::new(cfg).start_and_recover(|context| async move {
1949 let (blob, _) = context.open("crash_resize", b"blob").await.unwrap();
1950 blob.write_at(0, b"abcdefgh", WriteOptions::SYNC)
1951 .await
1952 .unwrap();
1953 blob.resize(3).await.unwrap();
1954 });
1955
1956 deterministic::Runner::from(checkpoint).start(|context| async move {
1957 let (blob, len) = context.open("crash_resize", b"blob").await.unwrap();
1958 assert_eq!(len, 3);
1959 assert_eq!(
1960 blob.read_at(0, 3, ReadOptions::default())
1961 .await
1962 .unwrap()
1963 .coalesce(),
1964 b"abc"
1965 );
1966 });
1967 }
1968
1969 #[test]
1970 fn test_recover_random_crash_writes_is_seeded_and_epoch_scoped() {
1971 const STABLE_LEN: usize = 32;
1972 const PENDING_LEN: usize = 256;
1973
1974 fn run(seed: u64) -> (Vec<u8>, Digest) {
1975 let cfg = deterministic::Config::default()
1976 .with_seed(seed)
1977 .with_storage_fault_config(FaultConfig::default().write(WriteConfig {
1978 failure_rate: probability!(0.0),
1979 retention_rate: probability!(0.5),
1980 mode: PartialWriteMode::Subset,
1981 }));
1982 let (_, checkpoint) =
1983 deterministic::Runner::new(cfg).start_and_recover(|context| async move {
1984 let (blob, _) = context.open("crash_epoch", b"blob").await.unwrap();
1985 blob.write_at(0, vec![0xA5; STABLE_LEN], WriteOptions::default())
1986 .await
1987 .unwrap();
1988 blob.sync().await.unwrap();
1989 blob.write_at(
1990 STABLE_LEN as u64,
1991 vec![0x5A; PENDING_LEN],
1992 WriteOptions::default(),
1993 )
1994 .await
1995 .unwrap();
1996 });
1997
1998 deterministic::Runner::from(checkpoint).start(|context| async move {
1999 let (blob, len) = context.open("crash_epoch", b"blob").await.unwrap();
2000 let mut bytes = vec![0; STABLE_LEN + PENDING_LEN];
2001 let len = usize::try_from(len).unwrap();
2002 let durable = blob
2003 .read_at(0, len, ReadOptions::default())
2004 .await
2005 .unwrap()
2006 .coalesce();
2007 bytes[..durable.len()].copy_from_slice(durable.as_ref());
2008 (bytes, context.storage_audit())
2009 })
2010 }
2011
2012 let first = run(12345);
2013 let second = run(12345);
2014 let different = run(54321);
2015 assert_eq!(first, second);
2016 assert_ne!(first.0, different.0);
2017 assert!(first.0[..STABLE_LEN].iter().all(|&byte| byte == 0xA5));
2018 assert!(first.0[STABLE_LEN..].contains(&0));
2019 assert!(first.0[STABLE_LEN..].contains(&0x5A));
2020 }
2021
2022 #[test]
2023 fn test_recover_dns_mappings_persist() {
2024 let executor = deterministic::Runner::default();
2026 let host = "example.com";
2027 let addrs = vec![
2028 IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 1)),
2029 IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 2)),
2030 ];
2031
2032 let (state, checkpoint) = executor.start_and_recover({
2034 let addrs = addrs.clone();
2035 |context| async move {
2036 context.resolver_register(host, Some(addrs));
2037 context.auditor().state()
2038 }
2039 });
2040
2041 assert_eq!(state, checkpoint.auditor.state());
2043
2044 let executor = Runner::from(checkpoint);
2046 executor.start(move |context| async move {
2047 let resolved = context.resolve(host).await.unwrap();
2048 assert_eq!(resolved, addrs);
2049 });
2050 }
2051
2052 #[test]
2053 fn test_recover_time_persists() {
2054 let executor = deterministic::Runner::default();
2056 let duration_to_sleep = Duration::from_secs(10);
2057
2058 let (time_before_recovery, checkpoint) = executor.start_and_recover(|context| async move {
2060 context.sleep(duration_to_sleep).await;
2061 context.current()
2062 });
2063
2064 assert_eq!(
2066 time_before_recovery.duration_since(UNIX_EPOCH).unwrap(),
2067 duration_to_sleep
2068 );
2069
2070 let executor2 = Runner::from(checkpoint);
2072 executor2.start(move |context| async move {
2073 assert_eq!(context.current(), time_before_recovery);
2074
2075 context.sleep(duration_to_sleep).await;
2077 assert_eq!(
2078 context.current().duration_since(UNIX_EPOCH).unwrap(),
2079 duration_to_sleep * 2
2080 );
2081 });
2082 }
2083
2084 #[test]
2085 #[should_panic(expected = "executor still has weak references")]
2086 fn test_context_return() {
2087 let executor = deterministic::Runner::default();
2089
2090 let context = executor.start(|context| async move {
2092 context
2094 });
2095
2096 drop(context);
2098 }
2099
2100 #[test]
2101 fn test_default_time_zero() {
2102 let executor = deterministic::Runner::default();
2104
2105 executor.start(|context| async move {
2106 assert_eq!(
2108 context.current().duration_since(UNIX_EPOCH).unwrap(),
2109 Duration::ZERO
2110 );
2111 });
2112 }
2113
2114 #[test]
2115 fn test_start_time() {
2116 let executor_default = deterministic::Runner::default();
2118 executor_default.start(|context| async move {
2119 assert_eq!(context.current(), UNIX_EPOCH);
2120 });
2121
2122 let start_time = UNIX_EPOCH + Duration::from_secs(100);
2124 let cfg = Config::default().with_start_time(start_time);
2125 let executor = deterministic::Runner::new(cfg);
2126
2127 executor.start(move |context| async move {
2128 assert_eq!(context.current(), start_time);
2130 });
2131 }
2132
2133 #[test]
2134 #[should_panic(expected = "start time must be greater than or equal to unix epoch")]
2135 fn test_bad_start_time() {
2136 let cfg = Config::default().with_start_time(UNIX_EPOCH - Duration::from_secs(1));
2137 deterministic::Runner::new(cfg);
2138 }
2139
2140 #[cfg(not(feature = "external"))]
2141 #[test]
2142 #[should_panic(expected = "runtime stalled")]
2143 fn test_stall() {
2144 let executor = deterministic::Runner::default();
2146
2147 executor.start(|_| async move {
2149 pending::<()>().await;
2150 });
2151 }
2152
2153 #[cfg(not(feature = "external"))]
2154 #[test]
2155 #[should_panic(expected = "runtime stalled")]
2156 fn test_external_simulated() {
2157 let executor = deterministic::Runner::default();
2159
2160 let (tx, rx) = oneshot::channel();
2162 std::thread::spawn(move || {
2163 std::thread::sleep(Duration::from_secs(1));
2164 tx.send(()).unwrap();
2165 });
2166
2167 executor.start(|_| async move {
2169 rx.await.unwrap();
2170 });
2171 }
2172
2173 #[cfg(feature = "external")]
2174 #[test]
2175 fn test_external_realtime() {
2176 let executor = deterministic::Runner::default();
2178
2179 let (tx, rx) = oneshot::channel();
2181 std::thread::spawn(move || {
2182 std::thread::sleep(Duration::from_secs(1));
2183 tx.send(()).unwrap();
2184 });
2185
2186 executor.start(|_| async move {
2188 rx.await.unwrap();
2189 });
2190 }
2191
2192 #[cfg(feature = "external")]
2193 #[test]
2194 fn test_external_realtime_variable() {
2195 let executor = deterministic::Runner::default();
2197
2198 executor.start(|context| async move {
2200 let start_real = SystemTime::now();
2202 let start_sim = context.current();
2203 let (first_tx, first_rx) = oneshot::channel();
2204 let (second_tx, second_rx) = oneshot::channel();
2205 let (results_tx, mut results_rx) = mpsc::channel(2);
2206
2207 let first_wait = Duration::from_secs(1);
2209 std::thread::spawn(move || {
2210 std::thread::sleep(first_wait);
2211 first_tx.send(()).unwrap();
2212 });
2213
2214 std::thread::spawn(move || {
2216 std::thread::sleep(Duration::ZERO);
2217 second_tx.send(()).unwrap();
2218 });
2219
2220 let first = context.child("sample_before_send").spawn({
2222 let results_tx = results_tx.clone();
2223 move |context| async move {
2224 first_rx.pace(&context, Duration::ZERO).await.unwrap();
2225 let elapsed_real = SystemTime::now().duration_since(start_real).unwrap();
2226 assert!(elapsed_real > first_wait);
2227 let elapsed_sim = context.current().duration_since(start_sim).unwrap();
2228 assert!(elapsed_sim < first_wait);
2229 results_tx.send(1).await.unwrap();
2230 }
2231 });
2232
2233 let second = context
2235 .child("sample_after_send")
2236 .spawn(move |context| async move {
2237 second_rx.pace(&context, first_wait).await.unwrap();
2238 let elapsed_real = SystemTime::now().duration_since(start_real).unwrap();
2239 assert!(elapsed_real >= first_wait);
2240 let elapsed_sim = context.current().duration_since(start_sim).unwrap();
2241 assert!(elapsed_sim >= first_wait);
2242 results_tx.send(2).await.unwrap();
2243 });
2244
2245 second.await.unwrap();
2247 first.await.unwrap();
2248
2249 let mut results = Vec::new();
2251 for _ in 0..2 {
2252 results.push(results_rx.recv().await.unwrap());
2253 }
2254 assert_eq!(results, vec![1, 2]);
2255 });
2256 }
2257
2258 #[cfg(not(feature = "external"))]
2259 #[test]
2260 fn test_simulated_skip() {
2261 let executor = deterministic::Runner::default();
2263
2264 executor.start(|context| async move {
2266 context.sleep(Duration::from_secs(1)).await;
2267
2268 let metrics = context.encode();
2270 let iterations = metrics
2271 .lines()
2272 .find_map(|line| {
2273 line.strip_prefix("runtime_iterations_total ")
2274 .and_then(|value| value.trim().parse::<u64>().ok())
2275 })
2276 .expect("missing runtime_iterations_total metric");
2277 assert!(iterations < 10);
2278 });
2279 }
2280
2281 #[cfg(feature = "external")]
2282 #[test]
2283 fn test_realtime_no_skip() {
2284 let executor = deterministic::Runner::default();
2286
2287 executor.start(|context| async move {
2289 context.sleep(Duration::from_secs(1)).await;
2290
2291 let metrics = context.encode();
2293 let iterations = metrics
2294 .lines()
2295 .find_map(|line| {
2296 line.strip_prefix("runtime_iterations_total ")
2297 .and_then(|value| value.trim().parse::<u64>().ok())
2298 })
2299 .expect("missing runtime_iterations_total metric");
2300 assert!(iterations > 500);
2301 });
2302 }
2303
2304 #[test]
2305 #[should_panic(expected = "label must start with [a-zA-Z]")]
2306 fn test_metrics_label_empty() {
2307 let executor = deterministic::Runner::default();
2308 executor.start(|context| async move {
2309 let _ = context.child("");
2310 });
2311 }
2312
2313 #[test]
2314 #[should_panic(expected = "label must start with [a-zA-Z]")]
2315 fn test_metrics_label_invalid_first_char() {
2316 let executor = deterministic::Runner::default();
2317 executor.start(|context| async move {
2318 let _ = context.child("1invalid");
2319 });
2320 }
2321
2322 #[test]
2323 #[should_panic(expected = "label must only contain [a-zA-Z0-9_]")]
2324 fn test_metrics_label_invalid_char() {
2325 let executor = deterministic::Runner::default();
2326 executor.start(|context| async move {
2327 let _ = context.child("invalid-label");
2328 });
2329 }
2330
2331 #[test]
2332 #[should_panic(expected = "using runtime label is not allowed")]
2333 fn test_metrics_label_reserved_prefix() {
2334 let executor = deterministic::Runner::default();
2335 executor.start(|context| async move {
2336 let _ = context.child(METRICS_PREFIX);
2337 });
2338 }
2339
2340 #[test]
2341 fn test_metrics_duplicate_attribute_overwrites() {
2342 let executor = deterministic::Runner::default();
2343 executor.start(|context| async move {
2344 let context = context
2345 .child("test")
2346 .with_attribute("epoch", "old")
2347 .with_attribute("epoch", "new");
2348 assert_eq!(
2349 context.name().attributes,
2350 vec![("epoch".to_string(), "new".to_string())]
2351 );
2352 });
2353 }
2354
2355 #[test]
2356 fn test_storage_fault_injection_and_recovery() {
2357 let cfg = deterministic::Config::default().with_storage_fault_config(FaultConfig {
2359 sync_rate: Some(probability!(1.0)),
2360 ..Default::default()
2361 });
2362
2363 let (result, checkpoint) =
2364 deterministic::Runner::new(cfg).start_and_recover(|ctx| async move {
2365 let (blob, _) = ctx.open("test_fault", b"blob").await.unwrap();
2366 blob.write_at(0, b"data".to_vec(), WriteOptions::default())
2367 .await
2368 .unwrap();
2369 blob.sync().await });
2371
2372 assert!(result.is_err());
2374
2375 deterministic::Runner::from(checkpoint).start(|ctx| async move {
2377 *ctx.storage_fault_config().write() = FaultConfig::default();
2379
2380 let (blob, len) = ctx.open("test_fault", b"blob").await.unwrap();
2382 assert_eq!(len, 0, "unsynced data should be lost after recovery");
2383
2384 blob.write_at(0, b"recovered".to_vec(), WriteOptions::default())
2386 .await
2387 .unwrap();
2388 blob.sync()
2389 .await
2390 .expect("sync should succeed with faults disabled");
2391
2392 let read_buf = blob.read_at(0, 9, ReadOptions::default()).await.unwrap();
2394 assert_eq!(read_buf.coalesce(), b"recovered");
2395 });
2396 }
2397
2398 #[test]
2399 fn test_storage_fault_dynamic_config() {
2400 let executor = deterministic::Runner::default();
2401 executor.start(|ctx| async move {
2402 let (blob, _) = ctx.open("test_dynamic", b"blob").await.unwrap();
2403
2404 blob.write_at(0, b"initial".to_vec(), WriteOptions::default())
2406 .await
2407 .unwrap();
2408 blob.sync().await.expect("initial sync should succeed");
2409
2410 let storage_fault_cfg = ctx.storage_fault_config();
2412 storage_fault_cfg.write().sync_rate = Some(probability!(1.0));
2413
2414 blob.write_at(0, b"updated".to_vec(), WriteOptions::default())
2416 .await
2417 .unwrap();
2418 let result = blob.sync().await;
2419 assert!(result.is_err(), "sync should fail with faults enabled");
2420
2421 storage_fault_cfg.write().sync_rate = Some(probability!(0.0));
2423
2424 blob.sync()
2426 .await
2427 .expect("sync should succeed with faults disabled");
2428 });
2429 }
2430
2431 #[test]
2432 fn test_storage_fault_determinism() {
2433 fn run_with_seed(seed: u64) -> Vec<bool> {
2435 let cfg = deterministic::Config::default()
2436 .with_seed(seed)
2437 .with_storage_fault_config(FaultConfig {
2438 open_rate: Some(probability!(0.5)),
2439 ..Default::default()
2440 });
2441
2442 let runner = deterministic::Runner::new(cfg);
2443 runner.start(|ctx| async move {
2444 let mut results = Vec::new();
2445 for i in 0..20 {
2446 let name = format!("blob{i}");
2447 let result = ctx.open("test_determinism", name.as_bytes()).await;
2448 results.push(result.is_ok());
2449 }
2450 results
2451 })
2452 }
2453
2454 let results1 = run_with_seed(12345);
2455 let results2 = run_with_seed(12345);
2456 assert_eq!(
2457 results1, results2,
2458 "same seed should produce same failure pattern"
2459 );
2460
2461 let results3 = run_with_seed(99999);
2462 assert_ne!(
2463 results1, results3,
2464 "different seeds should produce different patterns"
2465 );
2466 }
2467
2468 #[test]
2469 fn test_storage_fault_determinism_multi_task() {
2470 fn run_with_seed(seed: u64) -> Vec<u32> {
2473 let cfg = deterministic::Config::default()
2474 .with_seed(seed)
2475 .with_storage_fault_config(FaultConfig {
2476 open_rate: Some(probability!(0.5)),
2477 write_rate: Some(WriteConfig {
2478 failure_rate: probability!(0.3),
2479 retention_rate: probability!(0.0),
2480 mode: PartialWriteMode::Prefix,
2481 }),
2482 sync_rate: Some(probability!(0.2)),
2483 ..Default::default()
2484 });
2485
2486 let runner = deterministic::Runner::new(cfg);
2487 runner.start(|ctx| async move {
2488 let mut handles = Vec::new();
2490 for i in 0..5 {
2491 let ctx = ctx.child("task");
2492 handles.push(ctx.spawn(move |ctx| async move {
2493 let mut successes = 0u32;
2494 for j in 0..4 {
2495 let name = format!("task{i}_blob{j}");
2496 if let Ok((blob, _)) = ctx.open("partition", name.as_bytes()).await {
2497 successes += 1;
2498 if blob
2499 .write_at(0, b"data".to_vec(), WriteOptions::default())
2500 .await
2501 .is_ok()
2502 {
2503 successes += 1;
2504 }
2505 if blob.sync().await.is_ok() {
2506 successes += 1;
2507 }
2508 }
2509 }
2510 successes
2511 }));
2512 }
2513
2514 let mut results = Vec::new();
2516 for handle in handles {
2517 results.push(handle.await.unwrap());
2518 }
2519 results
2520 })
2521 }
2522
2523 let results1 = run_with_seed(42);
2524 let results2 = run_with_seed(42);
2525 assert_eq!(
2526 results1, results2,
2527 "same seed should produce same multi-task pattern"
2528 );
2529
2530 let results3 = run_with_seed(99999);
2531 assert_ne!(
2532 results1, results3,
2533 "different seeds should produce different patterns"
2534 );
2535 }
2536
2537 #[test]
2538 fn test_resolver() {
2539 let executor = deterministic::Runner::default();
2540 executor.start(|context| async move {
2541 let ip1: IpAddr = "192.168.1.1".parse().unwrap();
2543 let ip2: IpAddr = "192.168.1.2".parse().unwrap();
2544 context.resolver_register("example.com", Some(vec![ip1, ip2]));
2545
2546 let addrs = context.resolve("example.com").await.unwrap();
2548 assert_eq!(addrs, vec![ip1, ip2]);
2549
2550 let result = context.resolve("unknown.com").await;
2552 assert!(matches!(result, Err(Error::ResolveFailed(_))));
2553
2554 context.resolver_register("example.com", None);
2556 let result = context.resolve("example.com").await;
2557 assert!(matches!(result, Err(Error::ResolveFailed(_))));
2558 });
2559 }
2560
2561 #[test]
2564 fn test_parallel_strategy_spawn_completes() {
2565 let executor = deterministic::Runner::default();
2566 executor.start(|context| async move {
2567 let strategy = context.child("pool").strategy(NZUsize!(2)).manual();
2568 assert_eq!(strategy.parallelism(), 2);
2569
2570 let output = strategy
2571 .spawn(2, |strategy| strategy.map_collect_vec(0..2, |i| i + 1))
2572 .await;
2573
2574 assert_eq!(output, vec![1, 2]);
2575 });
2576 }
2577
2578 #[test]
2582 fn test_strategies_reuse_pool_across_runners() {
2583 let executor = deterministic::Runner::default();
2584 executor.start(|context| async move {
2585 let first = context.child("pool_a").strategy(NZUsize!(1)).manual();
2586 assert_eq!(first.parallelism(), 1);
2587 assert_eq!(first.run(2, || "serial", || "parallel"), "serial");
2588 let output = first
2589 .spawn(2, |strategy| strategy.map_collect_vec(0..2, |i| i + 1))
2590 .now_or_never()
2591 .expect("single-threaded pool should run spawned work inline");
2592 assert_eq!(output, vec![1, 2]);
2593
2594 let second = context.child("pool_b").strategy(NZUsize!(3)).manual();
2595 assert_eq!(second.parallelism(), 3);
2596 assert_eq!(second.run(2, || "serial", || "parallel"), "parallel");
2597 let output = second
2598 .spawn(3, |strategy| strategy.map_collect_vec(0..3, |i| i + 1))
2599 .now_or_never()
2600 .expect("single-threaded pool should run spawned work inline");
2601 assert_eq!(output, vec![1, 2, 3]);
2602 });
2603
2604 let executor = deterministic::Runner::default();
2605 executor.start(|context| async move {
2606 let third = context.child("pool_c").strategy(NZUsize!(4)).manual();
2607 assert_eq!(third.parallelism(), 4);
2608 assert_eq!(third.run(2, || "serial", || "parallel"), "parallel");
2609 let output = third
2610 .spawn(4, |strategy| strategy.map_collect_vec(0..4, |i| i + 1))
2611 .now_or_never()
2612 .expect("single-threaded pool should run spawned work inline");
2613 assert_eq!(output, vec![1, 2, 3, 4]);
2614 });
2615 }
2616
2617 #[test]
2621 fn test_pool_survives_suspension() {
2622 let executor = deterministic::Runner::default();
2623 executor.start(|context| async move {
2624 let strategy = context.child("pool").strategy(NZUsize!(2)).manual();
2625 context.sleep(Duration::from_millis(10)).await;
2626
2627 let output = strategy
2628 .spawn(2, |strategy| strategy.map_collect_vec(0..2, |i| i + 1))
2629 .await;
2630 assert_eq!(output, vec![1, 2]);
2631
2632 context.sleep(Duration::from_millis(10)).await;
2633 let sum = strategy.fold(0..100u64, || 0u64, |acc, i| acc + i, |a, b| a + b);
2634 assert_eq!(sum, 4950);
2635 });
2636 }
2637}