use std::time::{Duration, Instant};
use io_buffers::AlignedBuf;
use super::{QUEUE, TempFile};
use crate::dioring::{DeliveryMode, Dioring, Setup, TargetSetup};
use crate::windows_backend::{FileOptions, OpenedFile, Unbuffered, Win32FileTarget};
use durable_ioring::backend::RefusalCause;
use durable_ioring::{
DurableRing, Entry, IoTarget, IoTargetKey, LineageHandle, OpCompletion, Outcome, PushRefusal,
WarrantyClass, WriteCaching, WriteOptions,
};
const FILE: IoTargetKey = IoTargetKey(1);
const SECTOR: usize = Unbuffered::DEFAULT_SECTOR_SIZE as usize;
const BOUND: Duration = Duration::from_secs(10);
type AlignedRing = Dioring<AlignedBuf, u64, u32>;
fn unbuffered(temp: &TempFile) -> Win32FileTarget {
Win32FileTarget::new(
OpenedFile::<Unbuffered>::open(&temp.0, FileOptions::new().read(true).write(true))
.expect("open unbuffered"),
)
}
fn instance(temp: &TempFile) -> (AlignedRing, LineageHandle) {
let target = unbuffered(temp);
assert_eq!(target.warranty(), WarrantyClass::SharedDevice);
assert!(target.capabilities().write_through());
AlignedRing::new(Setup {
submission_queue_size: QUEUE,
completion_queue_size: QUEUE,
files: vec![TargetSetup { key: FILE, target }],
buffers: Vec::<Vec<u8>>::new(),
provider: None,
diagnostics: None,
delivery: DeliveryMode::Pool(None),
})
.expect("build an instance over an unbuffered file")
}
fn sector(byte: u8) -> AlignedBuf {
AlignedBuf::from_bytes(&vec![byte; SECTOR], SECTOR).expect("an aligned buffer")
}
fn completion(ring: &mut AlignedRing) -> OpCompletion<crate::WindowsIds<u64>, AlignedBuf, u32> {
let started = Instant::now();
loop {
match ring.pop().expect("pop") {
Some(Entry::Op(completion)) => return completion,
Some(other) => panic!("expected a completion, got {other:?}"),
None => {
assert!(started.elapsed() < BOUND, "nothing arrived");
std::thread::sleep(Duration::from_millis(1));
}
}
}
}
#[test]
fn a_per_write_write_through_write_completes_on_an_unbuffered_file() {
let temp = TempFile::new("unbuffered-through");
let (mut ring, lineage) = instance(&temp);
ring.write_with(
FILE,
0,
sector(9),
lineage.at(1),
5,
WriteOptions::new().caching(WriteCaching::WriteThrough),
)
.expect("push an aligned write-through write");
let done = completion(&mut ring);
assert!(
matches!(done.outcome, Outcome::Transferred(n) if n as usize == SECTOR),
"{:?}",
done.outcome
);
assert_eq!(done.buffer.as_deref().map(<[u8]>::len), Some(SECTOR));
assert_eq!(
std::fs::read(&temp.0).expect("read back")[..SECTOR],
vec![9u8; SECTOR]
);
}
#[test]
fn an_operation_off_the_sector_is_refused_before_the_kernel_is_asked() {
let temp = TempFile::new("unbuffered-refused");
let (mut ring, lineage) = instance(&temp);
for offset in [1, 512, SECTOR as u64 + 1] {
let refused = ring
.write(FILE, offset, sector(1), lineage.at(1), 6)
.expect_err("off the sector");
assert!(
matches!(refused.reason, PushRefusal::Backend(RefusalCause::Invalid)),
"offset {offset}: {:?}",
refused.reason
);
assert!(refused.buffer.is_some(), "the buffer comes back");
}
let short = AlignedBuf::from_bytes(&[0; 512], SECTOR).expect("aligned, but short");
let refused = ring
.write(FILE, 0, short, lineage.at(1), 7)
.expect_err("a length off the sector");
assert!(matches!(
refused.reason,
PushRefusal::Backend(RefusalCause::Invalid)
));
}