Lecture 13: Concurrency
(See also SAV07 Lecture 26.)
A language with concurrency primitives
c ::= [r] | c[]c | c;c | c* | c||c | atomic(c)
[r] - havoc, assume, assert, assignments, any simple command, given by relation r
Examples
What are the possible results?
(x := 1; y := 1; z := x+y) || (y := 2; x := 12; z := x+y)
(x := 1; y := 1; z := x+y) || (y := 2; x := y+10; z := x+y)
For the above two programs, we assume that every statement is atomic. The above two programs result in different possible values for x, y and z. Consider the first program. When it terminates, the possible values for x, y and z are {(1,1,2), (12, 2, 14), (12, 1, 13)}. As for the second program, the possible values upon termination are {(1,1,2), (12, 2, 14), (12, 1, 13), (11,1,12)}. We note that the last possible valuation on (x,y,z) results from an interleaving of assignments where (y:=1) occurs between (y:=2) and (x:=y+10).
This suggests that the two programs above are not equivalent for the purpose of reasoning in concurrently executing programs.
A semantics of the language with concurrency
What would be the semantics of || as a composition of relations?
c ::= [r] | c[]c | c;c | c* | c||c | atomic(c)
We express the semantics of c, as the set of all possible sequences of relations of c. .
(This is similar to a language theoretic approach, where we express the concatenation of two languages and as and .
.
Let and . Then and .
where and , where
and .
where and .
atomiceval where eval is a function such that eval.
An example
{ b = x + y } while (*) do if (*) then atomic{ x++; y--; } else atomic{ y++; x--; } endif end || while (*) do if (*) then atomic{ x++; y--; } else atomic{ y++; x--; } endif end { b = x + y }
In our language, this example can be expressed as
(atomic(x:=x+1;y:=y-1)[]atomic(x:=x-1;y:=y+1))* || (atomic(x:=x+1;y:=y-1)[]atomic(x:=x-1;y:=y+1))*
Global reachability invariants
Assume that atomic only applies to sequences of relations (so we treat them as biger atomic relations).
While theorem for parallel programs
(r1 [] ... [] rn)* || (r1 [] ... [] rn)* ||
While theorem when atomic is present - implementing atomic
- global lock
- transactions with state copying
Assume any set of states is expressible in our logic (allow set theory or FO logic with axioms).
Invariant for resulting while program is a global reachability invariant.
- good: only talks about current state
- bad: potentially refers to all components of parallel process
Alternative: complete history variables
- good: only talks about individual parallel process (except for join points)
- bad: refers not only to current, but also past states
Any point in between: introduce auxiliary variables, which can be local or global, whatever is best for the given program.
Example.
{x=0} (x:=x+1 || x:=x+10) {x=11}
Owicki-Gries method
Idea
- whenever modifying a group of shared variables (=resource), enforce certain invariants on those variables
- prevent interleavings within updates to variables of a resource
Examples: bytes of a word, nodes in a list
Parallel composition construct declares resources and starts parallel threads:
resource r1(vars1), ..., rn(varsn) cobegin S1 || ... || Sn coend
(Assume only one such top-level construct, for simplicity. Assume one resource.)
Accessing variables in a resource:
with r when B do S
Requirements:
- variable written in some process and used in another must be in a resource
- if a variable is in a resource, it can only be accessed using 'with' statement
Proof rules: introduce global invariant I for shared variables
{P & B & I} S {Q & I} -------------------------- {P} with r when B do S {Q}
{P1} S1 {Q1}, ..., {Pn} Sn {Qn} ------------------------------------------------------- {P1 & ... Pn & I} (S1 || ... || Sn) {Q1 & ... & Qn & I}
Requirements:
- variables in not modified outside
Need for history variables.
Granularity of concurrency
Using concurrency for efficiency: trade off between sizes of atomic sections and ease of reasoning.
Theorems about equivalence can be used to verify less concurrent version, run more concurrent one.
Operational description of
with r when B do S
with lock per resouce:
atomic(assume r_available & B; r_available := false); S; r_available := true;
it by
atomic (assume B; S)
and replace cobegin with simple || under the assumptions of Owicki-Gries?
Transactions: a more abstract mechanism for atomicity, promises to get good point in reasoning/efficiency space.