ringbahn / ringbahn

safe bindings to io-uring

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support mocking of IO

withoutboats opened this issue · comments

It occurred to me this monring that io-uring is exceptionally mockable as far as IO interfaces go, because it is mostly just a data structure. With changes to make it easier to create fake SQEs and CQEs, a mock driver for testing that does no actual IO would not be difficult to build.

This sounds like it would be very interesting for deterministic simulation testing!

For creating fake SQEs/CQEs, would that be something which could be done within the iou crate or were you thinking of adding traits for SQEs/CQEs which implementers of Drive would provide concrete impls for?

Don't mean to barge in but I believe at least mock CQEs are supported by iou (https://docs.rs/iou/0.3.3/iou/cqe/struct.CQE.html#method.from_raw_parts)

I think SQEs would be the tricky one. The only way to get a valid sqe pointer from liburing is to call io_uring_get_sqe on a ring instance, which is the same for iou. Adding a SQE::from_raw method to iou seems like an unnecessary footgun that would only be useful for mocks. Trying to actually submit the fake SQE will be buggy.

Is the goal to avoid requiring a valid io_uring instance entirely? Maybe another way is to define mock driver internal structs FakeSQE, FakeSQEs identical to iou::SQE, iou::SQEs etc. and transmute between them. This would let the driver implement all the prepare methods without requiring changes elsewhere.

@mxxo Good point! I’m not super familiar with the requirements of transmute so please correct me if I’m wrong but to transmute between FakeSQEs and SQEs, would the SQEs iterator have to have a stable layout like #repr(C) or something?

You're right for the general case but I think in this case we don't need any extra annotations because SQEs (SQE too) have a single field, so their layout is defined as that of the field. We can also avoid transmute entirely and use pointer casts (suggested by clippy):

// pub struct SQEs<'ring> {
//     sqes: slice::IterMut<'ring, uring_sys::io_uring_sqe>,
// }

use iou::SQEs;

struct MockSqes<'ring> {
    sqes: std::slice::IterMut<'ring, uring_sys::io_uring_sqe>,
}

impl<'ring> MockSqes<'ring> {
    fn as_sqes(&mut self) -> &mut SQEs {
        unsafe { &mut *(self as *mut MockSqes as *mut SQEs) }
    }
}

I don't know if this is a great implementation idea for a mock driver in general though.