TLA+ is not hard to learn. Amazon engineers report that: "Engineers from entry level to principal have been able to learn TLA+ from scratch and get useful results in two to three weeks, in some cases in their personal time on weekends and evenings, without further help or training."However, programmers and software engineers find TLA+ specs intimidating at first sight. I'm not going to risk scaring you now with a TLA+ spec. Instead, I'll present an example in PlusCal. PlusCal looks like a simple toy programming language, except that an expression can be any TLA+ expression—meaning any mathematical formula—making it infinitely more expressive than any programming language. The algorithm is translated into a TLA+ specification that can be checked with the TLA+ tools. PlusCal is not as expressive as TLA+, but it is often more convenient to use than raw TLA+, especially for simple algorithms. The example is Peterson's mutual exclusion algorithm. Here is the PlusCal code: --algorithm Peterson { variables flag = [i \in {0, 1} |-> FALSE], turn = 0; \* Declares the global variables flag and turn and their initial values; \* flag is a 2-element array with initially flag[0] = flag[1] = FALSE. fair process (proc \in {0,1}) { \* Declares two processes with identifier self equal to 0 and 1. \* The keyword fair means that no process can stop forever if it can \* always take a step. a1: while (TRUE) { skip ; \* the noncritical section a2: flag[self] := TRUE ; a3: turn := 1 - self ; a4: await (flag[1-self] = FALSE) \/ (turn = self); \* \/ is written || in C. cs: skip ; \* the critical section a5: flag[self] := FALSE } } }To specify a concurrent algorithm, you must say what the atomic operations are. In PlusCal, the code from one label to the next is executed atomically.
The TLC model checker can verify that the algorithm satisfies two
important properties: mutual exclusion, meaning that two processes are
never executing their critical section at the same time; and
starvation freedom, meaning that each process keeps executing its
critical section. The TLA+ translation introduces an array
variable (pc[0] /= "cs") \/ (pc[1] /= "cs")or more compactly as {pc[0],pc[1]} /=
{"cs"} . TLC can check that it is always true in any
execution of the algorithm. Starvation freedom here means that
each process executes its critical section infinitely often.
This is expressed in temporal logic by the formula ∀ i ∈ {0,1} : ☐♢(pc[i] = "cs") typed as \A i \in {0,1} : []<>(pc[i] = "cs")TLC can check that this property is satisfied by every execution of the algorithm.
In Peterson's algorithm, each read or write of a global variable should
be in a separate atomic action. This is not true in the PlusCal code
above because statement a4: while (TRUE) { a4a: if (flag[1-self] = FALSE) {goto cs}; a4b: if (turn = self) {goto cs} } ;TLC can verify that the resulting algorithm also satisfies mutual exclusion and starvation freedom. |