Skip to main content

Batch

Struct Batch 

Source
pub struct Batch<'ring> { /* private fields */ }
Expand description

A scoped, exclusive submission window over one IoRing (D-5).

Holding &mut IoRing for its lifetime is what turns Win32’s own “you must serialize submission” footnote into a compiler-enforced guarantee: two batches over the same ring cannot coexist. Every push queues its SQE the instant it succeeds – there is no rewind – so Batch submits on Drop rather than leaving queued SQEs for some later, unrelated submit to discover (D-5).

Implementations§

Source§

impl<'ring> Batch<'ring>

Source

pub fn new(ring: &'ring mut IoRing) -> Self

Open a batch over ring.

Source

pub unsafe fn read_raw<B: IoBufMut>( &mut self, file: impl Into<FileRef>, buffer: B, offset: u64, options: PushOptions, ) -> Result<Token<B>>

Queue a read of buffer.bytes_len() bytes from file at offset.

Prefer Batch::read unless file needs to address a raw FileRef directly, without SharedFile’s Arc bookkeeping.

§Safety

If file is FileRef::Raw, the handle must be valid, opened with read access, and must remain valid – not closed, not reused for a different object – until this operation’s completion is observed (via a popped Completion or Token::claim_if) or until the ring runs down (M8, PR #20 review response). A FileRef::Registered target needs none of this.

§Errors

io::ErrorKind::Unsupported if the ring was not probed as supporting Op::Read; io::ErrorKind::InvalidInput if the buffer is longer than u32::MAX; an crate::IoRingError wrapping IORING_E_SUBMISSION_QUEUE_FULL if the queue has no room (M3.3, not auto-flushed – see Batch’s own docs); or any other error from BuildIoRingReadFile. On any error the buffer is dropped normally, not leaked or handed back.

Source

pub fn read<B: IoBufMut, F: FileTarget>( &mut self, file: &F, buffer: B, offset: u64, options: PushOptions, ) -> Result<Token<(B, F::Guard)>>

As Batch::read_raw, but safe: file is a SharedFile rather than a bare FileRef, so the pushed operation keeps its own clone of the handle alive regardless of what the caller does with its copy. The returned token yields (buffer, file) once claimed.

§Errors

As Batch::read_raw, plus io::ErrorKind::InvalidInput if file is a RegisteredFile from a different ring.

Source

pub unsafe fn write_raw<B: IoBuf>( &mut self, file: impl Into<FileRef>, buffer: B, offset: u64, options: PushOptions, caching: WriteCaching, ) -> Result<Token<B>>

Queue a write of buffer.bytes_len() bytes to file at offset.

caching is FILE_WRITE_FLAGS; see WriteCaching, and note that write-through is a cache directive rather than a durability guarantee. Durability comes from Batch::flush with FlushCoverage::CoversPrecedingOperations, never from a write flag.

Prefer Batch::write unless file needs to address a raw FileRef directly, without SharedFile’s Arc bookkeeping.

§Safety

As Batch::read_raw’s, for a write-access handle.

§Errors

As Batch::read_raw, plus any error from BuildIoRingWriteFile.

Source

pub fn write<B: IoBuf, F: FileTarget>( &mut self, file: &F, buffer: B, offset: u64, options: PushOptions, caching: WriteCaching, ) -> Result<Token<(B, F::Guard)>>

As Batch::write_raw, but safe: file is a SharedFile rather than a bare FileRef. The returned token yields (buffer, file) once claimed.

§Errors

As Batch::write_raw, plus io::ErrorKind::InvalidInput if file is a RegisteredFile from a different ring.

Source

pub unsafe fn flush_raw( &mut self, file: impl Into<FileRef>, coverage: FlushCoverage, mode: FlushMode, ) -> Result<usize>

Queue a flush of file’s buffered data.

coverage decides whether this flush covers the operations queued before it. It is required rather than defaulted because there is no safe default – see FlushCoverage and the measured contract below.

mode is FILE_FLUSH_MODE; FlushMode::Default is the durability barrier. Note that FlushMode::NoSync issues no device sync and so makes nothing durable, whatever coverage says.

There is no buffer, so this returns the raw UserData identity rather than a Token: nothing owns a buffer for a completion to hand back. Prefer Batch::flush unless file needs to address a raw FileRef directly.

§A flush is the ring’s only durability primitive

The ring has no FUA. BuildIoRingWriteFile’s entire flag set is {FILE_WRITE_FLAGS_NONE, FILE_WRITE_FLAGS_WRITE_THROUGH}, and write-through is a cache-bypass directive to the OS rather than a device-level durability guarantee – whether it becomes a Force Unit Access bit depends on the driver, the volume, and whether the device’s write cache is enabled (see WriteCaching). So this operation is the only way the ring makes anything durable, and only with FlushCoverage::CoversPrecedingOperations and a syncing FlushMode.

§The measured contract (D-23 in DESIGN-NOTES.md)

An unflagged flush does not cover preceding writes. It is an ordinary operation competing with them, and it frequently wins: a flush pushed after a batch of writes with no barrier was observed completing while 17, and on another run 23, of 32 of those writes were still outstanding. A caller that reads such a completion as “the writes before it are now durable” has lost data it believes it has committed, and nothing reports the loss until power fails.

Which direction the reordering shows in is device-dependent, and a machine where the flush happens to land last anyway proves nothing: that is incidental behavior of one device stack, not a guarantee. Only the barrier makes it one.

Durability on this ring is therefore a property of an epoch, never of an individual write, because there is no per-write primitive to make it one: stream the writes unflagged, close the epoch with one covering flush, and wait on that flush rather than on the writes. “Durability on the ring” in DESIGN-NOTES.md has the full construction, and the three ways to pay for the barrier’s ring-wide stall.

§Safety

As Batch::read_raw’s, for a FileRef::Raw target.

§Errors

io::ErrorKind::Unsupported if the ring was not probed as supporting Op::Flush; an crate::IoRingError wrapping IORING_E_SUBMISSION_QUEUE_FULL if the queue has no room; or any other error from BuildIoRingFlushFile.

Source

pub fn flush<F: FileTarget>( &mut self, file: &F, coverage: FlushCoverage, mode: FlushMode, ) -> Result<Token<F::Guard>>

As Batch::flush_raw, but safe: file is a FileTarget – a SharedFile or a RegisteredFile – and the returned Token (rather than a bare UserData) holds its guard until this operation’s completion is observed.

coverage is required for the reason FlushCoverage documents: an unflagged flush does not cover preceding writes (D-23), so there is no default spelling of this call that is safe to inherit. mode selects how much is flushed and whether the device is synced at all; see FlushMode.

§Errors

As Batch::flush_raw, plus io::ErrorKind::InvalidInput if file is a RegisteredFile from a different ring.

Source

pub unsafe fn cancel_raw( &mut self, file: impl Into<FileRef>, target: usize, ) -> Result<usize>

Queue cancellation of the operation identified by target (the usize a prior push returned), against file.

A cancel is itself an operation: it completes on its own UserData, returned here, independently of whether target was actually outstanding. Cancelling a target that has already completed – or was never outstanding – reports ERROR_NOT_FOUND through this completion rather than failing to build (M3.6). Prefer Batch::cancel unless file needs to address a raw FileRef directly.

§Safety

As Batch::read_raw’s, for a FileRef::Raw target.

§Errors

io::ErrorKind::Unsupported if the ring was not probed as supporting Op::Cancel; an crate::IoRingError wrapping IORING_E_SUBMISSION_QUEUE_FULL if the queue has no room; or any other error from BuildIoRingCancelRequest.

Source

pub fn cancel<F: FileTarget>( &mut self, file: &F, target: usize, ) -> Result<Token<F::Guard>>

As Batch::cancel_raw, but safe: file is a SharedFile, and the returned Token keeps file’s clone alive until this operation’s completion is observed.

A cancel is a request, not a guarantee (M10.2): target may complete normally regardless, and this push produces its own completion in addition to the target’s, so a cancelled operation yields two. A result of ERROR_NOT_FOUND on this push’s own completion means target was no longer outstanding – a normal race, not a caller error.

§Errors

As Batch::cancel_raw.

Source

pub unsafe fn register_files( &mut self, handles: &[HANDLE], ) -> Result<PendingFileRegistration>

Queue registration of handles as a ring’s file-handle table (M5.1).

BuildIoRingRegisterFileHandles replaces the ring’s entire file-handle table rather than appending to it (Win32 docs: “If a previous registration exists, this replaces the previous registration completely”), which would silently invalidate every RegisteredFile index a prior registration handed out. Rather than track and resubmit that whole prior table transparently, this method refuses a second registration outright: a ring accepts at most one file-handle registration that assigned an index in its lifetime.

Two consequences of that rule being enforced against crate::IoRing::registered_file_count rather than a flag (M10.1): a zero-length handles does not spend the ring’s one registration, since it hands out no index for a later replacement to invalidate; and the count advances when this call queues, not when its completion succeeds (D-14), so a registration whose completion reports failure has still spent it. There is no retry – a consumer whose registration fails must build the registration on a new ring.

handles only needs to stay valid for this call, unlike a data buffer referenced through an IORING_HANDLE_REF/IORING_BUFFER_REF: BuildIoRingRegisterFileHandles has no such ref, it takes the array directly and reads it synchronously – confirmed by measurement, not assumed (D-32). The handles themselves must still stay open for as long as the registration is used – this crate does not take ownership of them, only of their assigned indices’ bookkeeping.

Do not generalize this to Batch::register_buffers: BuildIoRingRegisterBuffers reads its array when the op runs, and assuming otherwise was a live use-after-free in 0.1.2.

§Safety

Every handle in handles must be valid, and must remain valid for as long as the resulting registration is used – for the ring’s remaining life, since Win32 has no unregister call (M8, PR #20 review response). There is no safe counterpart: a single-push Token cannot express a lifetime spanning arbitrarily many later reads and writes against every registered index, unlike a Token tied to one push’s own completion.

§Errors

io::ErrorKind::AlreadyExists if this ring already has a file-handle registration (see above); io::ErrorKind::Unsupported if the ring was not probed as supporting Op::RegisterFiles; io::ErrorKind::InvalidInput if handles has more than u32::MAX entries; an crate::IoRingError wrapping IORING_E_SUBMISSION_QUEUE_FULL if the queue has no room; or any other error from BuildIoRingRegisterFileHandles.

Source

pub fn register_buffers<B: IoBufMut>( &mut self, buffers: Vec<B>, ) -> Result<PendingBufferRegistration<B>>

Queue registration of buffers as a ring’s registered-buffer table (M5.2).

As Batch::register_files: BuildIoRingRegisterBuffers replaces the ring’s entire buffer table rather than appending to it, so this method refuses a second registration outright – a ring accepts at most one buffer registration that assigned an index in its lifetime, with the same zero-length and failed-registration consequences Batch::register_files spells out (M10.1).

Unlike Batch::register_files, two things must outlive this call, and the asymmetry is measured rather than assumed (D-32):

  • the bytes each entry points at – the registration case IoBuf’s contract was extended to cover (D-11), so buffers is taken by value and kept inside the returned RegisteredBuffers once claimed;
  • the IORING_BUFFER_INFO array itself. BuildIoRingRegisterBuffers does not read it synchronously the way BuildIoRingRegisterFileHandles reads its handles array; the kernel reads it when the registration op runs, during a later SubmitIoRing. This crate builds that array and hands it to the ring, which holds it for its remaining life, so a caller has nothing to do – but the distinction is why crate::IoRing::push_raw callers building this op themselves must not pass a temporary.
§Errors

As Batch::register_files, for Op::RegisterBuffers and BuildIoRingRegisterBuffers.

Source

pub unsafe fn read_registered_raw<B: IoBufMut>( &mut self, file: impl Into<FileRef>, registration: &RegisteredBuffers<B>, span: RegisteredSpan, file_offset: u64, options: PushOptions, ) -> Result<Token<RegisteredUse>>

Queue a read of span.len bytes from file at file_offset, into span’s byte offset of registration’s buffer at span.buffer_index, instead of handing over a fresh owned buffer (M5.2).

The returned Token must be claimed once its completion is observed, exactly like Batch::read’s – but claiming it recovers no buffer, only releases this use against registration’s own drop check (M5.3). Read the transferred bytes back from registration itself afterward, for example via a caller-side accessor into the buffer it was constructed from. Prefer Batch::read_registered unless file needs to address a raw FileRef directly.

§Safety

As Batch::read_raw’s.

§Errors

As Batch::read_raw, plus io::ErrorKind::InvalidInput if span.buffer_index is out of range for registration.

Source

pub fn read_registered<B: IoBufMut, F: FileTarget>( &mut self, file: &F, registration: &RegisteredBuffers<B>, span: RegisteredSpan, file_offset: u64, options: PushOptions, ) -> Result<Token<(RegisteredUse, F::Guard)>>

As Batch::read_registered_raw, but safe: file is a FileTarget rather than a bare FileRef. The returned token yields (RegisteredUse, guard) once claimed.

Passing a RegisteredFile here is the fully-registered form – registered file and registered buffer, neither costing a handle lookup nor a buffer pin per operation – which before M10.4 the safe API could not express at all.

§Errors

As Batch::read_registered_raw, plus io::ErrorKind::InvalidInput if file is a RegisteredFile from a different ring.

Source

pub unsafe fn write_registered_raw<B: IoBufMut>( &mut self, file: impl Into<FileRef>, registration: &RegisteredBuffers<B>, span: RegisteredSpan, file_offset: u64, options: PushOptions, caching: WriteCaching, ) -> Result<Token<RegisteredUse>>

Queue a write of span.len bytes to file at file_offset, from span’s byte offset of registration’s buffer at span.buffer_index (M5.2).

As Batch::read_registered_raw, but for BuildIoRingWriteFile. Prefer Batch::write_registered unless file needs to address a raw FileRef directly.

§Safety

As Batch::read_registered_raw’s.

§Errors

As Batch::read_registered_raw.

Source

pub fn write_registered<B: IoBufMut, F: FileTarget>( &mut self, file: &F, registration: &RegisteredBuffers<B>, span: RegisteredSpan, file_offset: u64, options: PushOptions, caching: WriteCaching, ) -> Result<Token<(RegisteredUse, F::Guard)>>

As Batch::write_registered_raw, but safe: file is a FileTarget rather than a bare FileRef. The returned token yields (RegisteredUse, guard) once claimed.

Passing a RegisteredFile here is the fully-registered form, as for Batch::read_registered.

§Errors

As Batch::write_registered_raw, plus io::ErrorKind::InvalidInput if file is a RegisteredFile from a different ring.

Source

pub fn submit(self) -> Result<u32>

Submit everything queued so far, returning the number of entries the kernel accepted.

That count is entries submitted, never entries completed (M10.2): draining is crate::IoRing::try_pop’s job and stays the caller’s.

§Errors

Returns any error from SubmitIoRing.

Source

pub fn submit_and_wait( self, wait_operations: u32, timeout_ms: u32, ) -> Result<u32>

Submit everything queued so far, then block until at least wait_operations of them have completed or timeout_ms elapses (D-3’s Model B primitive: the fused submit-and-wait is the event loop for a pinned-thread consumer).

Returning does not mean wait_operations completions are poppable: the timeout can expire first, and the returned count is entries submitted rather than completed (M10.2). Drain with crate::IoRing::try_pop and count for yourself.

§Errors

Returns any error from SubmitIoRing.

Trait Implementations§

Source§

impl Drop for Batch<'_>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<'ring> !Sync for Batch<'ring>

§

impl<'ring> !UnwindSafe for Batch<'ring>

§

impl<'ring> Freeze for Batch<'ring>

§

impl<'ring> RefUnwindSafe for Batch<'ring>

§

impl<'ring> Send for Batch<'ring>

§

impl<'ring> Unpin for Batch<'ring>

§

impl<'ring> UnsafeUnpin for Batch<'ring>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.