can it be updated? • What can it be updated to? How can it change? • Which aliases / modules / threads can update? Key problem: Answers to these questions are entirely implicit in most languages with mutation.
constrain? – Who/What/When/Where • How do we specify constraints? • How precise are constraints? • How much specification effort is required? • Level of guarantees: – Non-interference vs. invariants vs. functional correctness
in active use at Microsoft. • Some differences from formal system o e.g. first-class tasks, unstrict blocks • Millions of lines of code • Web server, MPEG decoder, … • Nearly all parallelism checked o Exceptions in runtime system • Anecdotally: More RI finds more bugs • Largest industrial use of such a system
Works (data race freedom!) • Multi-purpose – Even unsound checking helps: • Found data races even without parallelism checks – Single-threaded benefits: • Lightweight caller-callee contract • Found extra (expensive!) defensive copying
– Incremental refinement • Local interference checks – Safe concurrency from local type env. Properties – Local summaries of global behavior • T in readable T is irrelevant • Single model for sequential and concurrent – Single tool behaves the same in both cases – Concurrency as a modest extension • Concurrency primitives introduce no new concepts • Effective as a mental model
permissions to arbitrary relations • E.g. monotonically increasing counter – Generalize reference immutability – Express how state changes, not just whether • Exploit similarity between threads and aliases • Preserve data structure invariants
thread interference – Analyses for one can inspire analyses for the other • Actions through aliases can be seen as concurrent • Rely-Guarantee reasoning is good for threads – Summarizes possible interference – Good match for concurrent data structures
alias interference 2. Guarantee bounds actions through this alias 3. Stable predicates preserved by interference 4. Compatible aliases: if x == y, then x.G ⊆ y.R && y.G ⊆ x.R • Subsumes ML references! (OCaml, SML, etc.) – (Incremental)
– Does x := !x + 1 really increment? • But RGrefs subsume RI – Hijack ideas from RI for safe concurrency • General rely-guarantee works for concurrency – Restrict reasoning for exprs with ! – Exploit conversion: • (convert r) has same properties as r with weaker type
• RG is a good match for fine-grained structures: – All easy to specify as rely-guarantee references – O’Hearn et al. identify 18 invariants and step restrictions for lock-free set correctness  – Correctness for Michael-Scott Queue, Trieber stack has similar restrictions – Some similarity to Turon’s CaReSL   PODC 2010  POPL’13, ICFP’13
design space – Basic mutation ⊆ RI ⊆ … ⊆ RGref • Can build higher-level abstractions above it – E.g. deterministic execution: • Strict non-interference ⇒ determinism • Monotonicity is halfway to determinism
Modules also too coarse, but a good match for abstractions (CAP, Explicit Stabilization) • Individual objects too fine • “Reachability slices” (RI, RGref) seem better, but both too coarse and too fine • Regions? (DPJ, Local Rely-Guarantee) • Other granularities? Missing abstractions?
– RI, RGref, Ownership/Universe Types – specification matches point of mutation • Attached to method specifications – Sep. Logic, Chalice, others – Formalizes current best practices • Attached to regions – Local Rely-Guarantee, DPJ • Where else? Thread init/end?
memory concurrency – Single toolset with sequential & concurrent benefits – “Normal” code has a type in a more expressive system • Many questions remain – Granularity, integration, expressiveness, specification burden? – Non-interference / invariants / full correctness?