// Test-only, unprivileged concurrent direct reads and durable DATA writes. use clap::Parser; use std::{ fs::{File, OpenOptions}, io::{Read, Seek, SeekFrom, Write}, os::unix::fs::OpenOptionsExt, path::PathBuf, sync::{Arc, Barrier}, time::Instant, }; #[repr(align(16384))] struct Block([u8; 65536]); #[derive(Parser)] #[command(about = "Read eleven cartridge fixtures while writing disposable DATA")] struct Cli { #[arg(required = true, num_args = 11)] paths: Vec, } fn main() { let paths = Cli::parse().paths; let barrier = Arc::new(Barrier::new(12)); let origin = Instant::now(); let mut jobs = Vec::new(); for (index, path) in paths.into_iter().enumerate() { let barrier = barrier.clone(); jobs.push(std::thread::spawn(move || { let mut file = OpenOptions::new() .read(true) .custom_flags(libc::O_DIRECT) .open(path) .unwrap(); // O_DIRECT let mut block = Box::new(Block([0; 65536])); barrier.wait(); let start = origin.elapsed().as_nanos(); for _ in 0..4 { file.seek(SeekFrom::Start(0)).unwrap(); for _ in 0..128 { file.read_exact(&mut block.0).unwrap(); assert!(block.0.iter().all(|b| *b == (index + 2) as u8)); } } ( index + 2, start, origin.elapsed().as_nanos(), 32 * 1024 * 1024, ) })); } let writer = std::thread::spawn(move || { let mut file = File::create("/data/stress.bin").unwrap(); let mut block = [0_u8; 65536]; for (i, byte) in block.iter_mut().enumerate() { *byte = (i % 256) as u8; } barrier.wait(); let start = origin.elapsed().as_nanos(); for _ in 0..1024 { file.write_all(&block).unwrap(); } file.sync_all().unwrap(); (1, start, origin.elapsed().as_nanos(), 64 * 1024 * 1024) }); let mut results: Vec<_> = jobs.into_iter().map(|j| j.join().unwrap()).collect(); results.push(writer.join().unwrap()); for (bay, start, end, bytes) in results { println!("{{\"bay\":{bay},\"start_ns\":{start},\"end_ns\":{end},\"bytes\":{bytes}}}"); } }