pub struct EventDelivery { /* private fields */ }Expand description
Delivers an IoRing’s completions on thread-pool callback threads via
its completion event, rather than a caller polling IoRing::try_pop
itself (Model A, D-3).
§Not a CleanupGroup member
There is no way to add an already-built EventDelivery to a
CleanupGroup, for
the same reason CleanupGroup excludes
ThreadpoolIo: the ring must
run down its outstanding operations before closing, and a group’s bulk
CloseThreadpoolCleanupGroupMembers has no way to run that logic for a
member it did not create itself. EventDelivery stays individually
owned, where its own field-drop order (below) gives the same
quiesce-then-close guarantee a group would otherwise provide.
Implementations§
Source§impl EventDelivery
impl EventDelivery
Sourcepub fn new<F>(
ring: IoRing,
on_completion: F,
env: Option<&mut CallbackEnviron<'_>>,
) -> Result<Self>
pub fn new<F>( ring: IoRing, on_completion: F, env: Option<&mut CallbackEnviron<'_>>, ) -> Result<Self>
Wire ring’s completion event to a thread-pool wait, delivering every
popped Completion to on_completion on a pool thread (M4.2).
The wait is armed before this returns, so the calling thread never waits for a completion itself (M4.4).
§Completions already queued when ring is handed over
They are delivered too – but that is a guarantee this method has to
buy, not one it inherits, and saying so is the point of this section.
The ring’s event is edge-triggered on the completion queue going empty
to non-empty (IoRing::completion_event, D-19), so attaching to a
ring whose queue is already non-empty signals nothing, and no later
completion signals either, because the queue never returns to empty to
re-arm the edge. Such a backlog is stranded permanently.
What closes that gap is the deliberate signal
IoRing::completion_event raises as it attaches: the first callback
then drains the backlog exactly as it would any other wakeup.
This was false in the implementation, and asserted anyway in this
rustdoc, before M11.3 – every test until then handed over a fresh
ring, so nothing contradicted it. A caller on an earlier version
cannot rely on the guarantee; tests/event_delivery.rs keeps the
repro that now holds it.
§Errors
Returns io::ErrorKind::Unsupported if the running system does not
report IORING_FEATURE_SET_COMPLETION_EVENT (M4.1), which
IoRing::completion_event is what decides. This crate refuses to
silently substitute a thread-based polling loop instead – a caller
who asked for event-driven delivery and got a spun-up thread has been
told something false. Also returns any other error from
IoRing::completion_event or from ThreadpoolWait::new.
Sourcepub fn scope(&self) -> RingScope<'_>
pub fn scope(&self) -> RingScope<'_>
Lock the wrapped ring and return a scope for submitting work to it.
A caller submits by opening a RingScope::batch, the same ring the
wait callback locks to pop completions. Mutex poisoning is absorbed
here rather than surfaced: a panic elsewhere does not invalidate a ring
handle, and the callback’s own drain already takes the same view, so
making every caller write unwrap_or_else(PoisonError::into_inner) only
invited the inconsistency this method removes.
§What the scope deliberately withholds
The rule is: every read-only part of IoRing, plus batch
construction – and nothing that can retarget the ring or steal the
pool’s completions. So there is no try_pop (D-21 makes the pool the
single drainer; a second one breaks the drain-to-empty that re-arms the
edge), no completion_event (it hands back a duplicate of the event
this EventDelivery already waits on, giving two waiters on one ring),
no run_down, and above all no &mut IoRing.
That last one is the point, and it is why this returns a scope rather
than the &Mutex<IoRing> it used to. Any &mut IoRing permits
whole-value assignment, so safe code could replace the ring while the
pool’s wait stayed armed on the original ring’s event – measured, and
delivery stopped silently (D-43). Note a
Deref/DerefMut newtype would not have closed that, since
*scope = ... works through DerefMut just as well; nor would handing
a &mut IoRing to a closure.
Replacing the ring is therefore refused at compile time:
let delivery =
EventDelivery::new(IoRing::new(8, 8).unwrap(), |_| {}, None).unwrap();
let mut scope = delivery.scope();
// No `DerefMut`, so there is no `&mut IoRing` to assign through.
*scope = IoRing::new(8, 8).unwrap();The same setup doing the legitimate thing must still compile. A
compile_fail example passes on any error, including a typo in its
own setup, so this pair is what keeps the one above honest:
let delivery =
EventDelivery::new(IoRing::new(8, 8).unwrap(), |_| {}, None).unwrap();
let scope = delivery.scope();
assert_eq!(scope.outstanding(), 0);