pub struct RingContract { /* private fields */ }Expand description
An executable form of this crate’s conservation rules.
Fed by the caller rather than wired into crate::Batch, for two reasons.
A ring can be driven through push_raw and the _raw entry points without
any of this crate’s bookkeeping being involved at all, so an internal hook
would silently cover less than it appears to. And a consumer validating its
own harness needs to drive the same rules from outside.
§What this costs to run, which is bounded (M28.2)
Memory here is a function of operations in flight, not of operations ever performed. An operation that finishes leaves the tracking map, so a consumer that pushes and claims forever holds a map whose size follows its own concurrency rather than its uptime.
That was not true before M28.2: a claimed operation was marked Completed
and kept, so the oracle retained one entry per operation for the life of
the process. The sample never showed it because it appends 24 records, and
nothing documented it – so a long-running consumer following this crate’s
own recommendation grew without limit. It is also why an always-on checked
inventory was ruled out in M23.3, since a check that cannot be left
running is not a check.
Retiring an entry would lose the ability to call a later completion for it
a duplicate rather than merely unrecognised, so the last
FINISHED_HISTORY finished identities are remembered for that purpose
alone. Beyond that window a duplicate is still reported, under the weaker
name.
§Example
use windows_ioring_sys::contract::RingContract;
let mut contract = RingContract::new();
contract.observe_push(0x1234);
contract.observe_completion(0x1234);
assert!(contract.check_quiescent().is_empty());Implementations§
Source§impl RingContract
impl RingContract
Sourcepub fn observe_push(&mut self, user_data: usize)
pub fn observe_push(&mut self, user_data: usize)
Record that an SQE was successfully queued with this user_data.
Report only pushes that queued. A Build* call that failed
synchronously releases its reservation and produces no completion, so
reporting it would manufacture an Violation::Outstanding at
teardown.
There is one push observer, as of M28.5. There were two:
observe_push recorded a push that returned a token, and
observe_tokenless_push recorded an SQE that carried no token to
claim – the _raw flush and cancel entry points – because reporting
one through observe_push produced a leak violation its caller had no
way to satisfy, there being no token to claim. D-74
removed the leak rule, which made the two identical in body as well as
in purpose, and two names for one behaviour is a restatement waiting
for the next change to reach only one of them.
Sourcepub fn observe_completion(&mut self, user_data: usize)
pub fn observe_completion(&mut self, user_data: usize)
Record a completion popped from the ring.
Sourcepub fn in_flight(&self) -> usize
pub fn in_flight(&self) -> usize
How many operations have been pushed and not yet completed.
The same number RingContract::check_quiescent turns into one
Violation::Outstanding each, offered directly because a consumer
usually wants the count rather than the diagnosis – a drain loop asks
“is anything left?”, not “what is wrong?”.
This exists because two callers had already written it by hand, and
both were a restatement of what this type already knew.
epoch_log’s appender kept a usize beside its contract and drove it
at every push and every completion, which is precisely the
record-the-same-event-twice shape that
D-55 exists to remove. The other counted
Outstanding violations out of check_quiescent, allocating a Vec
to answer a question about a map’s length.
Sourcepub fn observe_buffer(&mut self, index: u32, outstanding: usize)
pub fn observe_buffer(&mut self, index: u32, outstanding: usize)
Record a registered buffer’s outstanding count, as
crate::RegisteredBuffers::outstanding reports it.
Sourcepub fn violations(&self) -> &[Violation]
pub fn violations(&self) -> &[Violation]
Violations seen so far, without asking about quiescence.
Sourcepub fn check_quiescent(&self) -> Vec<Violation>
pub fn check_quiescent(&self) -> Vec<Violation>
Every violation, including operations still outstanding and buffers still in use.
Call once everything is expected to have finished. An empty result is the claim that nothing was lost.
Sourcepub fn assert_quiescent(&self)
pub fn assert_quiescent(&self)
Panic with every violation if the contract was broken.
The convenience form for a test. Reports all violations rather than the first, because they are usually one cause and seeing only one of them invites fixing a symptom.
§Panics
If RingContract::check_quiescent finds anything.