Вы находитесь на странице: 1из 7

Peterson’s Algorithm in Isabelle/HOL

Tom Ridge

January 12, 2006

1 Peterson’s Algorithm
1.1 Peterson’s Algorithm
Peterson’s algorithm [Pet81] is designed to ensure mutual exclusion between
two processes without any special hardware support.
Peterson’s algorithm has been formalised and verified several times1 .
Although the algorithm is essentially finite state, and therefore susceptible
to model checking, we provide an invariant style proof. Invariant style proofs
illustrate techniques that are usable for problems that do not fall to model
checking techniques. They can also provide important information to justify
intuitive notions of why an algorithm is correct, and this knowledge may
serve when tackling more complicated algorithms. Finally, invariant style
proofs are often more elegant than model checking proofs.

1.2 Peterson’s Algorithm in OCaml


Peterson’s algorithm may be written in OCaml as follows.

(*
ocamlmktop -I +threads -o ocamlthread unix.cma threads.cma
ocamlthread -I +threads
*)

let ps =
let turn = ref false in
let trys =
let try_f = ref false in
let try_t = ref false in
fun x -> if x then try_t else try_f in
let p =
fun id ->
let rec loop = fun () ->
Thread.delay (Random.float 1.0) (* non-crit region - do whatever *)
; (trys id) := true (* set trying flag *)
1
e.g. Coq verification at http://coq.inria.fr/contribs/mutual-exclusion.html

1
; turn := not id (* set turn flag *)
; (let rec test =
(fun x -> if (!turn = id) or (not (!(trys (not id)))) then () else test ())
in test ()) (* looping in test *)
; print_string ("Crit "^(string_of_bool id)); print_newline()
; Thread.delay (Random.float 1.0) (* crit region, do whatever *)
; print_string ("Exit "^(string_of_bool id)); print_newline()
; (trys id) := false (* exit crit by unsetting trying flag *)
; loop ()
in
loop () in
[Thread.create p false; Thread.create p true]
;;

Executing this in OCaml binds ps to two processes, identified by a


boolean. The global state consists of a turn boolean variable, and two
try boolean variables. try_f is read write by process false, but process
true only reads it, and vice versa for try_t. The try variables are accessed
through the trys function.
p takes a process identifier id (a boolean), and creates a process (in fact,
a thread). The process operates in an infinite loop. The process can be in
one of several states:

• Non-critical (in this case, we delay the process, but in general it might
be doing anything other than touching the global state).

• Setting its try flag

• Setting the turn flag to the other process id

• Testing whether the turn flag has been set to its own process id, or
whether the other process has its try flag set to false.

• Critical (in this case, we print some trace output and delay the pro-
cess, but in general it might be doing anything other than touching
the global state).

• Exiting the critical region, by setting its try flag to false.

The algorithm is designed to ensure mutual exclusion, i.e. that it is not


the case that both processes are in the critical region at the same time.
Executing the algorithm in a suitable OCaml toplevel gives the following
output.

Crit false
Exit false
Crit true
Exit true

2
Crit false
Exit false
Crit true
Exit true
Crit false
Exit false
Crit true
Exit true
Crit false
Exit false
Crit true
Exit true
Crit false
Exit false
Crit true
Exit true
Crit false
Exit false
Crit true
Exit true
Crit false
Exit false
Crit false
Exit false

Empirically it appears that the algorithm does indeed ensure mutual


exclusion. The challenge is to prove that this is indeed the case.

1.3 Peterson’s Algorithm in Isabelle/HOL


One approach to formalising Peterson’s algorithm would be to formalise
the semantics of OCaml, and reason directly about the program code given
above. However, this really obscures the reasons for the correctness of Pe-
terson’s algorithm, and unnecessarily ties the verification of the algorithm
to a particular implementation. For these reasons, we formalise an abstract
version of Peterson’s algorithm.
Each process may be executing in one of several states. We define an
enumeration of these states (as an abstract program counter).

datatype pc = NonCrit | SetTry | SetTurn | Test | Crit | Exit

We then define a type alias to distinguish try variables from other booleans.

types try = bool

3
The process state consists of a program counter, and a try variable.

types processState = pc ∗ try

The process identifiers, or process names, are booleans, but we distin-


guish them from booleans, by declaring another type alias.

types processName = bool

The system state consists of the collection of process states (represented


as a mapping from the finite boolean process names to process states), and
the shared turn variable.

types systemState = (processName => processState) ∗ turn

We can then define the transitions of the system.

constdefs trans :: systemState => systemState => bool


trans s s ′ ==

(∗ process i takes a turn ∗)


? i::processName.

let (f ,turn) = s in
let (f ′,turn ′) = s ′ in

(∗ j is the other process ∗)


let j = ∼ i in
(∗ nothing happens to j ∗)
(f ′ j = f j )
&(

(∗ get i ′s processState ∗)
let (ipc,itry) = f i in
let (ipc ′,itry ′) = f ′ i in

(∗ also need access to j ′s try variable ∗)


let (jpc,jtry) = f j in

(∗ NonCrit, looping ∗)
((ipc = NonCrit) & ((ipc ′,itry ′,turn ′) = (ipc,itry,turn)))

(∗ from NonCrit to SetTry ∗)


| ((ipc = NonCrit) & ((ipc ′,itry ′,turn ′) = (SetTry,itry,turn)))

(∗ SetTry to SetTurn ∗)
| ((ipc = SetTry) & ((ipc ′,itry ′,turn ′) = (SetTurn,True,turn)))

(∗ SetTurn to Test ∗)
| ((ipc = SetTurn) & ((ipc ′,itry ′,turn ′) = (Test,itry,j )))

(∗ Test, looping whilst waiting for access to critical region ∗)

4

| ((ipc = Test) & ((jtry = True) & (turn = i)) & ((ipc ′,itry ′,turn ′) = (ipc,itry,turn)))

(∗ Test to Crit ∗)
| ((ipc = Test) & ((jtry = False) | (turn = i)) & ((ipc ′,itry ′,turn ′) = (Crit,itry,turn)))

(∗ Crit, looping ∗)
| ((ipc = Crit) & ((ipc ′,itry ′,turn ′) = (ipc,itry,turn)))

(∗ Crit to Exit ∗)
| ((ipc = Crit) & ((ipc ′,itry ′,turn ′) = (Exit,itry,turn)))

(∗ Exit to NonCrit ∗)
| ((ipc = Exit) & ((ipc ′,itry ′,turn ′) = (NonCrit,False,turn)))

The transitions are defined as a relation between s, the current state of


the system, and s′ , the next state of the system. A transition of the system
involves the transition of one of the processes, i, whilst the state of the other
process j remains unchanged.

1.4 Correctness of Peterson’s Algorithm


We desire that it is not the case that the two processes are executing in the
critical region at the same time. This is a mutual exclusion property. We
must first define what an execution of the system is. We define the type of
executions and define a wellformed execution to be one where the execution
starts with both processes in the NonCrit state, with their try flags set to
false. Moreover, each step of the execution is a valid transition of the system.

types exec = nat => systemState

constdefs is-exec :: exec => bool


is-exec e ==

(∗ each process starts in the NonCrit region, not trying ∗)


(let (f ,turn) = e 0 in
! i. f i = (NonCrit, False))

(∗ consecutive states of the system are related by trans ∗)


& (! n. trans (e n) (e (n+1 )))

Then the mutual exclusion property may be stated as follows.

lemma mutual-exclusion: ! e. is-exec e


−−> (! n.
(! i.
let (f ,turn) = e n in
let (ipc,itry) = f i in
let j = ∼ i in

5
let (jpc,jtry) = f j in

((ipc = Crit) & (jpc = Crit))))

This property is a safety property. Safety properties are typically proved


by induction. In this case, we would like to induct on the variable n occurring
in the statement of the mutual exclusion property. Unfortunately the given
property is not inductive: we need to strengthen it.
First, let us dispense with a trivial lemma. This lemma states a sepa-
ration or ownership property, that a given process is the only process that
can modify its own try variable. This lemma is used at various points in
later proofs, and it is necessary to state it here for completeness. However,
we will use it without remark. The lemma is proved by induction on n.

lemma lemma-10-5-1 : ! e. is-exec e


−−> (! n.
(! i.
let (f ,turn) = e n in
let (ipc,itry) = f i in
(itry = (ipc : {SetTurn,Test,Crit,Exit}))))

The key to the correctness of Peterson’s algorithm is to realise that if


both processes are executing in the critical section, then one of them must
have entered first. So at some stage one process (call it i) is in the critical
section, and the other process (call it j) is executing the test loop. However,
in this case, j is prevented from entering the critical region because i’s try
flag is certainly True, and moreover turn is certainly set to i. One can prove
the following invariant by induction on n.

lemma lemma-10-5-2a: ! e. is-exec e


−−> (! n.
(! i.
let (f ,turn) = e n in
let (ipc,itry) = f i in
let j = ∼ i in
let (jpc,jtry) = f j in

(((ipc = Crit) & (jpc = Test)) −−> (turn = i))

))

Let us examine cases. We identify process i with its index i. Either i


or j has just taken a step. Assume i has taken a step. Either i is looping
in Crit, in which case apply the inductive hypothesis to the previous step
of the execution, or i has moved from Test to Crit, in which case j’s try
variable was set, so turn must have been set to i at the previous step of the
execution, and retains this value during i’s transition.
Alternatively, j has taken a step from SetTurn to Test. In this case j
has just set turn to i, and the invariant holds.

6
In order to obtain our mutual exclusion property, we strengthen this
property slightly as follows.

lemma lemma-10-5-2 : ! e. is-exec e


−−> (! n.
(! i.
let (f ,turn) = e n in
let (ipc,itry) = f i in
let j = ∼ i in
let (jpc,jtry) = f j in

(((ipc = Crit) & (jpc : {Test,Crit})) −−> (turn = i))

))

To see this is true, note that j’s transition from Test to Crit is not
allowed, because by the previous lemma, turn is set for i.
The mutual exclusion property is a direct corollary, since if both pro-
cesses are in Crit, then turn is set for both of them, which is a contradiction.

References
[Pet81] Gary L. Peterson. Myths about the mutual exclusion problem. In-
formation Processing Letters, 12(3):115-116, June 1981.

Вам также может понравиться