Higher-Order Concurrent Separation Logic: Why and How Lars Birkedal Aarhus University Nijmegen, Netherlands Dec, 2015 Modular Reasoning about Higher-Order Concurrent Imperative Programs 1 / 33
Introduction Goal ◮ Program logics for modular reasoning about partial correctness of higher-order, concurrent, imperative code programs. 2 / 33
Outline 1. Why higher-order logic + guarded recursion is useful for expressing such modular specs ◮ via example of layered and recursive abstraction. 2. Why impredicative protocols to govern shared state ◮ via example verification of lock implementation ◮ invariants to enforce protocols ◮ monoids to express protocols (ownership) 3. How the logic is modelled and showed sound (key ideas) ◮ BI-hyperdoctrine over ultra-metric spaces, Kripke model with recursively defined worlds. 4. Overview of resources to learn more ◮ tutorial material ◮ papers: iCAP [ESOP-2014] and Iris [POPL-2015] ◮ paper on formalization: ModuRes Library [ITP-2015] 5. Overview of features in iCAP and Iris not covered today 3 / 33
Higher-Order Programming ◮ Programming features ◮ HO functions ◮ Interfaces in OO languages ◮ Function Pointers in low-level languages ◮ for ◮ Building libraries ◮ Returning libraries ◮ Parameterization ◮ Point: ◮ Features important for modularizing large programs ◮ Allow programming relative to unknown code ◮ Goal: Logics and Models that support correspondingly modular specifications of code. 4 / 33
Example: Layered and Recursive Abstractions ◮ Modular library specifications that supports layering of abstractions and recursive abstractions. 5 / 33
Example: Layered and Recursive Abstractions ◮ Modular library specifications that supports layering of abstractions and recursive abstractions. LinkedList HashMap Lock 5 / 33
Example: Layered and Recursive Abstractions ◮ Modular library specifications that supports layering of abstractions and recursive abstractions. M 2 LinkedList HashMap M 1 Lock 5 / 33
Recursive Abstractions Reentrant Event Loop Library handler ( ) ; delegate void IEventLoop { interface loop ( ) ; void s i g n a l ( ) ; void void when ( handler f ) ; } 6 / 33
Recursive Abstractions Reentrant Event Loop Library handler ( ) ; delegate void IEventLoop { interface loop ( ) ; void s i g n a l ( ) ; void void when ( handler f ) ; } Event handlers are allowed to emit events! 6 / 33
Recursive Abstractions A library that allows us to close Landin’s Knot / perform Reentrant Event Loop Library recursion through the store. handler ( ) ; delegate void IEventLoop { interface loop ( ) ; void s i g n a l ( ) ; void void when ( handler f ) ; } Event handlers are allowed to emit events! 6 / 33
Recursive Abstractions A library that allows us to close Landin’s Knot / perform Reentrant Event Loop Library recursion through the store. handler ( ) ; delegate void IEventLoop { interface loop ( ) ; void s i g n a l ( ) ; void void when ( handler f ) ; } Event handlers are Realistic examples of this form: allowed to emit events! libevent, Node.js, Twisted, ... C5, GUI libraries, Joins library, ... 6 / 33
A Modular Lock Specification ∃ isLock , locked : Val × Prop → Prop . ∀ R : Prop . { R } { isLock(ret , R) } new Lock() { isLock(x , R) } { locked(x , R) ∗ R } x.Acquire() { locked(x , R) ∗ R } { isLock(x , R) } x.Release() ∀ x : Val . isLock(x , R) ⇔ isLock(x , R) ∗ isLock(x , R) 7 / 33
A Modular Lock Specification Standard sep. logic lock specification The resource invariant R describes the resources protected by the lock. ∃ isLock , locked : Val × Prop → Prop . ∀ R : Prop . { R } { isLock(ret , R) } new Lock() { isLock(x , R) } { locked(x , R) ∗ R } x.Acquire() { locked(x , R) ∗ R } { isLock(x , R) } x.Release() ∀ x : Val . isLock(x , R) ⇔ isLock(x , R) ∗ isLock(x , R) 7 / 33
A Modular Lock Specification ∃ isLock , locked : Val × Prop → Prop . ∀ R : Prop . { R } { isLock(ret , R) } new Lock() { isLock(x , R) } { locked(x , R) ∗ R } x.Acquire() { locked(x , R) ∗ R } { isLock(x , R) } x.Release() ∀ x : Val . isLock(x , R) ⇔ isLock(x , R) ∗ isLock(x , R) 7 / 33
A Modular Lock Specification Third-order quantification. ∃ isLock , locked : Val × Prop → Prop . ∀ R : Prop . { R } { isLock(ret , R) } new Lock() { isLock(x , R) } { locked(x , R) ∗ R } x.Acquire() { locked(x , R) ∗ R } { isLock(x , R) } x.Release() ∀ x : Val . isLock(x , R) ⇔ isLock(x , R) ∗ isLock(x , R) 7 / 33
A Modular Lock Specification ∀ R : Prop . ∃ isLock , locked : Val → Prop . { R } { isLock(ret) } new Lock() { isLock(x) } { locked(x) ∗ R } x.Acquire() { locked(x) ∗ R } { isLock(x) } x.Release() ∀ x : Val . isLock(x) ⇔ isLock(x) ∗ isLock(x) 7 / 33
A Modular Lock Specification Second-order quantification. ∀ R : Prop . ∃ isLock , locked : Val → Prop . { R } { isLock(ret) } new Lock() { isLock(x) } { locked(x) ∗ R } x.Acquire() { locked(x) ∗ R } { isLock(x) } x.Release() ∀ x : Val . isLock(x) ⇔ isLock(x) ∗ isLock(x) 7 / 33
A Modular Lock Specification This specification might suffice for layering of abstractions, but not for all recursive abstractions. ∀ R : Prop . ∃ isLock , locked : Val → Prop . { R } { isLock(ret) } new Lock() { isLock(x) } { locked(x) ∗ R } x.Acquire() { locked(x) ∗ R } { isLock(x) } x.Release() ∀ x : Val . isLock(x) ⇔ isLock(x) ∗ isLock(x) 7 / 33
Recursive Abstractions Event Loop Memory Safety Specification ∃ eloop : Val → Prop . { emp } { eloop(ret) } new EventLoop() { eloop(x) } { eloop(x) } x.loop() { eloop(x) } { eloop(x) } x.signal() { eloop(x) ∗ P } { eloop(x) } x.when(f) ∀ x : Val . eloop(x) ⇔ eloop(x) ∗ eloop(x) where P = f �→ { emp }{ emp } 8 / 33
Recursive Abstractions Event Loop Memory Safety Specification ∃ eloop : Val → Prop . { emp } { eloop(ret) } new EventLoop() { eloop(x) } { eloop(x) } x.loop() { eloop(x) } { eloop(x) } x.signal() { eloop(x) ∗ P } { eloop(x) } x.when(f) ∀ x : Val . eloop(x) ⇔ eloop(x) ∗ eloop(x) where P = f �→ { emp }{ emp } Event handler must run without any resources but emitting an event requires an eloop(x) resource! 8 / 33
Recursive Abstractions Reentrant Event Loop Memory Safety Specification ∃ eloop : Val → Prop . { emp } { eloop(ret) } new EventLoop() { eloop(x) } { eloop(x) } x.loop() { eloop(x) } { eloop(x) } x.signal() { eloop(x) ∗ P } { eloop(x) } x.when(f) ∀ x : Val . eloop(x) ⇔ eloop(x) ∗ eloop(x) where P = f �→ { eloop(x) }{ eloop(x) } 8 / 33
Recursive Abstractions Verifying a lock-based event loop implementation ◮ Since we are interested in memory safety, eloop has to specify the memory footprint of the event loop. 9 / 33
Recursive Abstractions Verifying a lock-based event loop implementation ◮ Since we are interested in memory safety, eloop has to specify the memory footprint of the event loop. ◮ Since an event loop can call handlers, its footprint include the footprint of its handlers. ◮ Since handlers can signal events, the footprint of handlers include the footprint of their event loop. 9 / 33
Recursive Abstractions Verifying a lock-based event loop implementation ◮ Since we are interested in memory safety, eloop has to specify the memory footprint of the event loop. ◮ Since an event loop can call handlers, its footprint include the footprint of its handlers. ◮ Since handlers can signal events, the footprint of handlers include the footprint of their event loop. ◮ The footprint of an event loop is thus recursively defined. 9 / 33
Recursive Abstractions Verifying a lock-based event loop implementation ◮ Imagine an implementation that maintains a set of signal handlers and a set of pending signals, protected by a lock: EventLoop : IEventLoop { class Lock lock ; private Set < handler > h a n d l e r s ; private Set < s i g n a l > s i g n a l s ; private . . . } ◮ Tying Landin’s Knot using a reference protected by a lock. 10 / 33
Recursive Abstractions Verifying a lock-based event loop implementation ◮ The footprint of an event loop is thus recursively defined and the recursion “goes through the lock” . 11 / 33
Recursive Abstractions Verifying a lock-based event loop implementation ◮ The footprint of an event loop is thus recursively defined and the recursion “goes through the lock” . ◮ We define eloop using guarded recursion and the third-order isLock representation predicate: eloop = fix ( λ eloop : Val → Prop . λ x : Val . ∃ l . x . lock �→ l ∗ isLock( l , ∃ y , z , A , B . set (y , A ) ∗ set (z , B ) ∗ x . handlers �→ y ∗ x . signals �→ z ∗ ∀ a ∈ A . ⊲ a �→ { eloop(x) }{ eloop(x) } )) 11 / 33
Recursive Abstractions Verifying a lock-based event loop implementation ◮ The footprint of an event loop is thus recursively defined and the recursion “goes through the lock” . ◮ We define eloop using guarded recursion and the third-order isLock representation predicate: eloop = fix ( λ eloop : Val → Prop . λ x : Val . ∃ l . x . lock �→ l ∗ isLock( l , ∃ y , z , A , B . set (y , A ) ∗ set (z , B ) ∗ x . handlers �→ y ∗ x . signals �→ z ∗ ∀ a ∈ A . ⊲ a �→ { eloop(x) }{ eloop(x) } )) 11 / 33
Recommend
More recommend