Rustan Leino RiSE, Microsoft Research, Redmond MIT 5 June 2009 Joint work with: Peter Müller, ETH Zurich Jan Smans, KU Leuven
Atomicity Sequential reasoning within atomic sections Monitor invariants Assumed when monitor is acquired Checked when monitor is released Locking order Deadlock prevention Rely-guarantee reasoning Thread interference class Cell { int val; invariant val > 0; void Set( int v ) { lock( this ) { val := v; } } void Swap( Cell c ) { lock( this ) { lock( c ) { int t := val; val := c.val; c.val := t; } } }
Client-side locking One monitor protects lots of state Fine-grained locking One field protected by several monitors Thread-local and shared objects Transitions in both directions Dynamic changes of locking order class Node { int val; Node next; invariant next null val next.val; … } class List { Node head; void Reverse( ) { … } … }
Experimental language with focus on: Share-memory concurrency Static verification Key features Memory access governed by a model of permissions Sharing via locks with monitor invariants Deadlock checking, dynamic lock re-ordering Other features Classes; Mutual exclusion and readers/writers locks; Fractional permissions;Two-state monitor invariants; Asynchronous method calls; Memory leak checking; Logic predicates and functions; Ghost and prophecy variables
Every memory location has an associated permission A memory location is an (object, field) pair Permissions can be held by activation records An activation record is a particular invocation of a method Permissions can be transferred dynamically Exhale Inhale
Objects can be shared thread local shared, available shared, locked newshareacquire release unshare free
An available object can hold permissions A monitor invariant describes the state of an available object thread local shared, available shared, locked newshareacquire release unshare free monitor invariant is checked here
Every shared object o is associated with a value o.mu in the locking order The locking order is a dense lattice, where << denotes its strict partial order Locks have to be acquired in ascending order o.mu is set by the share statement o.mu can be changed by the reorder statement
Fork/join provide asynchronous calls Roughly: call o.M() Exhale Pre; Inhale Post fork o.M() Exhale Pre join o.M() Inhale Post
Predicates provide abstraction Predicates can also hold permissions Predicates are opened and closed, usually automatically
Owicki-Gries example solution due to Bart Jacobs
:List:List :Node:Node:Node:Node:Node:Node:Node:Node head tail current
Chalice has many features for shared-memory concurrency Verification via Boogie Permissions are flexible, but hard to debug with current interface