Skip to main content

Completion

Struct Completion 

Source
pub struct Completion { /* private fields */ }
Expand description

One popped completion (M3.7): the operation’s identity, from IORING_CQE::UserData, and its result.

Implementations§

Source§

impl Completion

Source

pub fn user_data(&self) -> usize

The UserData identity this completion reports.

Not a way to reach what the operation was holding. The pop that produced this completion already returned that, and it is the only call that can – see IoRing::try_pop. This is the integer for correlation and for naming a cancel target, which is the same thing crate::OperationId is and for the same reason.

It used to say “match it against a held Token”, and that instruction was D-55’s evidence that the crate handed a caller two halves and connected them with nothing.

Source

pub fn result(&self) -> Result<usize>

This op’s result: the transferred byte count (read/write) or other op-specific value in IORING_CQE::Information, once ResultCode says success.

§The count may be short, and the remainder is yours

A successful read or write may report fewer bytes than were requested, including zero (RS-P-8 in RESPONSE-SPACE.md). WriteFile documents this for non-blocking byte-mode pipes; sockets report a short send when the transmit buffer is full, and reads are short at end of file. This crate takes a handle and does not constrain what kind it is, so a consumer must compare this count against the length it submitted rather than assume they are equal.

Nothing here reissues the remainder. Whether to submit another operation for it, how many times, and when to give up are the caller’s (D-67), and a consumer that loops must tolerate a completion that makes no progress.

A consumer that has narrowed its own handle type can rely on more – for an ordinary file on a local volume a successful completion is expected to carry the full length, and a full volume is an error rather than a short success. That is a guarantee such a consumer earns by constraining the handle, and it belongs in its contract rather than being assumed from this one.

§Errors

Returns the wrapped crate::IoRingError if ResultCode is a failure – for example ERROR_NOT_FOUND when a cancel target was not actually outstanding.

Source

pub fn with_injected_failure(self, failure: InjectedFailure) -> Self

Report failure from Completion::result instead of what this operation actually returned (M16.3).

§What this is for

Failure paths that are otherwise unreachable in a test. Most of what a consumer must handle – a write that fails partway, a flush the device rejects, a registration the kernel refuses – cannot be provoked on demand from a healthy machine, so that code is typically written once and never executed again. This crate shipped a defect of exactly that shape: a claim path that returned early on a failed write and leaked its registered-buffer slot permanently, on a branch no test had ever taken.

§Why this is sound, where fabricating a completion would not be

The pop’s safety argument is that a Completion for some UserData existing at all proves the kernel has finished with that operation, and therefore that handing its buffer back is sound.

This method consumes a completion the ring genuinely popped and returns one carrying the same UserData and the same ring identity. The operation really did finish; the only thing that changes is what Completion::result says about it. So the argument above is untouched, and claiming against the result is exactly as sound as claiming against the original.

Completion::synthetic is the opposite, and is why it is not reachable from here. A fabricated completion can name an operation that is still in flight, and reclaiming a payload against one hands a buffer back to the caller while the kernel is still writing through it – the precise use-after-free this crate exists to prevent. That is a test-only, crate-only tool and stays one.

§What it cannot do

Only failure can be injected, never success. Turning a real failure into an apparent success would let a test conceal a genuine defect, which is the wrong affordance for a tool whose whole purpose is exercising error handling. That is enforced rather than merely stated: a non-failing HRESULT panics.

§Panics

If failure does not resolve to a failing HRESULT. Only InjectedFailure::Hresult can express that at all.

§Prefer a real failure when one is reachable

A genuine kernel error – writing through a read-only handle, cancelling something that is not outstanding – tests the real path and confirms the crate’s own error translation. Reach for this only where no such route exists.

§The one place injection is not inert: registration completions

The soundness argument above is about the pop, where a failed completion changes nothing the caller does with memory: the buffer comes back either way. A registration claim is different. crate::PendingBufferRegistration::claim_if treats a failed completion as proof the kernel did not retain the addresses, and so drops the buffers. Inject a failure there and it frees memory the kernel genuinely does hold registered – leaving the ring with a registration pointing at freed pages.

That is inert only while nothing uses it. A test doing this must not push any registered-buffer operation on that ring afterwards, and should tear the ring down immediately.

This is a limitation of what a failed registration means, not something the seam can check: a Completion does not carry which operation produced it, so this cannot be refused at the call. Prefer a genuine failure for registration paths wherever one can be provoked.

§Example
ⓘ
// ONE pop. The completion being injected into has already been popped,
// and that same pop is what retired the inventory entry and handed the
// payload back -- there is no second pop that could return it again.
let (completion, payload) = ring
    .pop_within(WAIT)
    .expect("pop_within")
    .expect("a completion arrives within the wait");

// The payload comes back whatever the result says: the operation did
// complete, and a failed completion is still the proof that frees the
// buffer.
let completion = completion.with_injected_failure(
    InjectedFailure::Win32(ERROR_ACCESS_DENIED),
);
assert!(completion.result().is_err());
Source

pub fn with_injected_transfer(self, transferred: usize) -> Self

Report a successful transfer of transferred bytes instead of what this operation actually reported (M26.11).

§What this is for, and why the failure seam cannot do it

Completion::with_injected_failure models an operation that failed. RS-P-8 describes something different and stranger: an operation that succeeded while moving fewer bytes than were asked for. Windows documents that for non-blocking byte-mode pipes and it happens on sockets, but an ordinary file on a local volume does not do it – so a consumer’s handling of a short count is written once against the documentation and then never executed again, which is exactly the class of path the failure seam was introduced for.

A consumer that narrows its handle type may legitimately require complete transfers. This seam is how such a consumer tests that its requirement is enforced rather than merely stated – epoch_log’s appender uses it for precisely that.

§Why this one is inert

The result code is left successful and only the byte count moves, so the pop behaves exactly as it would for the real completion: the buffer comes back, and no path keys memory ownership off the transferred count. That makes this seam free of the registration hazard documented on Completion::with_injected_failure, which arises only because a failed registration is taken as proof the kernel retained nothing.

§Example
ⓘ
// A handle whose successful writes are complete is a requirement this
// consumer states; here we hand it one that violates the requirement.
let completion = completion.with_injected_transfer(RECORD_STRIDE - 1);
assert_eq!(completion.result().expect("still a success"), RECORD_STRIDE - 1);

Trait Implementations§

Source§

impl Clone for Completion

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Completion

Source§

impl Debug for Completion

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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, !>

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.