Assume you have a sequential operator "seq," a parallel operator "par," and a predicate on programs "valid." Then the following laws hold:
valid ((a `seq` b) `par` (c `seq` d)) -> valid ((a `par` c) `seq` (b `par` d)).
(The first law describes how the execution of several threads can interleave the steps.)
(valid a -> valid b) -> valid (a `seq` c) -> valid (b `seq` c)
(valid a -> valid b) -> valid (c `seq` a) -> valid (c `seq` b)
(valid a -> valid b) -> valid (a `par` c) -> valid (b `par` c).
valid a <-> valid (a `seq` id)
valid a <-> valid (id `seq` a)
valid a <-> valid (a `par` id)
valid a <-> valid (id `par` a).
p1 `seq` q `seq` (p2 `par` r)
then it follows that
p `par` (q `seq` r)
is valid.
(Rule 4 is the opposite of rule 1. Rule 1 allows you to deduce traces from a particular parallel form. Rule 4 allows you to deduce a particular parallel form from a set of traces.)
valid (p1 `seq` (p2 `seq` p3)) <-> valid ((p1 `seq` p2) `seq` p3).
Someone would use this formalism by first declaring a particular program they are interested in, valid, then following the implications of the validity of that trace, for the validity of other traces.
Example proof:
The commutativity of "par": valid (a `par` b) -> valid (b `par` a). Proof is by induction on the traces of a.
Base case:
forall b. valid (id `par` b) -> valid (b `par` id)
which is obvious.
Inductive step: we start with the original goal, and assume (valid ((x `seq` a) `par` b)) where x is an elementary program. Now we split b into sequences b1 and b2. For each way of splitting b we get
valid ((x `seq` a) `par` (b1 `seq` b2))
which implies
valid (b1 `seq` x `seq` (a `par` b2))
by rule 1. Then we apply the inductive hypothesis to get
valid (b1 `seq` x `seq` (b2 `par` a)).
Note that all the ways of splitting b correspond to the required hypotheses of rule 4. Therefore we conclude,
valid (b `par` (x `seq` a))
as required.
A useful fact about Hoare clauses and concurrency:
Say you have elementary programs a1, a2, a3, a4, ... and elementary programs b1, b2, b3, b4, .... Assume b1, b2, b3, b4 ... are predicate preserving, that is
forall P, { P } b_n { P }.
Assume PSup is the supremum of the preconditions of b1, b2, b3, b4, .... Assume PInf is the infimum of the postconditions of b1, b2, b3, b4, .... Finally, assume you have Hoare clause judgements
{ P1 /\ PInf } a1 { Q1 /\ PSup }
{ P2 /\ PInf } a2 { Q2 /\ PSup }
...
{ Pn /\ PInf } a_n { Qn /\ PSup }
so that Q1 implies P2, Q2 implies P3, and so forth.
The conclusion is that the program (a1 `seq` a2 `seq` a3 ...) `par` (b1 `seq` b2 `seq` b3 ...) has Hoare judgement:
{ P1 /\ PSup } ... { Qn /\ PInf }.
By JamesCandy
See also: StateLaws