automatically verifying concurrent queue algorithms

Post on 04-Feb-2016

53 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Automatically Verifying Concurrent Queue Algorithms. Eran Yahav Mooly Sagiv School of Computer Science Tel-Aviv University {yahave,msagiv}@post.tau.ac.il http://www.cs.tau.ac.il/~yahave. Automatically Verifying Partial Correctness of Software using Abstract Interpretation. - PowerPoint PPT Presentation

TRANSCRIPT

1

Eran Yahav Mooly SagivSchool of Computer Science

Tel-Aviv University

{yahave,msagiv}@post.tau.ac.ilhttp://www.cs.tau.ac.il/~yahave

Automatically Verifying Concurrent Queue Algorithms

2

Automatically Verifying Partial Correctness of Softwareusing Abstract Interpretation

Operates on the program source Fully automatic Conservative results

No errors are reported partial correctness is guaranteed

But may produce “false alarms”Makes the results non-useful

The Challenge avoid false alarms

3

Concurrent Queues

A common component of concurrent systems Operating systems

A large number of suggested algorithmsHard to get right

[Stone90] – races + items may be lost [Valois94] – items may be lost …

Mostly given without formal proof of correctness

4

Automatically Verifying Concurrent Queue Algorithms?

Support the following Concurrency Dynamic allocation/deallocation of objects Destructive updates Heap references Unbounded storage (heap) Dynamic allocation/deallocation of threads

Handling references with sufficient precision for establishing correctness properties Preceding pointer-analysis phase usually

insufficient

5

Example [Michael&Scott PODC96]

public void enqueue(Object value) { e_1 node = new QueueItem() // allocate queue nodee_2 node.val = value // copy enqueued value into nodee_3 node.next.ref = NULL e_4 while(true) { // Keep trying until done e_5 tail = this.Tail // get Tail.ptr and Tail.count e_6 next = tail.ref.next // get next ptr and count e_7 if (tail == this.Tail) { // are tails consistent?e_8 if (next.ref == NULL) { // was tail pointing to last node?e_9 if CAS(tail.ref.next,

next, <node, next.count+1>) { // try connecte_10 break // Enqueue is done. Exit loop e_11 }e_12 } else { // tail wasn’t pointing to last

nodee_13 CAS(this.Tail, tail,<next.ref, tail.count+1>) // try advance taile_14 } e_15 } e_16 } e_17 CAS(this.Tail, tail, <node, tail.count+1>) //enqueue done. try swing tail e_18 }

6

Correctness

P1 The linked list is always connectedP2 Nodes are only inserted after the last

node of the linked listP3 Nodes are only deleted from the

beginning of the linked listP4 Head always points to the first node in

the linked listP5 Tail always points to a node in the linked

list

7

Rich Problem Expressive Formalism

We use first-order logic with transitive closure

Naturally define behavior of heap-manipulating programs Heap references Heap Reachability Threads as heap-allocated objects (and

scheduling)

Can also model integers

8

Plan

Vanilla verification attempt Program configurations Expressing safety properties Abstraction

Refining the vanilla solution Instrumentation predicates

Prototype implementation (TVLA/3VMC)

9

Configurations

A program configuration encodes global store program-location of every thread status of locks and threads

First-order logical structures used to represent program configurations

10

Configurations as First-order Logical Structures

First-order structure Objects - Individuals properties of objects – unary predicates relationship between objects – binary predicates Additional integrity constraints FO formulas

Object Type – unary predicates References between objects – binary predicates

Thread Program location – unary predicates

Integers Distinguished zero individual – unary predicate Successor relationship – binary predicate With integrity constraints corresponding to Peano axioms

11

at[e_2]

at[e_2]

zerosuccsucc succ

rv[node]

rv[node]

rv[this]

rv[this]

rv[Head] rv[next] rv[next] rv[next]

rv[Tail]

iv[Tail]

iv[Head]

iv[next]

iv[next]iv[next]

Concrete Configuration

rv[value]

rv[value]

12

Configurations

Predicates model properties of interest eq(v1,v2) is_T(v) { at[lab](t) : lab Labels } { rv[fld](o1,o2) : fld Fields }

{ iv[fld](o1,o2) : fld Fields } heldBy(l,t), blocked(t,l), waiting(t,l) zero(v), succ(v1,v2)

Can use the framework with different predicates

13

Safety PropertiesProperty Property Formula

P1 Tail reachable from Head

q:nbq,vt. rv[Tail](q,vt)

vh. rv[Head](q,vh) rv[next]*(vh, vt)

P2 Insert after last

q:nbq,ti:thread,vi,vt. at[e_18](ti) rv[node](ti,vi) rv[tail](ti,vt)

rv[this](ti,q) rv[next](vt,vi) rv[Tail](q,vi)

P3 Delete first

q:nbq,td:thread,vd,vh. at[d_19](td) rv[head](td,vd)

rv[this](td,q) rv[Head](q,vh) rv[next](vd,vh)

P4 Head first q:nbq,v,u. rv[Head](q,v) rv[next](u,v)

P5 Tail exists

q:nbq. v. rv[Tail](q,v)

14

at[e_2]

at[e_2]

zerosuccsucc succ

rv[node]

rv[node]

rv[this]

rv[this]

rv[Head] rv[next] rv[next] rv[next]

rv[Tail]

iv[Tail]

iv[Head]

iv[next]

iv[next]iv[next]

Tail Reachable from Head

Vh Vt

q:nbq,vt. rv[Tail](q,vt)

vh. rv[Head](q,vh) rv[next]*(vh, vt)

rv[value]

rv[value]

15

Abstract Program Model

Conservative representation of the concrete model

Use 3-valued logical structures to conservatively represent multiple 2-valued structures 1 = true 0 = false 1/2 = unknown A join semi-lattice, 0 1 = 1/2

Conservatively apply actions on abstract configurations

16

at[e_2]

at[e_2]

zerosuccsucc succ

rv[node]

rv[node]

rv[this]

rv[this]

rv[Head] rv[next] rv[next] rv[next]

rv[Tail]

iv[Tail]

iv[Head]

iv[next]

iv[next]iv[next]

Concrete Configurationrv[value]

rv[value]

17

zerosucc

rv[Head]

iv[Tail]

iv[Head]

Abstract Configuration

at[e_2]

rv[this]

iv[next]

rv[node]

rv[this]

rv[next]

succ

rv[Tail]

rv[value]

18

canonical Abstraction

Merge all nodes with the same unary predicate values into a single summary node

Join predicate valuesConverts a configuration of infinite size

into a 3-valued abstract configuration of bounded size

19

at[e_2]

at[e_2]

zerosuccsucc succ

rv[node]

rv[node]

rv[this]

rv[this]

rv[Head] rv[next] rv[next]

rv[Tail]

iv[Tail]

iv[Head]

iv[next]

iv[next]

Concrete Bad Configuration

q:nbq,vt. rv[Tail](q,vt) vh. rv[Head](q,vh) rv[next]*(vh, vt)

rv[value]

rv[value]

20

at[e_2]

zerosucc

rv[Head]

iv[Tail]

iv[Head]

Abstract Configuration

at[e_2]

rv[this]

iv[next]

rv[this]

rv[next]

succ

rv[Tail]

q:nbq,vt. rv[Tail](q,vt) vh. rv[Head](q,vh) rv[next]*(vh, vt)

rv[node]

rv[value]

21

Instrumentation

Refine the abstraction by recording additional information

Natural idea – record which property-formulae hold via nullary predicates Corresponds to predicate abstraction

More generally – record subformulae that hold for an individual via unary predicates Obtain (some) useful results without changing

set of predicates per program/property

22

Instrumentation Predicates

Predicate Intended Meaning Defining Formula

is[fld](o) o is shared by fld of at least two different objects

v1,v2.eq(v1,v2) rv[fld](v1,o) rv[fld](v2,o)

exists[fld](o)

There exists an object referenced by fld of o

v.rv[fld](o,v)

is_acquired(l)

l is acquired by some thread t.heldBy(l,t)

rt[fld,n](o) o is reachable from object referenced by fld using a path of next fields

t,ot.rv[fld](t,ot) rv[next]*(ot,o)

r_by[fld](o) o is referenced by fld of some object

v.rv[fld](v,o)

i_by[fld](o) o is int value of fld of some object

v.iv[fld](v,o)

23

r_by[node]rt[node,n]

is[this]r_by[this]rt[this,n]

r_by[node]rt[node,n]

r_by[head]rt[head,n]

r_by[next]rt[Head,n]

r_by[next]rt[Head,n]

r_by[next]r_by[Tail]rt[Head,n]rt[Tail,n]

at[e_2]

at[e_2]

zeroi_by[…]

succsucc succ

rv[node]

rv[node]

rv[this]

rv[this]

rv[Head] rv[next] rv[next] rv[next]

rv[Tail]

iv[Tail]

iv[Head]

iv[next]

iv[next]iv[next]

Instrumented Concrete Configuration

r_by[value]rt[value,n]

r_by[value]rt[value,n]

rv[value]

rv[value]

24

r_by[node]rt[node,n]

is[this]r_by[this]rt[this,n]

r_by[Head]rt[Head,n]

r_by[next]rt[Head,n]

r_by[next]r_by[Tail]rt[Head,n]rt[Tail,n]

at[e_2]

rv[node]

rv[this]

rv[Head] rv[next] rv[next]

rv[Tail]

iv[Tail]

iv[Head]

iv[next]iv[next]rv[next]

succ

succ

Instrumented Abstract Configuration

zeroi_by[…]

q:nbq,vt. rv[Tail](q,vt)

vh. rv[Head](q,vh) rv[next]*(vh, vt)q:nbq,v. rv[Tail](q,v) rt[Head,n](v)

r_by[value]rt[value,n]rv[value]

26

Best Conservative Interpretation

Concrete Representati

on

Concrete Representati

on

Concretization Abstraction

Operational Semantics

[|S|]

Abstract Representati

on

Abstract Representati

onAbstract Semantics

[|S|]

27

r_by[node]rt[node,n]

is[this]r_by[this]rt[this,n]

r_by[Head]rt[Head,n]

r_by[next]rt[Head,n]

r_by[next]r_by[Tail]rt[Head,n]rt[Tail,n]

at[e_2]

rv[node]

rv[this]

rv[Head] rv[next] rv[next]

rv[Tail]

iv[Tail]

iv[Head]

iv[next]iv[next]rv[next]

succ

succ

zeroi_by[…]

r_by[value]rt[value,n]rv[value]

Abstract Interpretation - Concretizatione_2 node.val = value

28

r_by[node]rt[node,n]at[e_2]

rv[node]

Abstract Interpretation - Concretizationr_by[value]rt[value,n]rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

e_2 node.val = value

29

Abstract Interpretation - update

r_by[node]rt[node,n]exists[val]

at[e_3]rv[node]

r_by[value]rt[value,n]r_by[val]rt[val,n]

rv[value]

r_by[node]rt[node,n]exists[val]

at[e_3]rv[node]

r_by[value]rt[value,n]r_by[val]rt[val,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

r_by[node]rt[node,n]exists[val]

at[e_3]rv[node]

r_by[value]rt[value,n]r_by[val]rt[val,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

r_by[node]rt[node,n]at[e_2]

rv[node]

r_by[value]rt[value,n]

rv[value]

…rv[val]

rv[value] rv[value]

e_2 node.val = value

30

r_by[node]rt[node,n]

at[e_2]

rv[node]

Abstract Interpretation - abstraction

r_by[value]rt[value,n]rv[value]

at[e_3]

rv[value]

rv[val]

rv[node]

r_by[value]rt[value,n]r_by[val]rt[val,n]

r_by[node]rt[node,n]exists[val]

at[e_3]

rv[value]

rv[val]

rv[node]

r_by[value]rt[value,n]r_by[val]rt[val,n]

r_by[node]rt[node,n]exists[val] r_by[node]

rt[node,n]

at[e_2]

rv[node]

r_by[value]rt[value,n]rv[value]

at[e_3]

rv[value]

rv[val]

rv[node]

r_by[value]rt[value,n]r_by[val]rt[val,n]

r_by[node]rt[node,n]exists[val]

31

Prototype Implementation

TVLA/3VMCfocuscoerceLimitations

only intraprocedural no optimizations used

No partial-order reduction

32

Experimental Results

Program Configs Space (MB)

Time (Sec)

Nbq_enqueue 1833 14.2 727

Nbq_dequeue 1098 5.3 309

Nbq_err1 36 0.1 11

Nbq_uni 17 0.1 3

Tlq_enqueue 982 10 6162

Tlq_dequeue 225 4.1 304

Twolockqn 975 7.5 577

Twolockq_err1 24 0.1 30

33

Conclusions

Common challenges of model checking and abstract interpretation False alarms Cost Scalability

Size Language features

34

Conclusions

Traditional MC Our Approachconcrete configuration

propositional FOTC

abstraction model extraction*abstract interpretation

control flowinternally represented

internally represented

materialization ? basic featureobject number a priori bounded unbounded

thread numberfixed or a priori bounded

unbounded

abstraction refinement

yes ?

35

Summary

Verified interesting safety properties of concurrent queues

Unbounded number of objects and threads

Dynamic allocation of objects and threads

36

http://www.cs.tau.ac.il/~yahave

37

Integer Representation - Concrete

zerosuccsucc succ

iv[y]iv[x]

y++

x == y ?

yes

zerosuccsucc succ

iv[y]

iv[x]x == y ?

no

38

Integer Representation – No Instrumentation

zerosuccsucc succ

iv[y]iv[x]

y++

x == y ?

yes

zerosuccsucc succ

iv[y]iv[x]

x == y ?

no

39

Integer Representation – No Instrumentation

zerosucc

iv[y]iv[x]

y++

x == y ?

maybe

x == y ?

maybe

succ

zerosucc

iv[y]iv[x]

succ

40

Integer Representation – With Instrumentation

zerosuccsucc succ

iv[y]iv[x]

y++

x == y ?

yes

zerosuccsucc succ

iv[y]iv[x]

x == y ?

no

i_by[x]i_by[y]

i_by[x] i_by[y]

41

Integer Representation – With Instrumentation

zerosucc

iv[y]

iv[x]

y++

x == y ?

yes

x == y ?

no

succ

succ

zerosucc

iv[y]

iv[x]

succ

succ

succi_by[x] i_by[y]

i_by[x]i_by[y]

42

Backup Slides

43

References

[Stone90] J.M. Stone. A simple and Correct Shared-Queue

Algorithm using compare-and-swap. In Proceedings of Supercomputing ’90, November 1990.

[Valois94] J.D. Valois. Implementing Lock-Free Queues. In

Seventh international Conference on Parallel and Distributed Computing Systems, Las Vegas, NV, October 1994

44

Structural Operational Semantics - actions

An action consists of: precondition formula update formulae

Precondition formula may use a free variable ts for “currently scheduled” thread

Semantics is non-deterministic

45

Structural Operational Semantics - actions

lock(v)

tts: rval[v](ts,l) held_by(l,t)

held_by’(l1,t1) = held_by(l1,t1) (l1 = l t1 = ts)

blocked’ (t1,l1) = blocked(t1,l1) ((l1 l) (t1 ts))

precondition

predicateupdate

46

Initialize(C0) {for each C C0

push(stack,C)}explore() { while stack is not empty { C = pop(stack) if not member(C,stateSpace) { verify(C) stateSpace = stateSpace {C} for each action ac for each C’ such that C ac C’ push(stack,C’) } }}

State Space Exploration

47

Unbounded Number of Threads

Exploit state-space symmetryPrevious work defined symmetry between

process names (indices)Thread location = thread propertycanonical names = symmetry between

properties

top related