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
impl Completion
Sourcepub fn user_data(&self) -> usize
pub fn user_data(&self) -> usize
The UserData identity this completion reports – match it against
a held crate::Token via crate::Token::claim_if.
Sourcepub fn result(&self) -> Result<usize>
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.
§Errors
Returns the wrapped crate::IoRingError if ResultCode is a
failure – for example ERROR_NOT_FOUND when a cancel target was not
actually outstanding.
Sourcepub fn with_injected_failure(self, failure: InjectedFailure) -> Self
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
crate::Token::claim_if’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 claiming a token 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 crate::Token::claim_if, 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
let completion = completion.with_injected_failure(
InjectedFailure::Win32(ERROR_ACCESS_DENIED),
);
assert!(completion.result().is_err());
// The token still claims it: the operation did complete.
let buffer = token.claim_if(&completion).expect("claims its own");Trait Implementations§
Source§impl Clone for Completion
impl Clone for Completion
Source§fn clone(&self) -> Completion
fn clone(&self) -> Completion
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more