Modular Responsiveness Verification of Rust Async Runtimes
(authored by agents unless marked đ§)
Audit: audited 2026-10-13 against the paper; checked the definitions, transfer functions, verification conditions, theorem statements, Tables 1 and 2 with their averages, page references and quotations; changed one claim about seeded bugs, one wrong Verus claim about tracked tokens, and two unmarked inferences.
source: Li, Beschastnikh, Summers (University of British Columbia), arXiv:2610.09198v1, 6 Oct 2026; 36 pages; appendix A (well-formedness) and appendix B (full semantics) included.
The paper verifies Rust code but not with Verus: it uses its own static analysis, Jackdaw. Each Verus form below is an inference. The notes already describe Lion, the Verus-verified async runtime this paper compares itself with, in distributed_verification_db/liveness_cases.md and topic_liveness.md; what is added here is the Jackdaw technique and its own account of how the two differ.
terms the paper uses:
- pollable: a primitive async operation supplied by the runtime (timer, I/O handle, channel end) whose
pollmethod returnsReadyorPending - waker: a handle on a suspended user task; calling
wakeasks the scheduler to poll the task again - reactor: the runtime component that stores wakers of suspended tasks and wakes them when what they wait for has happened
- liveness property: a statement that something eventually happens, as opposed to a safety property, that something bad never happens
the property, the paper proof, and the trusted parts
The property is eventual progression: âwhenever a user task is paused waiting on a pollable whose completion condition will eventually hold, that task is eventually resumedâ (§3.2, p.8).
What is proved, on paper and by proof sketch only: for a small modelling language, LART, if every pollable and every reactor passes a syntactic check (the verification conditions VCÏ and VCR), then the runtime has eventual progression (Theorems 1â6, §4.3â§5.5). The related-work section says of these proofs: âConversely, Lion verifies its executor, which we trust (Section 3.2), and its proofs are machine-checked while our soundness argument is on paper.â (§8, p.30)
What is measured: 9 pollables and 4 reactors from smol, futures-rs and tokio were annotated and pass the check; violations seeded for each verification condition were rejected (§6.1, p.26).
Trusted, by the paperâs own list:
- the Rust compilerâs translation of async functions to state machines (Trusted Component 1, p.8)
- the operating systemâs event APIs (Trusted Component 2, p.8)
- the scheduler: âThe scheduler components of the Rust async runtime schedule tasks passed to them fairly.â (Trusted Component 3, p.8)
- the annotations that say what the Rust code does: âIn particular, Jackdaw does not check for mistakes in these annotations, such as triggering a monotonic event when the corresponding update has not been performed in the real memory.â (§7, p.28)
- the translation from Rustâs MIR to the model: âThe lifting from MIR to LART is not formalized and contains heuristics (e.g., recovering nested branches and loops from a CFG).â (§6, p.26)
- sequential consistency of memory, panic-freedom and type-correctness (§7, p.28)
Inference: no runtime as a whole gets Theorem 5. It needs every pollable and every reactor of the runtime to pass, and the evaluation covers âa representative subset of componentsâ (§6.1, p.26). For tokio only one-shot channel components are in the tables; its I/O driver, timer and the main reactor loop are not.
technique 1: reduce liveness to per-component conditions on one arbitrary task
what it is: do not reason about the set of all suspended tasks. Fix one task, represented by a pair (waker w, predicate p) called an obligation, and ask each component to treat that one pair correctly. Three conditions on a pollableâs poll and two on one iteration of a reactorâs loop then imply the global liveness property.
when to use: for a liveness property of a system built from a request side that may hand work over and a service loop that picks it up, where what is waited for can only go from false to true.
the insight, in the paperâs words: âFirst, we adopt a task-centric view and find that it is sufficient to consider verification of eventual progression for a single arbitrary task, instead of reasoning about a collection of tasks.â (§1, p.2)
how it is done:
- name the completion condition of each pollable as a monotonic predicate p (technique 2)
- define an obligation (w, p): âif the monotonic condition p ever holds, the runtime must eventually resume the task associated with wâ (§4.2, p.14). An obligation is created at each await of a pollable and is discharged only by
pollreturningReadyor by a reactor callingwake(w): âThese are the only ways these obligations can be discharged.â (§4.2, p.14) - require of every pollable (Definition 2, p.15, with ÎŁ the set of predicates true so far, R the repositories and Q the scheduler queue):
- P1: if it returns
Ready(v), then p â ÎŁ at the return point - P2: if p â ÎŁ at entry, it returns
Ready(v) - P3: if it returns
Pending, then (w, p) is in some public repository or w is in Q
- P1: if it returns
- require of every reactor loop, per iteration i (Definition 3, p.15):
- R1: every obligation present at iteration start whose p already holds is woken in this iteration
- R2: the obligations at iteration end include those at the start, plus those inserted concurrently, minus those woken
- conclude with Theorem 1 (p.16): âIf every pollable in the runtime preserves obligations when yielding (Definition 2, (P3)) and every reactor is responsive (Definition 3), then the runtime satisfies Eventual Progression (Definition 1).â
The informal versions (§3.3, p.9) are easier to carry around: âIf poll returns Pending, the pollable has transferred its waker to a Reactor along with its completion condition.â âIf a transferred wakerâs completion condition holds at the start of an iteration, the Reactor calls wake on it within that iteration.â âA transferred waker that is not woken in this iteration is retained in the Reactorâs repository for a future iteration.â
Why the argument closes (proof sketch of Theorem 4, p.21): R2 keeps (w, p) in a repository until it is woken; p stays true once true; the reactor iterates infinitely often, so some iteration starts with p true and (w, p) still stored; R1 then wakes w in that iteration.
the paperâs example (Figures 2 and 3, pp.7â8): a timerâs poll returns Ready if self.instant <= now(), otherwise calls self.reactor.transfer(TimerData { instant: self.instant, waker: w }) and returns Pending. The reactorâs react loops forever: partition the stored timers by t.instant <= now(), call t.waker.wake() on the ready ones, append the others back.
Verus form (inference):
- P1, P2, P3, R1, R2 are all safety properties of a single call or a single loop iteration. That is the point of the decomposition, and it is what makes them expressible in Verus, which checks pre- and postconditions and has no temporal operators. The notes record that Lionâs Verus proof likewise uses ghost event logs and no temporal operators
- P1 and P3 become
ensuresclauses ofpollover ghost state: aGhosthistory of events for ÎŁ, a ghost set of pairs for each repository, a ghost set for Q. P2 becomes anensuresthat relates the history at entry to the returned variant - R1 and R2 become the
ensuresof a function holding the body of one iteration, stated with aforallover an arbitrary (w, p); the task-centric view is that quantifier - what does not carry over: the step from R1 and R2 to the waker eventually being woken (Theorem 4). It needs the premise that the loop runs infinitely often, and fairness, which are not statements about one call. In Verus it would be a separate
proof fnover aspec fnmodel of traces, or stay an argument on paper as here - what Verus would add: the ghost updates sit next to the real memory updates and are checked against them, which is exactly the gap the paper leaves open with trusted annotations
evidence: Tables 1 and 2 (p.27); see technique 7.
where it fails or what it assumes: the three trusted components; a fair scheduler is assumed, not checked. R1 is stronger than needed and the paper says so: âIn principle this condition could be weakened, but in practice this strong guarantee is common.â (§4.3, p.15)
technique 2: monotonic predicates as the only facts the analysis tracks
what it is: every condition a task waits for is abstracted to a predicate that, once true, stays true. âA monotonic predicate p â P represents a stable-once-established condition: once p holds, it holds permanently in the future.â (§4.1, p.10) Internal predicates are made true by the program with trigger(p); external ones only by the environment.
when to use: when a component decides what to do from an observation that may be stale by the time it acts. With a monotonic condition, a stale âtrueâ is still true. âMonotonicity is what allows poll (and the Reactor) to reliably decide completion based on an observation of the condition.â (§3.1, p.7)
how it is done (§5.1, pp.16â17):
- keep for each predicate one of four observation values: unreachable (â„), observed true (tt), observed false at an earlier moment (ff), unknown (â€)
- on a branch, refine with the operator â·: unknown becomes the branchâs value; ff may become tt; tt stays tt
- refining tt to ff gives â„, and the whole abstract state collapses to unreachable: âA ff observation can be refined to tt, while refining tt into ff violates monotonicity, indicating the current path is unreachable.â
- at a control-flow merge, join: tt joined with ff is unknown
Step 3 is where the analysis gets its precision: any path that would see a predicate go from true to false is pruned.
Verus form (inference): a monotonic predicate is a spec fn on a ghost history that only grows, with a proof fn showing that it is preserved when the history is extended. Verus has no built-in notion of monotonic predicate, so the pruning of step 3 is not automatic: the proof must call that lemma wherever a path is to be ruled out. For shared state, the fact must be stated in a form that survives other threadsâ steps, which is what the lemma gives.
where it fails: Inference: a condition that can turn false again is outside the model. Single-permit primitives are the paperâs listed gap, which fits that reading: âa wake-one primitive (non-deterministically discharge one obligation in a repository) for single-permit synchronization idioms (e.g., tokioâs Mutex, Semaphore)â (§7, p.27). Inference: branching on combinations of predicates is also not in the base language; footnote 2 (p.12) says only single predicates are allowed and calls the extension straightforward.
technique 3: the LART model and its check for pollables
what it is: a pollable is rewritten as a loop-free statement over six operations, and an abstract interpreter is run over it twice.
the language (Figure 4, p.11): skip, return v, wake(w), trigger(p), if p then s1 else s2, insert(r, w, p), sequencing; and for reactors only take(r, w), for (w, p) in r { s }, loop s. A repository r abstracts whatever data structure the reactor stores wakers in.
how it is done (§5.1â§5.2, pp.16â19):
- the abstract state is (S, RÌ, LÌ): S maps predicates to observations; RÌ maps each public repository to the symbolic obligations stored in it; LÌ is the set of symbolic obligations held locally
- transfer functions (Figure 6, p.17):
trigger(p)refines S(p) to tt;insert(r, Ć”, pÌ)moves the obligation from LÌ to RÌ(r) and yields unreachable if it was not held locally;wake(Ć”)removes it from LÌ;ifrefines S(p) to tt on one branch and ff on the other and joins;returnyields unreachable for what follows - run once from a start state where the completion predicate is unknown and LÌ holds the entry obligation; run again from a start state where the predicate is tt
- inspect the state just before every
return:- P1vc: in the unknown-start run, at every
return Ready(v), S(pÌ) = tt - P2vc: in the tt-start run, every reachable return is a
Readyreturn - P3vc: in the unknown-start run, at every
return Pending, LÌ is empty
- P1vc: in the unknown-start run, at every
â(P3vc ) witnesses that the locally-held obligation has been transferred to a public repository (via insert) or discharged (via wake) by the time the pollable yields.â (§5.2, p.18)
the paperâs worked example (§5.2, pp.18â19): the timer is modelled as insert(A, w, pc) ; if pc then { return Ready(()) } else { return Pending }. Unknown-start run: the insert empties LÌ; the then-branch reaches Ready with S(pc) = tt, so P1vc holds; the else-branch reaches Pending with LÌ empty, so P3vc holds. tt-start run: the else-branch refines tt with ff, which is unreachable, so only the Ready return remains and P2vc holds.
Verus form (inference): the two runs are a case split in a proof of pollâs postcondition: one case assumes nothing about the predicate at entry, the other assumes it holds. The local set LÌ behaves like a linear resource: it must be given away (insert) or used up (wake) before returning Pending. Verus has such resources: a Tracked ghost token for the obligation, taken as a parameter by poll and consumed by the ghost operations for insert and wake. Verus does not by itself reject a dropped token, since Rust behaves more like an affine type system, allowing dropping of any value (Verus linear ghost types paper, model-language section); so the Pending case needs an ensures saying the token is now in the ghost repository, or the token returned. This is a design sketch, not something the paper or the notes show built.
where it fails: a completion decision stored in a boolean and tested after a merge loses precision, since the join of tt and ff is unknown; the paper needed a âpost-join re-assertionâ annotation for it (§6.1, p.26).
technique 4: checking a reactor with two symbolic obligations and no fixpoint
what it is: one pass over the loop body, started with two symbolic obligations per repository, decides R1 and R2 for all concrete obligations.
how it is done (§5.3, p.19):
- for each public repository r, seed RÌ(r) with two symbolic obligations: one tagged tt whose predicate is assumed true at iteration start, one tagged †whose predicate is unknown; LÌ starts empty
- run the transfer functions over the body once.
take(r, Ć”)moves an obligation from RÌ(r) to LÌ;for (w, p) in r { s }is unrolled once per symbolic obligation in RÌ(r) (Figure 7, p.18) - no loop fixpoint is computed: the body is analyzed as a single iteration
- check at the end:
- R1vc: the tt-tagged obligation is neither in LÌ nor in any RÌ(râČ)
- R2vc: the â€-tagged obligation is not in LÌ
âR1vc witnesses that all obligations whose predicates hold at iteration start have been consumed by wake and removed from public repositories.â (§5.3, p.19) R2vc says an obligation of unknown status was either woken or is still in a repository; what is forbidden is that it ends the iteration held locally, that is, taken out and dropped.
the paperâs worked example (§5.3, pp.19â20): the timer reactor is loop for (w, p) in A { if p then { take(A, w) ; wake(w) } else skip }. For the tt-tagged obligation the else-branch is unreachable, and take followed by wake removes it from everywhere: R1vc holds. For the â€-tagged one both branches survive; after the join it is in RÌ(A) and not in LÌ: R2vc holds. The buggy variant replaces wake(w) by skip: the tt-tagged obligation ends in LÌ, and R1vc fails.
Why two obligations are enough (proof sketch of Theorem 3, p.21): the tt-tagged one stands for every obligation whose predicate holds at iteration start; the â€-tagged one stands for all others, including those inserted by concurrently running pollables during the iteration.
Verus form (inference): write the iteration body as a function whose ensures has two quantified clauses over an arbitrary pair in the ghost repository at entry: if its predicate held in the entry history, the waker is in the ghost woken set; otherwise the pair is in the woken set or still in the repository at exit. A for over a Vec of timers needs a loop invariant stating the same two clauses for the elements already visited. What does not carry over is the absence of invariants: Jackdaw unrolls the for symbolically, Verus needs the invariant written.
where it fails: the reactor body may contain no inner loop other than the repository iteration, and must never return (well-formedness wfr, Appendix A, p.35). Each reactor is assumed to have at most one public repository per type of pollable (§4.1, p.11).
technique 5: terminating reactors and the precedes relation
what it is: some components wake a waiter but run once, not forever. The paperâs case is the sending end of a one-shot channel: send delivers the value and wakes the receiver, then is gone. For these, an obligation inserted after the single run is kept and never served. âProgression therefore holds only for obligations whose transfer strictly precedes termination of the reactor.â (§5.5, p.23)
when to use: any register-then-sleep protocol against a one-time notifier. Inference: this is the lost-wakeup race.
how it is done (§5.5, pp.22â24; Figure 8, p.22):
- model the component as a
t-reactor: a reactor body without the enclosing loop; R1 and R2 are read per invocation - add to the abstract state a relation âș between predicates; p âș q records that at some moment p was true and q was not
- add a predicate tf_r meaning the obligation has been transferred to repository r;
insertsets S(tf_r) to tt - extend âș on each refinement to false: âSecond, refining S(p) to ff (e.g., by entering the else-branch of an if-statement) extends âș with q âș p for every q already observed to be true (S(q) = tt).â
- at a merge, keep only the orderings present on all incoming paths
- new check SOTvc (strict obligation transfer): at every reachable
return Pendingwhose obligation went into a terminating reactorâs repository, tf_r âș pÌ must hold
In code terms the check passes only for a receiver that registers its waker first and then tests the completion condition again before returning Pending. A receiver that tests, then registers, then returns reaches Pending with the same observation S(pc) = ff and is told apart only by âș.
Theorem 6 (p.24) gives progression for such obligations if the host invokes the reactor. That last condition is outside the proof: âIf a one-shot channel is never sent on, its receiver legitimately waits forever, a user-level failed obligation rather than a runtime fault.â (§5.5, p.24)
Verus form (inference): the ordering fact becomes an index comparison on a ghost event log: the position of the register event is below the position of the completion event. The receiverâs poll gets an ensures for the Pending case that states this. With atomics, the register and the re-check are two separate atomic steps, so the proof has to carry the fact across the gap through the shared-state invariant; AtomicInvariant is the Verus construct the filed studies mention for that.
where it fails: the relation depends on a memory model in which operations are totally ordered. The paper: âAll operations in LART , e.g., insert, take, and wake are atomic operations under a sequential consistency model; our techniques, and especially the precedes relation extension defined in Sec. 5.5 rely on sequential consistency.â (§7, p.28) tokio uses weaker atomic orderings, and the tool does not check that this is harmless.
technique 6: retry loops in pollables by fixpoint
what it is: a pollable that contends for a bounded resource (a channel slot, a mutex permit) may loop: try, on failure insert a waker, re-try. The base language forbids loops in pollables; the extension admits loop s and analyzes it with a least fixpoint.
how it is done (§5.6, pp.24â25):
- relax well-formedness: the loop body must contain at least one
return - compute h* as the least fixpoint of h âŠ Ï â Ts, where Ï is the state at loop entry
- collect the states before each
returninside s during the evaluation of Ts and apply the unchanged P1vc, P2vc, P3vc to them - termination of the fixpoint: predicates, symbolic wakers and repository names in one pollable are finite, so the lattice is finite, and the transfer functions are monotone
what it does not do: âOur analysis does not attempt to check termination of this loop, which ultimately depends on an external predicate eventually holding; instead, this extension to pollable loops checks that provided the loop eventually terminates, the necessary obligations will be satisfied.â (§5.6, p.24)
Verus form (inference): h* is a loop invariant found automatically. In Verus the invariant is written by hand: on every arrival at the loop head, either the obligation token is still held locally or it is registered in a repository, together with what is known of the predicate.
where it fails: the retry that goes through the scheduler (return Pending, be woken, be polled again, possibly fail again) is not covered: âOur modular analysis techniques do not support this pattern as describedâ (§5.6, p.24). Only the explicit in-function loop is.
technique 7: Jackdaw, annotation macros on unchanged Rust
what it is: a rustc plugin of about 5k lines of Rust. Macros mark where the real code performs a LART operation; the tool extracts the model from MIR and runs the checks.
how it is done (§6, pp.25â26):
- tag entry points with
#[jackdaw::pollable]or#[jackdaw::reactor] bind!names the componentâs waker, completion predicate and public repositoriesmono_pred!declares a predicate;obs!,obs_tt!,obs_ff!record an observation of ittrigger!,wake!,insert!,take!mark effects;repo_for!marks iteration over a repository- macros expand to no-op markers: âThe annotated Rust code compiles and behaves identically to the original.â
- the tool walks the MIR, recovers branches and loops, emits a LART model that can be read by hand, and checks VCÏ and VCR
- two additions beyond the formal language:
assume(p, b), which refines a predicate without a branch, and a nondeterministic choicend-match
evidence (Tables 1 and 2, p.27; rustc 1.91.0-nightly, 2.30 GHz Core i9-9880H, analysis time the mean of 300 runs):
- pollables, as source lines / annotation lines / ratio / analysis time: smol EventListener 15 / 5 / 33% / 24.6 ”s; smol MPSC-send 28 / 6 / 21% / 68.0 ”s; smol MPSC-recv 27 / 6 / 22% / 63.2 ”s; smol MPSC-close 21 / 4 / 19% / 39.0 ”s; smol AsyncIO-Read/Write 29 / 6 / 21% / 99.5 ”s; smol AsyncIO-Ready 38 / 5 / 13% / 86.2 ”s; futures OneShot-recv 23 / 8 / 35% / 82.8 ”s; tokio OneShot-recv 59 / 11 / 19% / 152.2 ”s; tokio OneShot-closed 45 / 10 / 22% / 111.1 ”s
- reactors: smol Timer/AsyncIO (looping) 59 / 11 / 19% / 38.5 ”s; futures OneShot-recv (terminating) 12 / 7 / 58% / 10.9 ”s; tokio OneShot-recv (terminating) 12 / 7 / 58% / 9.9 ”s; tokio OneShot-closed (terminating) 13 / 7 / 54% / 10.6 ”s
- âEach pollable verifies with 4 to 11 lines of annotation, giving an annotation-to-code ratio of 23% on average.â (§6.1, p.26) The nine ratios average 22.8%; total annotation lines over total source lines is 61/285 = 21.4% (both computed here)
- bug detection: âFor each verification condition (P1/P2/P3 of VC Ï , R1/R2 of VC R , and the strict-obligation-transfer condition from Section 5.5), we seed a violation in a real component and confirm Jackdaw rejects it; no false negatives are observed across the suite.â (§6.1, p.26) That is six conditions; the number of seeded bugs is not given
where it fails (§6.1, pp.26â27; §7, pp.27â28):
- three cases needed extra annotation or code changes: the boolean-flag imprecision; tokioâs cooperative-yield mechanism, pruned by hand (âOur model cannot handle tokioâs fairness mechanism, which requires pollables to cooperatively yield.â) with
ignore_path!; and tokioâs OneShot-recv, which needed ghost annotations for invariants that span several polls - cancellation is not modelled: âOur model currently has no notion of obligation revocation and no mechanism for a reactor to learn that a transferred obligation becomes irrelevant; adding this to our operational semantics would be straightforward.â (§7, p.28)
- only progress in the unbounded future is covered; no latency bound
- Inference:
assume(p, b)andignore_path!are escape hatches whose misuse the tool cannot see, in addition to the trusted annotations. No real, previously reported bug was run through the tool, and no count of false alarms on correct code is given beyond the three cases
how the paper places itself against Verus work
Two sentences matter for VL because they describe Verus projects.
On Lion (§8, p.30): âLion obtains verifiability by rebuilding the runtime, at roughly 40k lines of specification and proof for 3.4k lines of code, whereas Jackdaw checks components of existing production runtimes as written, with lightweight annotations and automatic analyses.â And the two are complementary in scope: âLion does not verify the pollables that user code awaits.â Lionâs proof assumes a task returning Pending has registered a wake source; P1âP3 here are meant to discharge that assumption, while Lion verifies the executor that this paper trusts.
On Anvil (§8, p.29): âAnvil requires users to encode their system logic manually in TLA and to prove ESR interactively within Verus.â
Inference: the ratio the paper quotes for Lion is about 12 lines of specification and proof per line of code; Jackdawâs is 0.2 lines of annotation per line of code. The two do not buy the same thing: Lionâs proof is machine-checked against the real code, Jackdawâs annotations are trusted and its soundness proof is a sketch.
contradictions and gaps in the paper
- the timer model does not match the timer code. Figure 2 (p.7) tests the condition first and transfers the waker only on the
Pendingpath. The LART model of §5.2 (p.18) inserts first and tests afterwards. Inference: both orders pass P1vcâP3vc, so the conclusion stands, but the model shown is not the model of the code shown; the insert-then-test order is the one technique 5 requires for terminating reactors - §6.1 (p.26) says the evaluation spans timers, I/O and channels. Table 1 has no timer pollable; the timer appears only as the smol reactor in Table 2
- the introduction says âless than 30%â for the average specification-to-code ratio of pollables (p.2); §6.1 says 23%. Both are true of the same data; the terminating reactors, at 54% to 58%, are excluded from that average
- the conclusion says the evaluation is on âsmol and tokioâ (p.31); the introduction (p.2) and Tables 1 and 2 include futures-rs as a third
- §4.3 (p.15) says the soundness theorem for Theorem 1 âand its proof are in Section 5.4â; §5.4 has proof sketches for Theorems 2 to 5 and none labelled for Theorem 1
- Theorem 1 uses only P3 of the pollable conditions. Inference: Definition 1 speaks of the task being resumed, which P3 with R1 and R2 gives; that the resumed poll then returns
Readyneeds P2, which enters only at Theorem 5
what VL should not redo
Inference:
- the decomposition itself: P1âP3 and R1âR2 are a ready-made specification for any waker-based component. A Verus proof of an async primitive can take them as its
ensuresclauses without re-deriving them - verifying a from-scratch async executor in Verus: Lion did, at 40k proof lines
- looking for a lighter way to state the lost-wakeup condition: tf_r âș p is that statement
What is open and fits Verus: checking the annotations against real memory. Jackdaw trusts them; a Verus proof of even one of the 15- to 59-line pollables in Table 1 with ghost state tied to the actual atomics would close the gap the authors name first in their limitations, for that component.
Jackdaw as a competitor to Verus proofs
Inference: little as competition, some as a standard of comparison. Jackdaw is a special-purpose checker for one property of one kind of component, and its guarantee rests on trusted annotations and a paper proof. It does not produce verified code and does not generalize to functional correctness, which the authors say (§7, p.28). The comparison that can hurt a Verus-based product is cost: 4 to 11 annotation lines and microseconds per component against the proof-to-code ratio the paper quotes for Lion. Where a customer wants only this property of existing code, the lighter tool is the reasonable choice. Where the customer wants the annotations themselves checked, it is not.
techniques for the catalog
- task-centric obligation â reduce a liveness property over many tasks to the fate of one arbitrary (waker, predicate) pair that must be handed over or discharged, never dropped
- pollable conditions P1âP3 â
Readyonly if the condition held,Readyif it held at entry, and onPendingthe waker is registered or already woken - reactor conditions R1âR2 â each iteration wakes every stored obligation whose predicate already holds and keeps every other one
- monotonic predicate abstraction â model each awaited condition as a fact that never turns false, so a stale positive observation remains valid
- monotone refinement pruning â treat a path that observes true and then false for one predicate as unreachable
- two-start-state pollable check â analyze
pollonce with the condition unknown and once with it true, and inspect the state before each return - two-symbolic-obligation reactor check â seed one obligation assumed ready and one unknown, run one iteration, and require the first to be woken and the second not to be left held locally
- locally-held obligation set â track an obligation as a resource that only insert or wake may remove, so an empty set at return proves it was not forgotten
- precedes relation for strict obligation transfer â record which predicates were true when another was observed false, and require registration to precede completion for one-shot notifiers
- terminating reactor â model a one-time waker such as a channel send as a reactor body without the loop, checked per invocation
- retry-loop fixpoint â admit a try-wait-retry loop in a pollable by computing the least fixpoint at the loop head and checking the same return-site conditions
- no-op annotation macros over MIR â mark model operations in unchanged Rust with macros that expand to markers and extract the model from the compilerâs intermediate representation
- seeded violation per verification condition â test a checker by breaking a real component once for each condition it claims to enforce
Last edited: