1pub use crate::storage::faulty::{
46 Config as FaultConfig, PartialWriteMode, ResizeConfig, WriteConfig,
47};
48use crate::{
49 BlobVersion, Clock, Error, Execution, Handle, IoBufs, ListenerOf, METRICS_PREFIX, Name,
50 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::{Blob as AuditedBlob, Storage as AuditedStorage},
58 faulty::{Blob as FaultyBlob, Storage as FaultyStorage},
59 memory::{
60 Blob as MemBlob, Snapshot as MemStorageSnapshot, Storage as MemStorage,
61 open::{Blob as OpenBlob, Opens},
62 },
63 metered::{Blob as MeteredBlob, Storage as MeteredStorage},
64 },
65 telemetry::metrics::{
66 Counter, CounterFamily, GaugeFamily, Metric, Register, Registered, Registry, add_attribute,
67 raw, task::Label, validate_label,
68 },
69 utils::{
70 FactoryGuard, Panicker,
71 signal::{Signal, Stopper},
72 supervision::Tree,
73 },
74};
75#[cfg(feature = "external")]
76use crate::{Blocker, Pacer};
77use commonware_codec::Encode;
78use commonware_cryptography::{Crc32, Hasher};
79use commonware_formatting::hex;
80use commonware_macros::select;
81use commonware_parallel::{
82 Rayon, ThreadPool,
83 rayon::{ThreadPoolBuildError, ThreadPoolBuilder},
84};
85use commonware_utils::{
86 Cached, SystemTimeExt,
87 sync::{Mutex, RwLock},
88 time::SYSTEM_TIME_PRECISION,
89};
90#[cfg(feature = "external")]
91use futures::task::noop_waker;
92use futures::{
93 Future,
94 task::{ArcWake, AtomicWaker, waker},
95};
96use governor::clock::{Clock as GClock, ReasonablyRealtime};
97#[cfg(feature = "external")]
98use pin_project::pin_project;
99use rand::{CryptoRng, Rng, SeedableRng, TryCryptoRng, TryRng, prelude::SliceRandom, rngs::StdRng};
100use std::{
101 collections::{BTreeMap, BinaryHeap, HashMap},
102 convert::Infallible,
103 mem::{replace, take},
104 net::{IpAddr, SocketAddr},
105 num::NonZeroUsize,
106 panic::{AssertUnwindSafe, catch_unwind, resume_unwind},
107 pin::Pin,
108 sync::{Arc, Weak},
109 task::{self, Poll},
110 time::{Duration, SystemTime, UNIX_EPOCH},
111};
112use tracing::trace;
113
114#[derive(Debug)]
115struct Metrics {
116 iterations: Counter,
117 tasks_spawned: CounterFamily<Label>,
118 tasks_running: GaugeFamily<Label>,
119 task_polls: CounterFamily<Label>,
120}
121
122impl Metrics {
123 pub fn init(registry: &mut impl Register) -> Self {
124 Self {
125 iterations: registry.register(
126 "iterations",
127 "Total number of iterations",
128 raw::Counter::default(),
129 ),
130 tasks_spawned: registry.register(
131 "tasks_spawned",
132 "Total number of tasks spawned",
133 raw::Family::default(),
134 ),
135 tasks_running: registry.register(
136 "tasks_running",
137 "Number of tasks currently running",
138 raw::Family::default(),
139 ),
140 task_polls: registry.register(
141 "task_polls",
142 "Total number of task polls",
143 raw::Family::default(),
144 ),
145 }
146 }
147}
148
149type Digest = [u8; 4];
151
152#[derive(Default)]
154pub(crate) struct AuditHasher(Crc32);
155
156impl AuditHasher {
157 pub(crate) fn update(&mut self, value: impl AsRef<[u8]>) {
159 let value = value.as_ref();
160 self.0.update(&(value.len() as u64).to_be_bytes());
161 self.0.update(value);
162 }
163
164 pub(crate) fn update_bufs(&mut self, bufs: &IoBufs) {
169 self.0.update(&(bufs.len() as u64).to_be_bytes());
170 bufs.for_each_chunk(|chunk| {
171 self.0.update(chunk);
172 });
173 }
174
175 pub(crate) fn finalize(self) -> Digest {
177 self.0.finalize().1.0
178 }
179}
180
181pub struct Auditor {
183 digest: Mutex<Digest>,
184}
185
186impl Default for Auditor {
187 fn default() -> Self {
188 Self {
189 digest: Digest::default().into(),
190 }
191 }
192}
193
194impl Auditor {
195 pub(crate) fn event<F>(&self, label: &'static [u8], payload: F)
199 where
200 F: FnOnce(&mut AuditHasher),
201 {
202 let mut digest = self.digest.lock();
203
204 let mut hasher = AuditHasher::default();
205 hasher.update(digest.as_ref());
206 hasher.update(label);
207 payload(&mut hasher);
208
209 *digest = hasher.finalize();
210 }
211
212 pub fn state(&self) -> String {
217 let hash = self.digest.lock();
218 hex(hash.as_ref())
219 }
220}
221
222pub type BoxDynRng = Box<dyn CryptoRng + Send + 'static>;
224
225pub struct Config {
227 rng: BoxDynRng,
229
230 cycle: Duration,
233
234 start_time: SystemTime,
236
237 timeout: Option<Duration>,
239
240 catch_panics: bool,
242
243 storage_fault_cfg: FaultConfig,
246}
247
248impl Config {
249 pub fn new() -> Self {
251 Self {
252 rng: Box::new(StdRng::seed_from_u64(42)),
253 cycle: Duration::from_millis(1),
254 start_time: UNIX_EPOCH,
255 timeout: None,
256 catch_panics: false,
257 storage_fault_cfg: FaultConfig::default(),
258 }
259 }
260
261 pub fn with_seed(self, seed: u64) -> Self {
264 let rng: BoxDynRng = Box::new(StdRng::seed_from_u64(seed));
265 self.with_rng(rng)
266 }
267
268 pub fn with_rng(mut self, rng: impl Into<BoxDynRng>) -> Self {
274 self.rng = rng.into();
275 self
276 }
277
278 pub const fn with_cycle(mut self, cycle: Duration) -> Self {
280 self.cycle = cycle;
281 self
282 }
283 pub const fn with_start_time(mut self, start_time: SystemTime) -> Self {
285 self.start_time = start_time;
286 self
287 }
288 pub const fn with_timeout(mut self, timeout: Option<Duration>) -> Self {
290 self.timeout = timeout;
291 self
292 }
293 pub const fn with_catch_panics(mut self, catch_panics: bool) -> Self {
295 self.catch_panics = catch_panics;
296 self
297 }
298
299 pub const fn with_storage_fault_config(mut self, faults: FaultConfig) -> Self {
305 self.storage_fault_cfg = faults;
306 self
307 }
308
309 pub const fn cycle(&self) -> Duration {
312 self.cycle
313 }
314 pub const fn start_time(&self) -> SystemTime {
316 self.start_time
317 }
318 pub const fn timeout(&self) -> Option<Duration> {
320 self.timeout
321 }
322 pub const fn catch_panics(&self) -> bool {
324 self.catch_panics
325 }
326
327 pub fn assert(&self) {
329 assert!(
330 self.cycle != Duration::default() || self.timeout.is_none(),
331 "cycle duration must be non-zero when timeout is set",
332 );
333 assert!(
334 self.cycle >= SYSTEM_TIME_PRECISION,
335 "cycle duration must be greater than or equal to system time precision"
336 );
337 assert!(
338 self.start_time >= UNIX_EPOCH,
339 "start time must be greater than or equal to unix epoch"
340 );
341 }
342}
343
344impl Default for Config {
345 fn default() -> Self {
346 Self::new()
347 }
348}
349
350pub struct Executor {
352 registry: Registry,
353 cycle: Duration,
354 deadline: Option<SystemTime>,
355 metrics: Arc<Metrics>,
356 auditor: Arc<Auditor>,
357 rng: Arc<Mutex<BoxDynRng>>,
358 time: Mutex<SystemTime>,
359 tasks: Arc<Tasks>,
360 sleeping: Mutex<BinaryHeap<Alarm>>,
361 shutdown: Mutex<Stopper>,
362 panicker: Panicker,
363 dns: Mutex<HashMap<String, Vec<IpAddr>>>,
364}
365
366impl Executor {
367 fn advance_time(&self) -> SystemTime {
372 #[cfg(feature = "external")]
373 std::thread::sleep(self.cycle);
374
375 let mut time = self.time.lock();
376 *time = time
377 .checked_add(self.cycle)
378 .expect("executor time overflowed");
379 let now = *time;
380 trace!(now = now.epoch_millis(), "time advanced");
381 now
382 }
383
384 fn assert_deadline(&self, current: SystemTime) {
386 if self.deadline.is_some_and(|deadline| current >= deadline) {
387 panic!("runtime timeout");
388 }
389 }
390
391 fn skip_idle_time(&self, current: SystemTime) -> SystemTime {
396 if cfg!(feature = "external") || self.tasks.ready() != 0 {
397 return current;
398 }
399
400 let mut skip_until = None;
401 {
402 let sleeping = self.sleeping.lock();
403 if let Some(next) = sleeping.peek()
404 && next.time > current
405 {
406 skip_until = Some(next.time);
407 }
408 }
409
410 skip_until.map_or(current, |deadline| {
411 let mut time = self.time.lock();
412 *time = deadline;
413 let now = *time;
414 trace!(now = now.epoch_millis(), "time skipped");
415 now
416 })
417 }
418
419 fn wake_ready_sleepers(&self, current: SystemTime) {
421 let mut sleeping = self.sleeping.lock();
422 while let Some(next) = sleeping.peek() {
423 if next.time <= current {
424 let sleeper = sleeping.pop().unwrap();
425 sleeper.waker.wake();
426 } else {
427 break;
428 }
429 }
430 }
431
432 fn wake_until_progress(&self, mut current: SystemTime) {
439 loop {
440 current = self.skip_idle_time(current);
443 self.assert_deadline(current);
444 self.wake_ready_sleepers(current);
445
446 if cfg!(feature = "external") || self.tasks.ready() != 0 {
449 return;
450 }
451 if self.sleeping.lock().is_empty() {
452 panic!("runtime stalled");
453 }
454 }
455 }
456}
457
458pub struct Checkpoint {
462 cycle: Duration,
463 deadline: Option<SystemTime>,
464 auditor: Arc<Auditor>,
465 rng: Arc<Mutex<BoxDynRng>>,
466 time: Mutex<SystemTime>,
467 storage: MemStorageSnapshot,
468 storage_fault_cfg: FaultConfig,
469 dns: Mutex<HashMap<String, Vec<IpAddr>>>,
470 catch_panics: bool,
471}
472
473impl Checkpoint {
474 pub fn auditor(&self) -> Arc<Auditor> {
476 self.auditor.clone()
477 }
478}
479
480enum State {
481 Config(Config),
482 Checkpoint(Checkpoint),
483}
484
485pub struct Runner {
487 state: State,
488}
489
490impl From<Config> for Runner {
491 fn from(cfg: Config) -> Self {
492 Self::new(cfg)
493 }
494}
495
496impl From<Checkpoint> for Runner {
497 fn from(checkpoint: Checkpoint) -> Self {
498 Self {
499 state: State::Checkpoint(checkpoint),
500 }
501 }
502}
503
504impl Runner {
505 pub fn new(cfg: Config) -> Self {
507 cfg.assert();
509 Self {
510 state: State::Config(cfg),
511 }
512 }
513
514 pub fn seeded(seed: u64) -> Self {
517 Self::new(Config::default().with_seed(seed))
518 }
519
520 pub fn timed(timeout: Duration) -> Self {
523 let cfg = Config {
524 timeout: Some(timeout),
525 ..Config::default()
526 };
527 Self::new(cfg)
528 }
529
530 pub fn start_and_recover<F, Fut>(self, f: F) -> (Fut::Output, Checkpoint)
533 where
534 F: FnOnce(Context) -> Fut,
535 Fut: Future,
536 {
537 let (context, executor, panicked) = match self.state {
539 State::Config(config) => Context::new(config),
540 State::Checkpoint(checkpoint) => Context::recover(checkpoint),
541 };
542
543 let storage = context.storage.clone();
545 let root = Box::pin(panicked.interrupt(f(context)));
546
547 Self::run(executor, storage, root)
549 }
550
551 fn run<TOutput>(
564 executor: Arc<Executor>,
565 storage: Arc<Storage>,
566 mut root: Pin<Box<dyn Future<Output = TOutput> + '_>>,
567 ) -> (TOutput, Checkpoint) {
568 Tasks::register_root(&executor.tasks);
570
571 let result = catch_unwind(AssertUnwindSafe(|| {
574 loop {
575 let current = *executor.time.lock();
577 executor.assert_deadline(current);
578
579 let mut queue = executor.tasks.drain();
581
582 if queue.len() > 1 {
584 let mut rng = executor.rng.lock();
585 queue.shuffle(&mut *rng);
586 }
587
588 trace!(
594 iter = executor.metrics.iterations.get(),
595 tasks = queue.len(),
596 "starting loop"
597 );
598 let mut output = None;
599 for id in queue {
600 let Some(task) = executor.tasks.get(id) else {
602 trace!(id, "skipping missing task");
603 continue;
604 };
605
606 executor.auditor.event(b"process_task", |hasher| {
608 hasher.update(task.id.to_be_bytes());
609 hasher.update(task.label.name());
610 });
611 executor.metrics.task_polls.get_or_create(&task.label).inc();
612 trace!(id, "processing task");
613
614 let waker = waker(Arc::new(TaskWaker {
616 id,
617 tasks: Arc::downgrade(&executor.tasks),
618 }));
619 let mut cx = task::Context::from_waker(&waker);
620
621 match &task.mode {
623 Mode::Root => {
624 if let Poll::Ready(result) = root.as_mut().poll(&mut cx) {
626 trace!(id, "root task is complete");
627 output = Some(result);
628 break;
629 }
630 }
631 Mode::Work(future) => {
632 let mut fut_opt = future.lock();
636 let fut = fut_opt.as_mut().expect("polled task must have a future");
637
638 if fut.as_mut().poll(&mut cx).is_ready() {
640 trace!(id, "task is complete");
641
642 executor.tasks.remove(id);
644 *fut_opt = None;
645 continue;
646 }
647 }
648 }
649
650 trace!(id, "task is still pending");
652 }
653
654 if let Some(output) = output {
656 break output;
657 }
658
659 let current = executor.advance_time();
661 executor.wake_until_progress(current);
662
663 executor.metrics.iterations.inc();
665 }
666 }));
667
668 executor.sleeping.lock().clear(); let tasks = executor.tasks.clear();
676 for task in tasks {
677 let Mode::Work(future) = &task.mode else {
678 continue;
679 };
680 *future.lock() = None;
681 }
682
683 drop(root);
687
688 storage
690 .inner()
691 .inner()
692 .crash()
693 .expect("retaining successful unsynced writes at crash should succeed");
694 let storage_fault_cfg = storage.inner().inner().config().read().clone();
695 let storage = storage.inner().inner().inner().take_snapshot();
696
697 assert!(
700 Arc::weak_count(&executor) == 0,
701 "executor still has weak references"
702 );
703
704 let output = match result {
706 Ok(output) => output,
707 Err(payload) => resume_unwind(payload),
708 };
709
710 let executor = Arc::into_inner(executor).expect("executor still has strong references");
712
713 let checkpoint = Checkpoint {
715 cycle: executor.cycle,
716 deadline: executor.deadline,
717 auditor: executor.auditor,
718 rng: executor.rng,
719 time: executor.time,
720 storage,
721 storage_fault_cfg,
722 dns: executor.dns,
723 catch_panics: executor.panicker.catch(),
724 };
725
726 (output, checkpoint)
727 }
728}
729
730impl Default for Runner {
731 fn default() -> Self {
732 Self::new(Config::default())
733 }
734}
735
736impl crate::Runner for Runner {
737 type Context = Context;
738
739 fn start<F, Fut>(self, f: F) -> Fut::Output
740 where
741 F: FnOnce(Self::Context) -> Fut,
742 Fut: Future,
743 {
744 let (output, _) = self.start_and_recover(f);
745 output
746 }
747}
748
749enum Mode {
751 Root,
752 Work(Mutex<Option<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>>),
753}
754
755struct Task {
757 id: u128,
758 label: Label,
759
760 mode: Mode,
761}
762
763struct TaskWaker {
765 id: u128,
766
767 tasks: Weak<Tasks>,
768}
769
770impl ArcWake for TaskWaker {
771 fn wake_by_ref(arc_self: &Arc<Self>) {
772 if let Some(tasks) = arc_self.tasks.upgrade() {
777 tasks.queue(arc_self.id);
778 }
779 }
780}
781
782struct Tasks {
784 counter: Mutex<u128>,
786 ready: Mutex<Vec<u128>>,
788 running: Mutex<BTreeMap<u128, Arc<Task>>>,
790}
791
792impl Tasks {
793 const fn new() -> Self {
795 Self {
796 counter: Mutex::new(0),
797 ready: Mutex::new(Vec::new()),
798 running: Mutex::new(BTreeMap::new()),
799 }
800 }
801
802 fn increment(&self) -> u128 {
804 let mut counter = self.counter.lock();
805 let old = *counter;
806 *counter = counter.checked_add(1).expect("task counter overflow");
807 old
808 }
809
810 fn register_root(arc_self: &Arc<Self>) {
814 let id = arc_self.increment();
815 let task = Arc::new(Task {
816 id,
817 label: Label::root(),
818 mode: Mode::Root,
819 });
820 arc_self.register(id, task);
821 }
822
823 fn register_work(
825 arc_self: &Arc<Self>,
826 label: Label,
827 future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
828 ) {
829 let id = arc_self.increment();
830 let task = Arc::new(Task {
831 id,
832 label,
833 mode: Mode::Work(Mutex::new(Some(future))),
834 });
835 arc_self.register(id, task);
836 }
837
838 fn register(&self, id: u128, task: Arc<Task>) {
840 self.running.lock().insert(id, task);
842
843 self.queue(id);
845 }
846
847 fn queue(&self, id: u128) {
849 let mut ready = self.ready.lock();
850 ready.push(id);
851 }
852
853 fn drain(&self) -> Vec<u128> {
855 let mut queue = self.ready.lock();
856 let len = queue.len();
857 replace(&mut *queue, Vec::with_capacity(len))
858 }
859
860 fn ready(&self) -> usize {
862 self.ready.lock().len()
863 }
864
865 fn get(&self, id: u128) -> Option<Arc<Task>> {
870 let running = self.running.lock();
871 running.get(&id).cloned()
872 }
873
874 fn remove(&self, id: u128) {
876 self.running.lock().remove(&id);
877 }
878
879 fn clear(&self) -> Vec<Arc<Task>> {
881 self.ready.lock().clear();
883
884 let running: BTreeMap<u128, Arc<Task>> = {
886 let mut running = self.running.lock();
887 take(&mut *running)
888 };
889 running.into_values().collect()
890 }
891}
892
893type Network = MeteredNetwork<AuditedNetwork<DeterministicNetwork>>;
894type Storage = MeteredStorage<AuditedStorage<FaultyStorage<MemStorage>>>;
895
896pub type Blob = OpenBlob<MeteredBlob<AuditedBlob<FaultyBlob<MemBlob>>>>;
898
899fn build_storage(
900 inner: MemStorage,
901 rng: Arc<Mutex<BoxDynRng>>,
902 faults: FaultConfig,
903 auditor: Arc<Auditor>,
904 registry: &mut impl Register,
905) -> Storage {
906 MeteredStorage::new(
907 AuditedStorage::new(
908 FaultyStorage::new(inner, rng, Arc::new(RwLock::new(faults))),
909 auditor,
910 ),
911 registry,
912 )
913}
914
915pub struct Context {
919 name: String,
920 attributes: Vec<(String, String)>,
921 executor: Weak<Executor>,
922 network: Arc<Network>,
923 storage: Arc<Storage>,
924 opens: Arc<Opens>,
925 tree: Arc<Tree>,
926 execution: Execution,
927}
928
929impl Context {
930 fn new(cfg: Config) -> (Self, Arc<Executor>, Panicked) {
931 let mut registry = Registry::new();
933 let mut runtime_registry = registry.sub_registry(METRICS_PREFIX);
934
935 let metrics = Arc::new(Metrics::init(&mut runtime_registry));
937 let start_time = cfg.start_time;
938 let deadline = cfg
939 .timeout
940 .map(|timeout| start_time.checked_add(timeout).expect("timeout overflowed"));
941 let auditor = Arc::new(Auditor::default());
942
943 let rng = Arc::new(Mutex::new(cfg.rng));
945
946 let storage = build_storage(
947 MemStorage::new(),
948 rng.clone(),
949 cfg.storage_fault_cfg,
950 auditor.clone(),
951 &mut runtime_registry,
952 );
953
954 let network = AuditedNetwork::new(DeterministicNetwork::default(), auditor.clone());
956 let network = MeteredNetwork::new(network, &mut runtime_registry);
957
958 let (panicker, panicked) = Panicker::new(cfg.catch_panics);
960
961 let executor = Arc::new(Executor {
962 registry,
963 cycle: cfg.cycle,
964 deadline,
965 metrics,
966 auditor,
967 rng,
968 time: Mutex::new(start_time),
969 tasks: Arc::new(Tasks::new()),
970 sleeping: Mutex::new(BinaryHeap::new()),
971 shutdown: Mutex::new(Stopper::default()),
972 panicker,
973 dns: Mutex::new(HashMap::new()),
974 });
975
976 (
977 Self {
978 name: String::new(),
979 attributes: Vec::new(),
980 executor: Arc::downgrade(&executor),
981 network: Arc::new(network),
982 storage: Arc::new(storage),
983 opens: Arc::default(),
984 tree: Tree::root(),
985 execution: Execution::default(),
986 },
987 executor,
988 panicked,
989 )
990 }
991
992 fn recover(checkpoint: Checkpoint) -> (Self, Arc<Executor>, Panicked) {
1005 let mut registry = Registry::new();
1007 let mut runtime_registry = registry.sub_registry(METRICS_PREFIX);
1008 let metrics = Arc::new(Metrics::init(&mut runtime_registry));
1009
1010 let network =
1012 AuditedNetwork::new(DeterministicNetwork::default(), checkpoint.auditor.clone());
1013 let network = MeteredNetwork::new(network, &mut runtime_registry);
1014
1015 let storage = build_storage(
1016 MemStorage::from_snapshot(checkpoint.storage),
1017 checkpoint.rng.clone(),
1018 checkpoint.storage_fault_cfg,
1019 checkpoint.auditor.clone(),
1020 &mut runtime_registry,
1021 );
1022
1023 let (panicker, panicked) = Panicker::new(checkpoint.catch_panics);
1025
1026 let executor = Arc::new(Executor {
1027 cycle: checkpoint.cycle,
1029 deadline: checkpoint.deadline,
1030 auditor: checkpoint.auditor,
1031 rng: checkpoint.rng,
1032 time: checkpoint.time,
1033 dns: checkpoint.dns,
1034
1035 registry,
1037 metrics,
1038 tasks: Arc::new(Tasks::new()),
1039 sleeping: Mutex::new(BinaryHeap::new()),
1040 shutdown: Mutex::new(Stopper::default()),
1041 panicker,
1042 });
1043 (
1044 Self {
1045 name: String::new(),
1046 attributes: Vec::new(),
1047 executor: Arc::downgrade(&executor),
1048 network: Arc::new(network),
1049 storage: Arc::new(storage),
1050 opens: Arc::default(),
1051 tree: Tree::root(),
1052 execution: Execution::default(),
1053 },
1054 executor,
1055 panicked,
1056 )
1057 }
1058
1059 fn executor(&self) -> Arc<Executor> {
1061 self.executor.upgrade().expect("executor already dropped")
1062 }
1063
1064 fn metrics(&self) -> Arc<Metrics> {
1066 self.executor().metrics.clone()
1067 }
1068
1069 pub fn auditor(&self) -> Arc<Auditor> {
1071 self.executor().auditor.clone()
1072 }
1073
1074 pub fn storage_audit(&self) -> Digest {
1076 self.storage.inner().inner().inner().audit()
1077 }
1078
1079 #[cfg(any(test, feature = "test-utils"))]
1082 pub fn logical_blob(&self, partition: &str, name: &[u8]) -> Option<Vec<u8>> {
1083 self.storage
1084 .inner()
1085 .inner()
1086 .inner()
1087 .logical_blob(partition, name)
1088 }
1089
1090 pub fn storage_fault_config(&self) -> Arc<RwLock<FaultConfig>> {
1096 self.storage.inner().inner().config()
1097 }
1098
1099 pub fn resolver_register(&self, host: impl Into<String>, addrs: Option<Vec<IpAddr>>) {
1104 let executor = self.executor();
1106 let host = host.into();
1107 executor.auditor.event(b"resolver_register", |hasher| {
1108 hasher.update(host.as_bytes());
1109 hasher.update(addrs.encode());
1110 });
1111
1112 let mut dns = executor.dns.lock();
1114 match addrs {
1115 Some(addrs) => {
1116 dns.insert(host, addrs);
1117 }
1118 None => {
1119 dns.remove(&host);
1120 }
1121 }
1122 }
1123}
1124
1125impl crate::Spawner for Context {
1126 fn dedicated(mut self) -> Self {
1127 self.execution = Execution::Dedicated;
1128 self
1129 }
1130
1131 fn shared(mut self, blocking: bool) -> Self {
1132 self.execution = Execution::Shared(blocking);
1133 self
1134 }
1135
1136 fn spawn<F, Fut, T>(mut self, f: F) -> Handle<T>
1137 where
1138 F: FnOnce(Self) -> Fut + Send + 'static,
1139 Fut: Future<Output = T> + Send + 'static,
1140 T: Send + 'static,
1141 {
1142 let (label, metric) = spawn_metrics!(self);
1144
1145 let parent = Arc::clone(&self.tree);
1147 self.execution = Execution::default();
1148 let (child, aborted) = Tree::child(&parent);
1149 if aborted {
1150 return Handle::closed(metric);
1151 }
1152 self.tree = child;
1153
1154 let guard = FactoryGuard::new(&parent, metric);
1156 let executor = self.executor();
1157 let future = f(self);
1158 let (f, handle) = Handle::init(
1159 future,
1160 guard.disarm(),
1161 executor.panicker.clone(),
1162 Arc::clone(&parent),
1163 );
1164 Tasks::register_work(&executor.tasks, label, Box::pin(f));
1165
1166 handle
1167 }
1168
1169 async fn stop(self, value: i32, timeout: Option<Duration>) -> Result<(), Error> {
1170 let executor = self.executor();
1171 executor.auditor.event(b"stop", |hasher| {
1172 hasher.update(value.to_be_bytes());
1173 });
1174 let stop_resolved = {
1175 let mut shutdown = executor.shutdown.lock();
1176 shutdown.stop(value)
1177 };
1178
1179 let timeout_future = timeout.map_or_else(
1181 || futures::future::Either::Right(futures::future::pending()),
1182 |duration| futures::future::Either::Left(self.sleep(duration)),
1183 );
1184 select! {
1185 result = stop_resolved => {
1186 result.map_err(|_| Error::Closed)?;
1187 Ok(())
1188 },
1189 _ = timeout_future => Err(Error::Timeout),
1190 }
1191 }
1192
1193 fn stopped(&self) -> Signal {
1194 let executor = self.executor();
1195 executor.auditor.event(b"stopped", |_| {});
1196
1197 executor.shutdown.lock().stopped()
1198 }
1199}
1200
1201commonware_utils::thread_local_cache!(static THREAD_POOL: ThreadPool);
1204
1205fn shared_thread_pool() -> Result<ThreadPool, ThreadPoolBuildError> {
1210 let pool = Cached::take(
1211 &THREAD_POOL,
1212 || {
1213 ThreadPoolBuilder::new()
1214 .num_threads(1)
1215 .use_current_thread()
1216 .build()
1217 .map(Arc::new)
1218 },
1219 |_| Ok(()),
1220 )?;
1221 Ok(Arc::clone(&pool))
1222}
1223
1224impl crate::Strategizer for Context {
1234 fn strategy(&self, parallelism: NonZeroUsize) -> Rayon {
1235 Rayon::with_pool(
1236 shared_thread_pool().expect("failed to create deterministic Rayon thread pool"),
1237 )
1238 .with_parallelism(parallelism)
1239 }
1240}
1241
1242impl crate::Supervisor for Context {
1243 fn child(&self, label: &'static str) -> Self {
1244 let (tree, _) = Tree::child(&self.tree);
1245 Self {
1246 name: child_label(&self.name, label),
1247 attributes: self.attributes.clone(),
1248 executor: self.executor.clone(),
1249 network: self.network.clone(),
1250 storage: self.storage.clone(),
1251 opens: self.opens.clone(),
1252 tree,
1253 execution: Execution::default(),
1254 }
1255 }
1256
1257 fn with_attribute(mut self, key: &'static str, value: impl std::fmt::Display) -> Self {
1258 validate_label(key);
1260
1261 add_attribute(&mut self.attributes, key, value);
1263 self
1264 }
1265
1266 fn name(&self) -> Name {
1267 Name {
1268 label: self.name.clone(),
1269 attributes: self.attributes.clone(),
1270 }
1271 }
1272}
1273
1274impl crate::Metrics for Context {
1275 fn register<N: Into<String>, H: Into<String>, M: Metric>(
1276 &self,
1277 name: N,
1278 help: H,
1279 metric: M,
1280 ) -> Registered<M> {
1281 let name = name.into();
1282 let help = help.into();
1283 let executor = self.executor();
1284 executor.auditor.event(b"register", |hasher| {
1285 hasher.update(name.as_bytes());
1286 hasher.update(help.as_bytes());
1287 for (k, v) in &self.attributes {
1288 hasher.update(k.as_bytes());
1289 hasher.update(v.as_bytes());
1290 }
1291 });
1292 let metric = Arc::new(metric);
1293 executor.registry.register(
1294 prefixed_name(&self.name, &name),
1295 help,
1296 self.attributes.clone(),
1297 metric,
1298 )
1299 }
1300
1301 fn encode(&self) -> String {
1302 let executor = self.executor();
1303 executor.auditor.event(b"encode", |_| {});
1304 executor.registry.encode()
1305 }
1306}
1307
1308struct Sleeper {
1309 executor: Weak<Executor>,
1310 time: SystemTime,
1311 waker: Option<Arc<AtomicWaker>>,
1312}
1313
1314impl Sleeper {
1315 fn executor(&self) -> Arc<Executor> {
1317 self.executor.upgrade().expect("executor already dropped")
1318 }
1319}
1320
1321struct Alarm {
1322 time: SystemTime,
1323 waker: Arc<AtomicWaker>,
1324}
1325
1326impl PartialEq for Alarm {
1327 fn eq(&self, other: &Self) -> bool {
1328 self.time.eq(&other.time)
1329 }
1330}
1331
1332impl Eq for Alarm {}
1333
1334impl PartialOrd for Alarm {
1335 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1336 Some(self.cmp(other))
1337 }
1338}
1339
1340impl Ord for Alarm {
1341 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1342 other.time.cmp(&self.time)
1344 }
1345}
1346
1347impl Future for Sleeper {
1348 type Output = ();
1349
1350 fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
1351 let executor = self.executor();
1352 {
1353 let current_time = *executor.time.lock();
1354 if current_time >= self.time {
1355 return Poll::Ready(());
1356 }
1357 }
1358 if let Some(waker) = &self.waker {
1359 waker.register(cx.waker());
1360 } else {
1361 let waker = Arc::new(AtomicWaker::new());
1362 waker.register(cx.waker());
1363 executor.sleeping.lock().push(Alarm {
1364 time: self.time,
1365 waker: waker.clone(),
1366 });
1367 self.waker = Some(waker);
1368 }
1369 Poll::Pending
1370 }
1371}
1372
1373impl Clock for Context {
1374 fn current(&self) -> SystemTime {
1375 *self.executor().time.lock()
1376 }
1377
1378 fn sleep(&self, duration: Duration) -> impl Future<Output = ()> + Send + 'static + use<> {
1379 let deadline = self
1380 .current()
1381 .checked_add(duration)
1382 .expect("overflow when setting wake time");
1383 self.sleep_until(deadline)
1384 }
1385
1386 fn sleep_until(
1387 &self,
1388 deadline: SystemTime,
1389 ) -> impl Future<Output = ()> + Send + 'static + use<> {
1390 Sleeper {
1391 executor: self.executor.clone(),
1392
1393 time: deadline,
1394 waker: None,
1395 }
1396 }
1397}
1398
1399#[cfg(feature = "external")]
1403#[pin_project]
1404struct Waiter<F: Future> {
1405 sleeper: Sleeper,
1406 #[pin]
1407 future: F,
1408 ready: Option<F::Output>,
1409 started: bool,
1410}
1411
1412#[cfg(feature = "external")]
1413impl<F> Future for Waiter<F>
1414where
1415 F: Future + Send,
1416{
1417 type Output = F::Output;
1418
1419 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
1420 let mut this = self.project();
1421
1422 if !*this.started {
1426 *this.started = true;
1427 let waker = noop_waker();
1428 let mut cx_noop = task::Context::from_waker(&waker);
1429 if let Poll::Ready(value) = this.future.as_mut().poll(&mut cx_noop) {
1430 *this.ready = Some(value);
1431 }
1432 }
1433
1434 std::task::ready!(Pin::new(this.sleeper).poll(cx));
1436
1437 if let Some(value) = this.ready.take() {
1439 return Poll::Ready(value);
1440 }
1441
1442 let blocker = Blocker::new();
1445 loop {
1446 let waker = waker(blocker.clone());
1447 let mut cx_block = task::Context::from_waker(&waker);
1448 match this.future.as_mut().poll(&mut cx_block) {
1449 Poll::Ready(value) => {
1450 break Poll::Ready(value);
1451 }
1452 Poll::Pending => blocker.wait(),
1453 }
1454 }
1455 }
1456}
1457
1458#[cfg(feature = "external")]
1459impl Pacer for Context {
1460 fn pace<'a, F, T>(&'a self, latency: Duration, future: F) -> impl Future<Output = T> + Send + 'a
1461 where
1462 F: Future<Output = T> + Send + 'a,
1463 T: Send + 'a,
1464 {
1465 let target = self
1467 .executor()
1468 .time
1469 .lock()
1470 .checked_add(latency)
1471 .expect("overflow when setting wake time");
1472
1473 Waiter {
1474 sleeper: Sleeper {
1475 executor: self.executor.clone(),
1476 time: target,
1477 waker: None,
1478 },
1479 future,
1480 ready: None,
1481 started: false,
1482 }
1483 }
1484}
1485
1486impl GClock for Context {
1487 type Instant = SystemTime;
1488
1489 fn now(&self) -> Self::Instant {
1490 self.current()
1491 }
1492}
1493
1494impl ReasonablyRealtime for Context {}
1495
1496impl crate::Network for Context {
1497 type Listener = ListenerOf<Network>;
1498
1499 async fn bind(&self, socket: SocketAddr) -> Result<Self::Listener, Error> {
1500 self.network.bind(socket).await
1501 }
1502
1503 async fn dial(
1504 &self,
1505 socket: SocketAddr,
1506 ) -> Result<(crate::SinkOf<Self>, crate::StreamOf<Self>), Error> {
1507 self.network.dial(socket).await
1508 }
1509}
1510
1511impl crate::Resolver for Context {
1512 async fn resolve(&self, host: &str) -> Result<Vec<IpAddr>, Error> {
1513 let executor = self.executor();
1515 let dns = executor.dns.lock();
1516 let result = dns.get(host).cloned();
1517 drop(dns);
1518
1519 executor.auditor.event(b"resolve", |hasher| {
1521 hasher.update(host.as_bytes());
1522 hasher.update(result.encode());
1523 });
1524 result.ok_or_else(|| Error::ResolveFailed(host.to_string()))
1525 }
1526}
1527
1528impl TryRng for Context {
1529 type Error = Infallible;
1530
1531 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
1532 let executor = self.executor();
1533 executor.auditor.event(b"rand", |hasher| {
1534 hasher.update(b"next_u32");
1535 });
1536 let result = executor.rng.lock().next_u32();
1537 Ok(result)
1538 }
1539
1540 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
1541 let executor = self.executor();
1542 executor.auditor.event(b"rand", |hasher| {
1543 hasher.update(b"next_u64");
1544 });
1545 let result = executor.rng.lock().next_u64();
1546 Ok(result)
1547 }
1548
1549 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
1550 let executor = self.executor();
1551 executor.auditor.event(b"rand", |hasher| {
1552 hasher.update(b"fill_bytes");
1553 });
1554 executor.rng.lock().fill_bytes(dest);
1555 Ok(())
1556 }
1557}
1558
1559impl TryCryptoRng for Context {}
1560
1561impl crate::Storage for Context {
1562 type Blob = Blob;
1563
1564 async fn open_versioned(
1565 &self,
1566 partition: &str,
1567 name: &[u8],
1568 versions: std::ops::RangeInclusive<BlobVersion>,
1569 ) -> Result<(Self::Blob, u64, BlobVersion), Error> {
1570 let opened = self.opens.open(
1571 partition,
1572 name,
1573 self.storage.open_versioned(partition, name, versions),
1574 )?;
1575 let retired = self.storage.inner().inner().admit(partition, name);
1576 let opened = opened.finish();
1577 drop(retired);
1578 Ok(opened)
1579 }
1580
1581 async fn remove(&self, partition: &str, name: Option<&[u8]>) -> Result<(), Error> {
1582 let audited = self.storage.inner();
1583 let retired = self.opens.remove(
1584 partition,
1585 name,
1586 audited.remove_with(
1587 partition,
1588 name,
1589 audited.inner().remove_retired(partition, name),
1590 ),
1591 )?;
1592 drop(retired);
1593 Ok(())
1594 }
1595
1596 async fn scan(&self, partition: &str) -> Result<Vec<Vec<u8>>, Error> {
1597 self.storage.scan(partition).await
1598 }
1599}
1600
1601#[cfg(test)]
1602mod tests {
1603 use super::*;
1604 #[cfg(feature = "external")]
1605 use crate::FutureExt;
1606 use crate::{
1607 Blob, IoBuf, Metrics as _, ReadOptions, Resolver, Runner as _, Spawner as _, Storage,
1608 Strategizer, Supervisor as _, WriteOptions, deterministic, reschedule,
1609 };
1610 use bytes::Bytes;
1611 use commonware_macros::test_traced;
1612 use commonware_parallel::{Strategy, mocks::Explorer};
1613 #[cfg(feature = "external")]
1614 use commonware_utils::channel::mpsc;
1615 use commonware_utils::{NZUsize, ScriptedRng, channel::oneshot, probability};
1616 #[cfg(feature = "external")]
1617 use futures::StreamExt;
1618 #[cfg(not(feature = "external"))]
1619 use futures::future::pending;
1620 #[cfg(not(feature = "external"))]
1621 use futures::stream::StreamExt as _;
1622 use futures::{FutureExt as _, stream::FuturesUnordered};
1623 use std::{
1624 cell::Cell,
1625 rc::Rc,
1626 sync::atomic::{AtomicUsize, Ordering},
1627 };
1628
1629 #[rstest::rstest]
1630 #[case::open_named(true, true)]
1631 #[case::open_partition(true, false)]
1632 #[case::remove_named(false, true)]
1633 #[case::remove_partition(false, false)]
1634 fn test_logical_open_namespace_handoff(#[case] open_first: bool, #[case] named: bool) {
1635 Runner::default().start(|context| async move {
1636 let (seed, _) = context.open("partition", b"blob").await.unwrap();
1638 seed.write_at(0, b"saved", WriteOptions::SYNC)
1639 .await
1640 .unwrap();
1641 drop(seed);
1642
1643 let name = named.then_some(b"blob".as_slice());
1646 let worker_context = context.child("namespace");
1647 let competing_context = context.child("competing");
1648 let (entered, entering) = std::sync::mpsc::channel();
1649 let (release, released) = std::sync::mpsc::channel();
1650 let (observed, observing) = std::sync::mpsc::channel();
1651 let operation = move |context: Context, open| async move {
1652 if open {
1653 Some(context.open("partition", b"blob").await.unwrap())
1654 } else {
1655 context.remove("partition", name).await.unwrap();
1656 None
1657 }
1658 };
1659
1660 let (worker, competing) = std::thread::scope(move |scope| {
1663 let worker = scope.spawn(move || {
1664 worker_context.opens.pause_namespace(entered, released);
1665 operation(worker_context, open_first)
1666 .now_or_never()
1667 .unwrap()
1668 });
1669 entering.recv().unwrap();
1670 let competing = scope.spawn(move || {
1671 competing_context.opens.watch_registry(observed);
1672 operation(competing_context, !open_first)
1673 .now_or_never()
1674 .unwrap()
1675 });
1676
1677 let locked = observing.recv().unwrap();
1680 if locked {
1681 release.send(()).unwrap();
1682 }
1683 let competing = competing.join().unwrap();
1684 if !locked {
1685 release.send(()).unwrap();
1686 }
1687 (worker.join().unwrap(), competing)
1688 });
1689
1690 let (old, current) = if open_first {
1693 let (old, len) = worker.unwrap();
1694 assert_eq!(len, 5);
1695 assert_eq!(
1696 old.read_at(0, 5, ReadOptions::default())
1697 .await
1698 .unwrap()
1699 .coalesce(),
1700 b"saved"
1701 );
1702 let (current, len) = context.open("partition", b"blob").await.unwrap();
1703 assert_eq!(len, 0);
1704 (Some(old), current)
1705 } else {
1706 let (current, len) = competing.unwrap();
1707 assert_eq!(len, 0);
1708 (None, current)
1709 };
1710
1711 let current = Arc::new(current);
1713 let retained = current.clone();
1714 drop(old);
1715 drop(current);
1716 assert!(matches!(
1717 context.open("partition", b"blob").await,
1718 Err(Error::BlobAlreadyOpen(p, n)) if p == "partition" && n == "626c6f62"
1719 ));
1720
1721 retained
1723 .write_at(0, b"new", WriteOptions::SYNC)
1724 .await
1725 .unwrap();
1726 drop(retained);
1727 let (reopened, len) = context.open("partition", b"blob").await.unwrap();
1728 assert_eq!(len, 3);
1729 assert_eq!(
1730 reopened
1731 .read_at(0, 3, ReadOptions::default())
1732 .await
1733 .unwrap()
1734 .coalesce(),
1735 b"new"
1736 );
1737 });
1738 }
1739
1740 #[rstest::rstest]
1741 #[case::retained(false)]
1742 #[case::synced(true)]
1743 fn test_logical_open_releases_retained_mutations(#[case] sync: bool) {
1744 let cfg = Config::default().with_storage_fault_config(FaultConfig::default().write(
1746 WriteConfig {
1747 failure_rate: probability!(0.0),
1748 retention_rate: probability!(1.0),
1749 mode: PartialWriteMode::Prefix,
1750 },
1751 ));
1752 let (_, checkpoint) = Runner::new(cfg).start_and_recover(|context| async move {
1753 let (blob, _) = context.open("partition", b"blob").await.unwrap();
1756 let blob = Arc::new(blob);
1757 blob.write_at(0, b"saved", WriteOptions::SYNC)
1758 .await
1759 .unwrap();
1760 blob.write_at(0, b"stale", WriteOptions::default())
1761 .await
1762 .unwrap();
1763 blob.write_at(2, b"X", WriteOptions::SYNC).await.unwrap();
1764 let retained = blob.clone();
1765 drop(blob);
1766 drop(retained);
1767
1768 let (reopened, len) = context.open("partition", b"blob").await.unwrap();
1770 let reopened = Arc::new(reopened);
1771 assert_eq!(len, 5);
1772 assert_eq!(
1773 reopened
1774 .read_at(0, 5, ReadOptions::default())
1775 .await
1776 .unwrap()
1777 .coalesce(),
1778 b"saXed"
1779 );
1780 let retained = reopened.clone();
1781 drop(reopened);
1782
1783 let before = context.auditor().state();
1785 assert!(matches!(
1786 context.open("partition", b"blob").await,
1787 Err(Error::BlobAlreadyOpen(_, _))
1788 ));
1789 assert_ne!(before, context.auditor().state());
1790
1791 if sync {
1793 retained
1794 .write_at(0, b"fresh", WriteOptions::default())
1795 .await
1796 .unwrap();
1797 retained.sync().await.unwrap();
1798 }
1799 });
1800
1801 Runner::from(checkpoint).start(|context| async move {
1803 let (blob, len) = context.open("partition", b"blob").await.unwrap();
1804 assert_eq!(len, 5);
1805 assert_eq!(
1806 blob.read_at(0, 5, ReadOptions::default())
1807 .await
1808 .unwrap()
1809 .coalesce(),
1810 if sync { b"fresh" } else { b"saXed" }
1811 );
1812 });
1813 }
1814
1815 #[rstest::rstest]
1816 #[case::remove("remove")]
1817 #[case::remove_partition("remove_partition")]
1818 #[case::admit("admit")]
1819 fn test_retired_write_owner_releases_other_blob(#[case] operation: &'static str) {
1820 struct Owner<B> {
1821 data: Vec<u8>,
1822 _blob: B,
1823 released: Arc<std::sync::atomic::AtomicBool>,
1824 }
1825
1826 impl<B> AsRef<[u8]> for Owner<B> {
1827 fn as_ref(&self) -> &[u8] {
1828 &self.data
1829 }
1830 }
1831
1832 impl<B> Drop for Owner<B> {
1833 fn drop(&mut self) {
1834 self.released.store(true, Ordering::SeqCst);
1835 }
1836 }
1837
1838 let faults = FaultConfig::default().write(WriteConfig {
1840 failure_rate: probability!(0.0),
1841 retention_rate: probability!(1.0),
1842 mode: PartialWriteMode::Prefix,
1843 });
1844 Runner::new(Config::default().with_storage_fault_config(faults)).start(
1845 |context| async move {
1846 let (a, _) = context.open("partition", b"a").await.unwrap();
1848 let (b, _) = context.open("partition", b"b").await.unwrap();
1849 let b = Arc::new(b);
1850 let released = Arc::new(std::sync::atomic::AtomicBool::new(false));
1851 let payload = Bytes::from_owner(Owner {
1852 data: b"saved".to_vec(),
1853 _blob: b.clone(),
1854 released: released.clone(),
1855 });
1856 a.write_at(0, payload, WriteOptions::default())
1857 .await
1858 .unwrap();
1859
1860 if operation == "remove_partition" {
1862 b.write_at(0, b"second", WriteOptions::default())
1863 .await
1864 .unwrap();
1865 }
1866 drop(b);
1867 assert!(!released.load(Ordering::SeqCst));
1868
1869 if operation == "remove" {
1872 context.remove("partition", Some(b"a")).await.unwrap();
1873 } else if operation == "remove_partition" {
1874 context.remove("partition", None).await.unwrap();
1875 } else {
1876 assert_eq!(operation, "admit");
1877 drop(a);
1878 drop(context.open("partition", b"a").await.unwrap());
1879 }
1880
1881 assert!(released.load(Ordering::SeqCst));
1883 drop(context.open("partition", b"b").await.unwrap());
1884 },
1885 );
1886 }
1887
1888 #[rstest::rstest]
1889 #[case::sync("sync")]
1890 #[case::start_sync("start_sync")]
1891 #[case::overwrite("overwrite")]
1892 fn test_sync_retirement_progresses_with_namespace_open(#[case] operation: &'static str) {
1893 struct Owner<B> {
1895 data: Vec<u8>,
1896 _blob: B,
1897 retiring: std::sync::mpsc::Sender<()>,
1898 }
1899
1900 impl<B> AsRef<[u8]> for Owner<B> {
1901 fn as_ref(&self) -> &[u8] {
1902 &self.data
1903 }
1904 }
1905
1906 impl<B> Drop for Owner<B> {
1907 fn drop(&mut self) {
1908 self.retiring.send(()).unwrap();
1909 }
1910 }
1911
1912 let faults = FaultConfig::default().write(WriteConfig {
1914 failure_rate: probability!(0.0),
1915 retention_rate: probability!(1.0),
1916 mode: PartialWriteMode::Prefix,
1917 });
1918 Runner::new(Config::default().with_storage_fault_config(faults)).start(
1919 |context| async move {
1920 let (a, _) = context.open("partition", b"a").await.unwrap();
1922 let (b, _) = context.open("partition", b"b").await.unwrap();
1923 let b = Arc::new(b);
1924 let (retiring, retired) = std::sync::mpsc::channel();
1925 a.write_at(
1926 0,
1927 Bytes::from_owner(Owner {
1928 data: b"saved".to_vec(),
1929 _blob: b.clone(),
1930 retiring,
1931 }),
1932 WriteOptions::default(),
1933 )
1934 .await
1935 .unwrap();
1936 drop(b);
1937
1938 let (entered, entering) = std::sync::mpsc::channel();
1940 let (release, released) = std::sync::mpsc::channel();
1941 let namespace = context.child("namespace");
1942 let opener = std::thread::spawn(move || {
1943 namespace.opens.pause_namespace(entered, released);
1944 drop(
1945 namespace
1946 .open("partition", b"c")
1947 .now_or_never()
1948 .unwrap()
1949 .unwrap(),
1950 );
1951 });
1952 entering.recv().unwrap();
1953
1954 let mutator = std::thread::spawn(move || {
1956 if operation == "sync" {
1957 a.sync().now_or_never().unwrap().unwrap();
1958 } else if operation == "start_sync" {
1959 a.start_sync()
1960 .now_or_never()
1961 .unwrap()
1962 .now_or_never()
1963 .unwrap()
1964 .unwrap();
1965 } else {
1966 assert_eq!(operation, "overwrite");
1967 a.write_at(0, b"fresh", WriteOptions::SYNC)
1968 .now_or_never()
1969 .unwrap()
1970 .unwrap();
1971 }
1972 });
1973
1974 retired.recv().unwrap();
1977 release.send(()).unwrap();
1978 mutator.join().unwrap();
1979 opener.join().unwrap();
1980 drop(context.open("partition", b"b").await.unwrap());
1981 },
1982 );
1983 }
1984
1985 async fn task(i: usize) -> usize {
1986 for _ in 0..5 {
1987 reschedule().await;
1988 }
1989 i
1990 }
1991
1992 fn run_tasks(tasks: usize, runner: deterministic::Runner) -> (String, Vec<usize>) {
1993 runner.start(|context| async move {
1994 let mut handles = FuturesUnordered::new();
1995 for i in 0..=tasks - 1 {
1996 handles.push(context.child("task").spawn(move |_| task(i)));
1997 }
1998
1999 let mut outputs = Vec::new();
2000 while let Some(result) = handles.next().await {
2001 outputs.push(result.unwrap());
2002 }
2003 assert_eq!(outputs.len(), tasks);
2004 (context.auditor().state(), outputs)
2005 })
2006 }
2007
2008 fn run_with_seed(seed: u64) -> (String, Vec<usize>) {
2009 let executor = deterministic::Runner::seeded(seed);
2010 run_tasks(5, executor)
2011 }
2012
2013 fn run_with_metric(name: &'static str, help: &'static str) -> String {
2014 deterministic::Runner::default().start(|context| async move {
2015 let _: Registered<raw::Counter> = context.register(name, help, raw::Counter::default());
2016 context.auditor().state()
2017 })
2018 }
2019
2020 #[test]
2021 fn test_auditor_separates_metric_fields() {
2022 let state_a = run_with_metric("a", "bc");
2023 let state_b = run_with_metric("ab", "c");
2024
2025 assert_ne!(state_a, state_b);
2026 }
2027
2028 #[test]
2029 fn test_auditor_separates_events() {
2030 let split = Auditor::default();
2032 split.event(b"a", |hasher| hasher.update(b"x"));
2033 split.event(b"b", |_| {});
2034
2035 let joined = Auditor::default();
2036 joined.event(b"a", |hasher| {
2037 hasher.update(b"x");
2038 hasher.update(b"b");
2039 });
2040
2041 assert_ne!(split.state(), joined.state());
2042 }
2043
2044 #[test]
2045 fn test_auditor_state_preserves_history() {
2046 let observed = Auditor::default();
2047 let uninterrupted = Auditor::default();
2048 assert_eq!(observed.state(), "00000000");
2049
2050 observed.event(b"a", |hasher| hasher.update(b"x"));
2052 let before = observed.state();
2053 assert_eq!(before.len(), 8);
2054 assert_eq!(before, observed.state());
2055 observed.event(b"b", |hasher| hasher.update(b"y"));
2056
2057 uninterrupted.event(b"a", |hasher| hasher.update(b"x"));
2059 uninterrupted.event(b"b", |hasher| hasher.update(b"y"));
2060 assert_ne!(observed.state(), before);
2061 assert_eq!(observed.state(), uninterrupted.state());
2062 }
2063
2064 #[test]
2065 fn test_auditor_ignores_buffer_chunking() {
2066 let contiguous = Auditor::default();
2068 contiguous.event(b"send", |hasher| hasher.update(b"abcd"));
2069
2070 let chunked = Auditor::default();
2071 let bufs = IoBufs::from(vec![
2072 IoBuf::from(b"a".to_vec()),
2073 IoBuf::from(b"bc".to_vec()),
2074 IoBuf::from(b"d".to_vec()),
2075 ]);
2076 chunked.event(b"send", |hasher| hasher.update_bufs(&bufs));
2077 assert_eq!(contiguous.state(), chunked.state());
2078
2079 let different = Auditor::default();
2081 different.event(b"send", |hasher| hasher.update(b"abce"));
2082 assert_ne!(contiguous.state(), different.state());
2083 }
2084
2085 #[test]
2086 fn test_same_seed_same_order() {
2087 let mut outputs = Vec::new();
2089 for seed in 0..1000 {
2090 let output = run_with_seed(seed);
2091 outputs.push(output);
2092 }
2093
2094 for seed in 0..1000 {
2096 let output = run_with_seed(seed);
2097 assert_eq!(output, outputs[seed as usize]);
2098 }
2099 }
2100
2101 #[test_traced("TRACE")]
2102 fn test_different_seeds_different_order() {
2103 let output1 = run_with_seed(12345);
2104 let output2 = run_with_seed(54321);
2105 assert_ne!(output1, output2);
2106 }
2107
2108 #[test]
2109 fn test_alarm_min_heap() {
2110 let now = SystemTime::now();
2112 let alarms = vec![
2113 Alarm {
2114 time: now + Duration::new(10, 0),
2115 waker: Arc::new(AtomicWaker::new()),
2116 },
2117 Alarm {
2118 time: now + Duration::new(5, 0),
2119 waker: Arc::new(AtomicWaker::new()),
2120 },
2121 Alarm {
2122 time: now + Duration::new(15, 0),
2123 waker: Arc::new(AtomicWaker::new()),
2124 },
2125 Alarm {
2126 time: now + Duration::new(5, 0),
2127 waker: Arc::new(AtomicWaker::new()),
2128 },
2129 ];
2130 let mut heap = BinaryHeap::new();
2131 for alarm in alarms {
2132 heap.push(alarm);
2133 }
2134
2135 let mut sorted_times = Vec::new();
2137 while let Some(alarm) = heap.pop() {
2138 sorted_times.push(alarm.time);
2139 }
2140 assert_eq!(
2141 sorted_times,
2142 vec![
2143 now + Duration::new(5, 0),
2144 now + Duration::new(5, 0),
2145 now + Duration::new(10, 0),
2146 now + Duration::new(15, 0),
2147 ]
2148 );
2149 }
2150
2151 #[test]
2152 fn test_sleep_refreshes_one_alarm_after_repolling() {
2153 struct Counter(AtomicUsize);
2154
2155 impl ArcWake for Counter {
2156 fn wake_by_ref(this: &Arc<Self>) {
2157 this.0.fetch_add(1, Ordering::Relaxed);
2158 }
2159 }
2160
2161 Runner::default().start(|context| async move {
2162 let first = Arc::new(Counter(AtomicUsize::new(0)));
2164 let latest = Arc::new(Counter(AtomicUsize::new(0)));
2165 let mut sleep = context.sleep(Duration::from_millis(10)).boxed();
2166
2167 for counter in [&first, &latest].into_iter().cycle().take(100) {
2169 assert!(
2170 sleep
2171 .poll_unpin(&mut task::Context::from_waker(&waker(counter.clone())))
2172 .is_pending()
2173 );
2174 }
2175 assert_eq!(context.executor().sleeping.lock().len(), 1);
2176
2177 context.sleep(Duration::from_millis(20)).await;
2179 assert_eq!(first.0.load(Ordering::Relaxed), 0);
2180 assert_eq!(latest.0.load(Ordering::Relaxed), 1);
2181 assert!(futures::poll!(&mut sleep).is_ready());
2182 });
2183 }
2184
2185 #[test]
2186 fn test_dropped_sleeper_before_live_deadline() {
2187 let executor = deterministic::Runner::default();
2188 executor.start(|context| async move {
2189 let (started_sender, started_receiver) = oneshot::channel();
2190 let sleeper = context.child("sleeper").spawn(|context| async move {
2191 let mut sleepers = FuturesUnordered::new();
2192 sleepers.push(context.sleep(Duration::from_secs(1)));
2193 started_sender.send(()).unwrap();
2194 sleepers.next().await;
2195 });
2196
2197 started_receiver.await.unwrap();
2199 sleeper.abort();
2200
2201 context.sleep(Duration::from_secs(2)).await;
2203 });
2204 }
2205
2206 #[cfg(not(feature = "external"))]
2207 #[test]
2208 #[should_panic(expected = "runtime timeout")]
2209 fn test_dropped_sleeper_beyond_timeout() {
2210 let executor = deterministic::Runner::timed(Duration::from_secs(10));
2211 executor.start(|context| async move {
2212 let (started_sender, started_receiver) = oneshot::channel();
2213 let sleeper = context.child("sleeper").spawn(|context| async move {
2214 let mut sleep = Box::pin(context.sleep(Duration::from_secs(20)));
2215 assert!(sleep.as_mut().now_or_never().is_none());
2216 started_sender.send(()).unwrap();
2217 sleep.await;
2218 });
2219
2220 started_receiver.await.unwrap();
2221 sleeper.abort();
2222 pending::<()>().await;
2223 });
2224 }
2225
2226 #[test]
2227 #[should_panic(expected = "runtime timeout")]
2228 fn test_timeout() {
2229 let executor = deterministic::Runner::timed(Duration::from_secs(10));
2230 executor.start(|context| async move {
2231 loop {
2232 context.sleep(Duration::from_secs(1)).await;
2233 }
2234 });
2235 }
2236
2237 #[test]
2238 #[should_panic(expected = "cycle duration must be non-zero when timeout is set")]
2239 fn test_bad_timeout() {
2240 let cfg = Config {
2241 timeout: Some(Duration::default()),
2242 cycle: Duration::default(),
2243 ..Config::default()
2244 };
2245 deterministic::Runner::new(cfg);
2246 }
2247
2248 #[test]
2249 #[should_panic(
2250 expected = "cycle duration must be greater than or equal to system time precision"
2251 )]
2252 fn test_bad_cycle() {
2253 let cfg = Config {
2254 cycle: SYSTEM_TIME_PRECISION - Duration::from_nanos(1),
2255 ..Config::default()
2256 };
2257 deterministic::Runner::new(cfg);
2258 }
2259
2260 #[test]
2262 fn test_removed_blob_reopens_while_handle_alive() {
2263 deterministic::Runner::default().start(|context| async move {
2264 let (old, _) = context.open("partition", b"blob").await.unwrap();
2265 old.write_at(0, b"old", WriteOptions::default())
2266 .await
2267 .unwrap();
2268 context.remove("partition", None).await.unwrap();
2269 let (current, len) = context.open("partition", b"blob").await.unwrap();
2270 assert_eq!(len, 0);
2271
2272 drop(old);
2274 current
2275 .write_at(0, b"new", WriteOptions::default())
2276 .await
2277 .unwrap();
2278 current.sync().await.unwrap();
2279 let read = current
2280 .read_at(0, 3, ReadOptions::default())
2281 .await
2282 .unwrap()
2283 .coalesce();
2284 assert_eq!(read.as_ref(), b"new");
2285 });
2286 }
2287
2288 #[test]
2289 fn test_recover_synced_storage_persists() {
2290 let executor1 = deterministic::Runner::default();
2292 let partition = "test_partition";
2293 let name = b"test_blob";
2294 let data = b"Hello, world!";
2295
2296 let (state, checkpoint) = executor1.start_and_recover(|context| async move {
2298 let (blob, _) = context.open(partition, name).await.unwrap();
2299 blob.write_at(0, data, WriteOptions::default())
2300 .await
2301 .unwrap();
2302 blob.sync().await.unwrap();
2303 context.auditor().state()
2304 });
2305
2306 assert_eq!(state, checkpoint.auditor.state());
2308
2309 let executor = Runner::from(checkpoint);
2311 executor.start(|context| async move {
2312 let (blob, len) = context.open(partition, name).await.unwrap();
2313 assert_eq!(len, data.len() as u64);
2314 let read = blob
2315 .read_at(0, data.len(), ReadOptions::default())
2316 .await
2317 .unwrap();
2318 assert_eq!(read.coalesce(), data);
2319 });
2320 }
2321
2322 #[test]
2323 #[should_panic(expected = "goodbye")]
2324 fn test_recover_panic_handling() {
2325 let executor1 = deterministic::Runner::default();
2327 let (_, checkpoint) = executor1.start_and_recover(|_| async move {
2328 reschedule().await;
2329 });
2330
2331 let executor = Runner::from(checkpoint);
2333 executor.start(|_| async move {
2334 panic!("goodbye");
2335 });
2336 }
2337
2338 #[test]
2339 fn test_recover_unsynced_storage_does_not_persist() {
2340 let executor = deterministic::Runner::default();
2342 let partition = "test_partition";
2343 let name = b"test_blob";
2344 let data = b"Hello, world!";
2345
2346 let (_, checkpoint) = executor.start_and_recover(|context| async move {
2348 let (blob, _) = context.open(partition, name).await.unwrap();
2349 blob.write_at(0, data, WriteOptions::default())
2350 .await
2351 .unwrap();
2352 });
2353
2354 let executor = Runner::from(checkpoint);
2356
2357 executor.start(|context| async move {
2359 let (_, len) = context.open(partition, name).await.unwrap();
2360 assert_eq!(len, 0);
2361 });
2362 }
2363
2364 #[test]
2365 fn test_recover_snapshots_fault_configuration() {
2366 let (stale_config, checkpoint) =
2367 deterministic::Runner::default().start_and_recover(|context| async move {
2368 let config = context.storage_fault_config();
2369 *config.write() = FaultConfig::default().open(probability!(1.0));
2370 config
2371 });
2372 *stale_config.write() = FaultConfig::default();
2373
2374 deterministic::Runner::from(checkpoint).start(|context| async move {
2375 assert!(context.open("fault_config", b"blob").await.is_err());
2376 });
2377 }
2378
2379 #[test]
2380 fn test_recover_retained_successful_resize() {
2381 let retained_resize = [u64::MAX, 0];
2382 let cfg = deterministic::Config::default()
2383 .with_rng(ScriptedRng::new(retained_resize))
2384 .with_storage_fault_config(FaultConfig::default().resize(ResizeConfig {
2385 failure_rate: probability!(0.5),
2386 partial_rate: probability!(0.0),
2387 }));
2388 let (_, checkpoint) =
2389 deterministic::Runner::new(cfg).start_and_recover(|context| async move {
2390 let (blob, _) = context.open("crash_resize", b"blob").await.unwrap();
2391 blob.write_at(0, b"abcdefgh", WriteOptions::SYNC)
2392 .await
2393 .unwrap();
2394 blob.resize(3).await.unwrap();
2395 });
2396
2397 deterministic::Runner::from(checkpoint).start(|context| async move {
2398 let (blob, len) = context.open("crash_resize", b"blob").await.unwrap();
2399 assert_eq!(len, 3);
2400 assert_eq!(
2401 blob.read_at(0, 3, ReadOptions::default())
2402 .await
2403 .unwrap()
2404 .coalesce(),
2405 b"abc"
2406 );
2407 });
2408 }
2409
2410 #[test]
2411 fn test_recover_random_crash_writes_is_seeded_and_epoch_scoped() {
2412 const STABLE_LEN: usize = 32;
2413 const PENDING_LEN: usize = 256;
2414
2415 fn run(seed: u64) -> (Vec<u8>, Digest) {
2416 let cfg = deterministic::Config::default()
2417 .with_seed(seed)
2418 .with_storage_fault_config(FaultConfig::default().write(WriteConfig {
2419 failure_rate: probability!(0.0),
2420 retention_rate: probability!(0.5),
2421 mode: PartialWriteMode::Subset,
2422 }));
2423 let (_, checkpoint) =
2424 deterministic::Runner::new(cfg).start_and_recover(|context| async move {
2425 let (blob, _) = context.open("crash_epoch", b"blob").await.unwrap();
2426 blob.write_at(0, vec![0xA5; STABLE_LEN], WriteOptions::default())
2427 .await
2428 .unwrap();
2429 blob.sync().await.unwrap();
2430 blob.write_at(
2431 STABLE_LEN as u64,
2432 vec![0x5A; PENDING_LEN],
2433 WriteOptions::default(),
2434 )
2435 .await
2436 .unwrap();
2437 });
2438
2439 deterministic::Runner::from(checkpoint).start(|context| async move {
2440 let (blob, len) = context.open("crash_epoch", b"blob").await.unwrap();
2441 let mut bytes = vec![0; STABLE_LEN + PENDING_LEN];
2442 let len = usize::try_from(len).unwrap();
2443 let durable = blob
2444 .read_at(0, len, ReadOptions::default())
2445 .await
2446 .unwrap()
2447 .coalesce();
2448 bytes[..durable.len()].copy_from_slice(durable.as_ref());
2449 (bytes, context.storage_audit())
2450 })
2451 }
2452
2453 let first = run(12345);
2454 let second = run(12345);
2455 let different = run(54321);
2456 assert_eq!(first, second);
2457 assert_ne!(first.0, different.0);
2458 assert!(first.0[..STABLE_LEN].iter().all(|&byte| byte == 0xA5));
2459 assert!(first.0[STABLE_LEN..].contains(&0));
2460 assert!(first.0[STABLE_LEN..].contains(&0x5A));
2461 }
2462
2463 #[test]
2464 fn test_recover_dns_mappings_persist() {
2465 let executor = deterministic::Runner::default();
2467 let host = "example.com";
2468 let addrs = vec![
2469 IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 1)),
2470 IpAddr::V4(std::net::Ipv4Addr::new(192, 168, 1, 2)),
2471 ];
2472
2473 let (state, checkpoint) = executor.start_and_recover({
2475 let addrs = addrs.clone();
2476 |context| async move {
2477 context.resolver_register(host, Some(addrs));
2478 context.auditor().state()
2479 }
2480 });
2481
2482 assert_eq!(state, checkpoint.auditor.state());
2484
2485 let executor = Runner::from(checkpoint);
2487 executor.start(move |context| async move {
2488 let resolved = context.resolve(host).await.unwrap();
2489 assert_eq!(resolved, addrs);
2490 });
2491 }
2492
2493 #[test]
2494 fn test_recover_time_persists() {
2495 let executor = deterministic::Runner::default();
2497 let duration_to_sleep = Duration::from_secs(10);
2498
2499 let (time_before_recovery, checkpoint) = executor.start_and_recover(|context| async move {
2501 context.sleep(duration_to_sleep).await;
2502 context.current()
2503 });
2504
2505 assert_eq!(
2507 time_before_recovery.duration_since(UNIX_EPOCH).unwrap(),
2508 duration_to_sleep
2509 );
2510
2511 let executor2 = Runner::from(checkpoint);
2513 executor2.start(move |context| async move {
2514 assert_eq!(context.current(), time_before_recovery);
2515
2516 context.sleep(duration_to_sleep).await;
2518 assert_eq!(
2519 context.current().duration_since(UNIX_EPOCH).unwrap(),
2520 duration_to_sleep * 2
2521 );
2522 });
2523 }
2524
2525 #[test]
2526 #[should_panic(expected = "executor still has weak references")]
2527 fn test_context_return() {
2528 let executor = deterministic::Runner::default();
2530
2531 let context = executor.start(|context| async move {
2533 context
2535 });
2536
2537 drop(context);
2539 }
2540
2541 #[test]
2542 fn test_default_time_zero() {
2543 let executor = deterministic::Runner::default();
2545 executor.start(|context| async move {
2546 assert_eq!(
2548 context.current().duration_since(UNIX_EPOCH).unwrap(),
2549 Duration::ZERO
2550 );
2551 });
2552 }
2553
2554 #[test]
2555 fn test_start_time() {
2556 let executor_default = deterministic::Runner::default();
2558 executor_default.start(|context| async move {
2559 assert_eq!(context.current(), UNIX_EPOCH);
2560 });
2561
2562 let start_time = UNIX_EPOCH + Duration::from_secs(100);
2564 let cfg = Config::default().with_start_time(start_time);
2565 let executor = deterministic::Runner::new(cfg);
2566 executor.start(move |context| async move {
2567 assert_eq!(context.current(), start_time);
2569 });
2570 }
2571
2572 #[test]
2573 #[should_panic(expected = "start time must be greater than or equal to unix epoch")]
2574 fn test_bad_start_time() {
2575 let cfg = Config::default().with_start_time(UNIX_EPOCH - Duration::from_secs(1));
2576 deterministic::Runner::new(cfg);
2577 }
2578
2579 #[cfg(not(feature = "external"))]
2580 #[test]
2581 #[should_panic(expected = "runtime stalled")]
2582 fn test_stall() {
2583 let executor = deterministic::Runner::default();
2585
2586 executor.start(|_| async move {
2588 pending::<()>().await;
2589 });
2590 }
2591
2592 #[cfg(not(feature = "external"))]
2593 #[test]
2594 #[should_panic(expected = "runtime stalled")]
2595 fn test_external_simulated() {
2596 let executor = deterministic::Runner::default();
2598
2599 let (tx, rx) = oneshot::channel();
2601 std::thread::spawn(move || {
2602 std::thread::sleep(Duration::from_secs(1));
2603 tx.send(()).unwrap();
2604 });
2605
2606 executor.start(|_| async move {
2608 rx.await.unwrap();
2609 });
2610 }
2611
2612 #[cfg(feature = "external")]
2613 #[test]
2614 fn test_paced_future_moves_between_tasks() {
2615 Runner::timed(Duration::from_secs(1)).start(|context| async move {
2616 let clock = context.child("clock");
2618 let latency = Duration::from_millis(10);
2619 let deadline = context.current() + latency;
2620 let mut future = async move { async { 7 }.pace(&clock, latency).await }.boxed();
2621
2622 assert!(futures::poll!(&mut future).is_pending());
2625 let moved = context.child("moved").spawn(move |_| future);
2626
2627 assert_eq!(moved.await.unwrap(), 7);
2629 assert!(context.current() >= deadline);
2630 });
2631 }
2632
2633 #[cfg(feature = "external")]
2634 #[test]
2635 fn test_external_realtime() {
2636 let executor = deterministic::Runner::default();
2638
2639 let (tx, rx) = oneshot::channel();
2641 std::thread::spawn(move || {
2642 std::thread::sleep(Duration::from_secs(1));
2643 tx.send(()).unwrap();
2644 });
2645
2646 executor.start(|_| async move {
2648 rx.await.unwrap();
2649 });
2650 }
2651
2652 #[cfg(feature = "external")]
2653 #[test]
2654 fn test_external_realtime_variable() {
2655 let executor = deterministic::Runner::default();
2657
2658 executor.start(|context| async move {
2660 let start_real = SystemTime::now();
2662 let start_sim = context.current();
2663 let (first_tx, first_rx) = oneshot::channel();
2664 let (second_tx, second_rx) = oneshot::channel();
2665 let (results_tx, mut results_rx) = mpsc::channel(2);
2666
2667 let first_wait = Duration::from_secs(1);
2669 std::thread::spawn(move || {
2670 std::thread::sleep(first_wait);
2671 first_tx.send(()).unwrap();
2672 });
2673
2674 std::thread::spawn(move || {
2676 std::thread::sleep(Duration::ZERO);
2677 second_tx.send(()).unwrap();
2678 });
2679
2680 let first = context.child("sample_before_send").spawn({
2682 let results_tx = results_tx.clone();
2683 move |context| async move {
2684 first_rx.pace(&context, Duration::ZERO).await.unwrap();
2685 let elapsed_real = SystemTime::now().duration_since(start_real).unwrap();
2686 assert!(elapsed_real > first_wait);
2687 let elapsed_sim = context.current().duration_since(start_sim).unwrap();
2688 assert!(elapsed_sim < first_wait);
2689 results_tx.send(1).await.unwrap();
2690 }
2691 });
2692
2693 let second = context
2695 .child("sample_after_send")
2696 .spawn(move |context| async move {
2697 second_rx.pace(&context, first_wait).await.unwrap();
2698 let elapsed_real = SystemTime::now().duration_since(start_real).unwrap();
2699 assert!(elapsed_real >= first_wait);
2700 let elapsed_sim = context.current().duration_since(start_sim).unwrap();
2701 assert!(elapsed_sim >= first_wait);
2702 results_tx.send(2).await.unwrap();
2703 });
2704
2705 second.await.unwrap();
2707 first.await.unwrap();
2708
2709 let mut results = Vec::new();
2711 for _ in 0..2 {
2712 results.push(results_rx.recv().await.unwrap());
2713 }
2714 assert_eq!(results, vec![1, 2]);
2715 });
2716 }
2717
2718 #[cfg(not(feature = "external"))]
2719 #[test]
2720 fn test_simulated_skip() {
2721 let executor = deterministic::Runner::default();
2723
2724 executor.start(|context| async move {
2726 context.sleep(Duration::from_secs(1)).await;
2727
2728 let metrics = context.encode();
2730 let iterations = metrics
2731 .lines()
2732 .find_map(|line| {
2733 line.strip_prefix("runtime_iterations_total ")
2734 .and_then(|value| value.trim().parse::<u64>().ok())
2735 })
2736 .expect("missing runtime_iterations_total metric");
2737 assert!(iterations < 10);
2738 });
2739 }
2740
2741 #[cfg(feature = "external")]
2742 #[test]
2743 fn test_realtime_no_skip() {
2744 let executor = deterministic::Runner::default();
2746
2747 executor.start(|context| async move {
2749 context.sleep(Duration::from_secs(1)).await;
2750
2751 let metrics = context.encode();
2753 let iterations = metrics
2754 .lines()
2755 .find_map(|line| {
2756 line.strip_prefix("runtime_iterations_total ")
2757 .and_then(|value| value.trim().parse::<u64>().ok())
2758 })
2759 .expect("missing runtime_iterations_total metric");
2760 assert!(iterations > 500);
2761 });
2762 }
2763
2764 #[test]
2765 #[should_panic(expected = "label must start with [a-zA-Z]")]
2766 fn test_metrics_label_empty() {
2767 let executor = deterministic::Runner::default();
2768 executor.start(|context| async move {
2769 let _ = context.child("");
2770 });
2771 }
2772
2773 #[test]
2774 #[should_panic(expected = "label must start with [a-zA-Z]")]
2775 fn test_metrics_label_invalid_first_char() {
2776 let executor = deterministic::Runner::default();
2777 executor.start(|context| async move {
2778 let _ = context.child("1invalid");
2779 });
2780 }
2781
2782 #[test]
2783 #[should_panic(expected = "label must only contain [a-zA-Z0-9_]")]
2784 fn test_metrics_label_invalid_char() {
2785 let executor = deterministic::Runner::default();
2786 executor.start(|context| async move {
2787 let _ = context.child("invalid-label");
2788 });
2789 }
2790
2791 #[test]
2792 #[should_panic(expected = "using runtime label is not allowed")]
2793 fn test_metrics_label_reserved_prefix() {
2794 let executor = deterministic::Runner::default();
2795 executor.start(|context| async move {
2796 let _ = context.child(METRICS_PREFIX);
2797 });
2798 }
2799
2800 #[test]
2801 fn test_metrics_duplicate_attribute_overwrites() {
2802 let executor = deterministic::Runner::default();
2803 executor.start(|context| async move {
2804 let context = context
2805 .child("test")
2806 .with_attribute("epoch", "old")
2807 .with_attribute("epoch", "new");
2808 assert_eq!(
2809 context.name().attributes,
2810 vec![("epoch".to_string(), "new".to_string())]
2811 );
2812 });
2813 }
2814
2815 #[test]
2816 fn test_storage_fault_injection_and_recovery() {
2817 let cfg = deterministic::Config::default().with_storage_fault_config(FaultConfig {
2819 sync_rate: Some(probability!(1.0)),
2820 ..Default::default()
2821 });
2822
2823 let (result, checkpoint) =
2824 deterministic::Runner::new(cfg).start_and_recover(|ctx| async move {
2825 let (blob, _) = ctx.open("test_fault", b"blob").await.unwrap();
2826 blob.write_at(0, b"data".to_vec(), WriteOptions::default())
2827 .await
2828 .unwrap();
2829 blob.sync().await });
2831
2832 assert!(result.is_err());
2834
2835 deterministic::Runner::from(checkpoint).start(|ctx| async move {
2837 *ctx.storage_fault_config().write() = FaultConfig::default();
2839
2840 let (blob, len) = ctx.open("test_fault", b"blob").await.unwrap();
2842 assert_eq!(len, 0, "unsynced data should be lost after recovery");
2843
2844 blob.write_at(0, b"recovered".to_vec(), WriteOptions::default())
2846 .await
2847 .unwrap();
2848 blob.sync()
2849 .await
2850 .expect("sync should succeed with faults disabled");
2851
2852 let read_buf = blob.read_at(0, 9, ReadOptions::default()).await.unwrap();
2854 assert_eq!(read_buf.coalesce(), b"recovered");
2855 });
2856 }
2857
2858 #[test]
2859 fn test_storage_fault_dynamic_config() {
2860 let executor = deterministic::Runner::default();
2861 executor.start(|ctx| async move {
2862 let (blob, _) = ctx.open("test_dynamic", b"blob").await.unwrap();
2863
2864 blob.write_at(0, b"initial".to_vec(), WriteOptions::default())
2866 .await
2867 .unwrap();
2868 blob.sync().await.expect("initial sync should succeed");
2869
2870 let storage_fault_cfg = ctx.storage_fault_config();
2872 storage_fault_cfg.write().sync_rate = Some(probability!(1.0));
2873
2874 blob.write_at(0, b"updated".to_vec(), WriteOptions::default())
2876 .await
2877 .unwrap();
2878 let result = blob.sync().await;
2879 assert!(result.is_err(), "sync should fail with faults enabled");
2880
2881 storage_fault_cfg.write().sync_rate = Some(probability!(0.0));
2883
2884 blob.sync()
2886 .await
2887 .expect("sync should succeed with faults disabled");
2888 });
2889 }
2890
2891 #[test]
2892 fn test_storage_fault_determinism() {
2893 fn run_with_seed(seed: u64) -> Vec<bool> {
2895 let cfg = deterministic::Config::default()
2896 .with_seed(seed)
2897 .with_storage_fault_config(FaultConfig {
2898 open_rate: Some(probability!(0.5)),
2899 ..Default::default()
2900 });
2901
2902 let runner = deterministic::Runner::new(cfg);
2903 runner.start(|ctx| async move {
2904 let mut results = Vec::new();
2905 for i in 0..20 {
2906 let name = format!("blob{i}");
2907 let result = ctx.open("test_determinism", name.as_bytes()).await;
2908 results.push(result.is_ok());
2909 }
2910 results
2911 })
2912 }
2913
2914 let results1 = run_with_seed(12345);
2915 let results2 = run_with_seed(12345);
2916 assert_eq!(
2917 results1, results2,
2918 "same seed should produce same failure pattern"
2919 );
2920
2921 let results3 = run_with_seed(99999);
2922 assert_ne!(
2923 results1, results3,
2924 "different seeds should produce different patterns"
2925 );
2926 }
2927
2928 #[test]
2929 fn test_storage_fault_determinism_multi_task() {
2930 fn run_with_seed(seed: u64) -> Vec<u32> {
2933 let cfg = deterministic::Config::default()
2934 .with_seed(seed)
2935 .with_storage_fault_config(FaultConfig {
2936 open_rate: Some(probability!(0.5)),
2937 write_rate: Some(WriteConfig {
2938 failure_rate: probability!(0.3),
2939 retention_rate: probability!(0.0),
2940 mode: PartialWriteMode::Prefix,
2941 }),
2942 sync_rate: Some(probability!(0.2)),
2943 ..Default::default()
2944 });
2945
2946 let runner = deterministic::Runner::new(cfg);
2947 runner.start(|ctx| async move {
2948 let mut handles = Vec::new();
2950 for i in 0..5 {
2951 let ctx = ctx.child("task");
2952 handles.push(ctx.spawn(move |ctx| async move {
2953 let mut successes = 0u32;
2954 for j in 0..4 {
2955 let name = format!("task{i}_blob{j}");
2956 if let Ok((blob, _)) = ctx.open("partition", name.as_bytes()).await {
2957 successes += 1;
2958 if blob
2959 .write_at(0, b"data".to_vec(), WriteOptions::default())
2960 .await
2961 .is_ok()
2962 {
2963 successes += 1;
2964 }
2965 if blob.sync().await.is_ok() {
2966 successes += 1;
2967 }
2968 }
2969 }
2970 successes
2971 }));
2972 }
2973
2974 let mut results = Vec::new();
2976 for handle in handles {
2977 results.push(handle.await.unwrap());
2978 }
2979 results
2980 })
2981 }
2982
2983 let results1 = run_with_seed(42);
2984 let results2 = run_with_seed(42);
2985 assert_eq!(
2986 results1, results2,
2987 "same seed should produce same multi-task pattern"
2988 );
2989
2990 let results3 = run_with_seed(99999);
2991 assert_ne!(
2992 results1, results3,
2993 "different seeds should produce different patterns"
2994 );
2995 }
2996
2997 #[test]
2998 fn test_resolver() {
2999 let executor = deterministic::Runner::default();
3000 executor.start(|context| async move {
3001 let ip1: IpAddr = "192.168.1.1".parse().unwrap();
3003 let ip2: IpAddr = "192.168.1.2".parse().unwrap();
3004 context.resolver_register("example.com", Some(vec![ip1, ip2]));
3005
3006 let addrs = context.resolve("example.com").await.unwrap();
3008 assert_eq!(addrs, vec![ip1, ip2]);
3009
3010 let result = context.resolve("unknown.com").await;
3012 assert!(matches!(result, Err(Error::ResolveFailed(_))));
3013
3014 context.resolver_register("example.com", None);
3016 let result = context.resolve("example.com").await;
3017 assert!(matches!(result, Err(Error::ResolveFailed(_))));
3018 });
3019 }
3020
3021 #[test]
3024 fn test_parallel_strategy_spawn_completes() {
3025 let executor = deterministic::Runner::default();
3026 executor.start(|context| async move {
3027 let pool = context.child("pool").strategy(NZUsize!(2));
3028 let strategy = Explorer::parallel(&pool);
3029
3030 let output = strategy
3031 .spawn(2, |strategy| strategy.map_collect_vec(0..2, |i| i + 1))
3032 .await;
3033
3034 assert_eq!(output, vec![1, 2]);
3035 });
3036 }
3037
3038 #[test]
3042 fn test_strategies_reuse_pool_across_runners() {
3043 let executor = deterministic::Runner::default();
3044 executor.start(|context| async move {
3045 let first = context.child("pool_a").strategy(NZUsize!(1));
3046 assert_eq!(first.run(2, || "serial", || "parallel"), "serial");
3047 let output = first
3048 .spawn(2, |strategy| strategy.map_collect_vec(0..2, |i| i + 1))
3049 .now_or_never()
3050 .expect("single-threaded pool should run spawned work inline");
3051 assert_eq!(output, vec![1, 2]);
3052
3053 let pool_b = context.child("pool_b").strategy(NZUsize!(3));
3054 let second = Explorer::parallel(&pool_b);
3055 assert_eq!(second.run(2, || "serial", || "parallel"), "parallel");
3056 let output = second
3057 .spawn(3, |strategy| strategy.map_collect_vec(0..3, |i| i + 1))
3058 .now_or_never()
3059 .expect("single-threaded pool should run spawned work inline");
3060 assert_eq!(output, vec![1, 2, 3]);
3061 });
3062
3063 let executor = deterministic::Runner::default();
3064 executor.start(|context| async move {
3065 let pool_c = context.child("pool_c").strategy(NZUsize!(4));
3066 let third = Explorer::parallel(&pool_c);
3067 assert_eq!(third.run(2, || "serial", || "parallel"), "parallel");
3068 let output = third
3069 .spawn(4, |strategy| strategy.map_collect_vec(0..4, |i| i + 1))
3070 .now_or_never()
3071 .expect("single-threaded pool should run spawned work inline");
3072 assert_eq!(output, vec![1, 2, 3, 4]);
3073 });
3074 }
3075
3076 #[test]
3080 fn test_pool_survives_suspension() {
3081 let executor = deterministic::Runner::default();
3082 executor.start(|context| async move {
3083 let pool = context.child("pool").strategy(NZUsize!(2));
3084 let strategy = Explorer::parallel(&pool);
3085 context.sleep(Duration::from_millis(10)).await;
3086
3087 let output = strategy
3088 .spawn(2, |strategy| strategy.map_collect_vec(0..2, |i| i + 1))
3089 .await;
3090 assert_eq!(output, vec![1, 2]);
3091
3092 context.sleep(Duration::from_millis(10)).await;
3093 let sum = strategy.fold(0..100u64, || 0u64, |acc, i| acc + i, |a, b| a + b);
3094 assert_eq!(sum, 4950);
3095 });
3096 }
3097
3098 #[test]
3102 fn test_borrowed_non_send_root() {
3103 let value = String::from("borrowed output");
3105 let calls = Rc::new(Cell::new(0));
3106 let value = &value;
3107 let calls = &calls;
3108
3109 let run = |context: Context| async move {
3112 context.sleep(Duration::from_millis(1)).await;
3113 calls.set(calls.get() + 1);
3114 value.as_str()
3115 };
3116
3117 let (output, checkpoint) = Runner::default().start_and_recover(run);
3119 assert_eq!(output, value.as_str());
3120 assert_eq!(calls.get(), 1);
3121
3122 let output = Runner::from(checkpoint).start(run);
3125 assert_eq!(output, value.as_str());
3126 assert_eq!(calls.get(), 2);
3127 }
3128}