use std::{ cell::{Cell, RefCell}, sync::Arc, }; use rand::{RngCore, SeedableRng}; use rand_chacha::ChaCha8Rng; use turso_core::{Clock, IO, Instant, OpenFlags, PlatformIO, Result}; use crate::runner::{clock::SimulatorClock, file::SimulatorFile}; pub(crate) struct SimulatorIO { pub(crate) inner: Box, pub(crate) fault: Cell, pub(crate) files: RefCell>>, pub(crate) rng: RefCell, pub(crate) page_size: usize, seed: u64, latency_probability: usize, clock: Arc, } unsafe impl Send for SimulatorIO {} unsafe impl Sync for SimulatorIO {} impl SimulatorIO { pub(crate) fn new( seed: u64, page_size: usize, latency_probability: usize, min_tick: u64, max_tick: u64, ) -> Result { let inner = Box::new(PlatformIO::new()?); let fault = Cell::new(false); let files = RefCell::new(Vec::new()); let rng = RefCell::new(ChaCha8Rng::seed_from_u64(seed)); let clock = SimulatorClock::new(ChaCha8Rng::seed_from_u64(seed), min_tick, max_tick); Ok(Self { inner, fault, files, rng, page_size, seed, latency_probability, clock: Arc::new(clock), }) } pub(crate) fn inject_fault(&self, fault: bool) { self.fault.replace(fault); for file in self.files.borrow().iter() { file.inject_fault(fault); } } pub(crate) fn print_stats(&self) { for file in self.files.borrow().iter() { tracing::info!( "\n===========================\n\nPath: {}\n{}", file.path, file.stats_table() ); } } } impl Clock for SimulatorIO { fn now(&self) -> Instant { self.clock.now().into() } } impl IO for SimulatorIO { fn open_file( &self, path: &str, flags: OpenFlags, _direct: bool, ) -> Result> { let inner = self.inner.open_file(path, flags, false)?; let file = Arc::new(SimulatorFile { path: path.to_string(), inner, fault: Cell::new(false), nr_pread_faults: Cell::new(0), nr_pwrite_faults: Cell::new(0), nr_sync_faults: Cell::new(0), nr_pread_calls: Cell::new(0), nr_pwrite_calls: Cell::new(0), nr_sync_calls: Cell::new(0), page_size: self.page_size, rng: RefCell::new(ChaCha8Rng::seed_from_u64(self.seed)), latency_probability: self.latency_probability, sync_completion: RefCell::new(None), queued_io: RefCell::new(Vec::new()), clock: self.clock.clone(), }); self.files.borrow_mut().push(file.clone()); Ok(file) } fn remove_file(&self, path: &str) -> Result<()> { self.files.borrow_mut().retain(|x| x.path != path); Ok(()) } fn run_once(&self) -> Result<()> { let now = self.now(); for file in self.files.borrow().iter() { file.run_queued_io(now)?; } self.inner.run_once()?; Ok(()) } fn generate_random_number(&self) -> i64 { self.rng.borrow_mut().next_u64() as i64 } }