A model in our settings is a transition system. The goal of the following exercises is to build models that represents the behaviors of given system, with specific verification goals in mind. We will first write models as transition systems, and then write the corresponding PlusCal code.
PlusCal is an algorithmic language. It differs from programming languages since:
The TLA tools offer three main features:
Please refer to the PlusCal documentation
~herbrete/public/TLA/c-manual.pdf
and
~herbrete/public/TLA/pluscal.pdf
if needed.
We consider the following C function:
int f(int x)
{
while (x != 0) {
if (x < -1)
= 0;
x = x - 1;
x }
}
f
above as a
transition system assuming that int
values range from
-2
to 2
. What defines a state?f
above. Compare
the model to the C code, and check for termination using the TLC
model-checker.We consider the C program below which has a shared variable
x
and a function increment
that is executed by
several processes. This function first stores the value of
x
in a local variable y
. Then, it writes
y+1
into the shared variable x
. We assume that
each assignment is an atomic instruction.
int x = 0;
void increment()
{
int y = x;
= y + 1;
x }
N
processes?N=2
threads as a
transition systemx
?The following code is a PlusCal model of the C program above, with
N
threads executing the function
increment
.
EXTENDS Naturals, TLC
CONSTANTS N (* Number of processes *)
(*
--algorithm non_atomic_increment {
variables
x = 0;
process (P \in 1..N)
variables
y = 0;
{
l0: y := x;
l1: x := y + 1
}
}
*)
In this algorithm:
EXTENDS
is used to import libraries (here, the TLC
library and the natural numbers library).CONSTANT
introduces new symbolic names. Here
N
is the number of processes in the algorithm. Its value is
unspecified yet.(* ... *)
. Its specification starts with
--algorithm
followed by a name.x
is declared in the algorithm scope. It
is a global variable that is shared by all the processes. The variable
y
is declared in the process scope. Each process of type
P
has its own local variable y
.x = 0
and y = 0
define
the initial values of variables x
and y
.
PlusCal allows non-deterministic initial values. For instance, replacing
y = 0
by y \in 1..10
result in choosing the
initial value of y
as any value between 1
and
10
. Our algorithm would thus have 10
initial
states: one for each possible initial value of y
.l0
and l1
are labels that identify
statements. Labels are used to define atomic blocks of statements as we
will see later.Use the parse module
command to check for syntax errors,
and get a translation of the PlusCal code in TLA+. The TLA+ translation
corresponds to a logical description of the algorithm. It is used by the
model-checking tool TLC
to assess the correcteness of the
algorithm.
Our goal is to check that, when the algorithm terminates, the value
of x
is equal to N
:
AllDone == \A self \in 1..N: pc[self] = "Done"
Correctness == [](AllDone => x=N)
atomicity
, and add this
specification after the translation of the algorithm. Check the
correctness of the algorithm for N=1
process, and then
N=2
processes. What do you observe? Does this correspond to
the transition system model?Labels in PlusCal algorithms define which blocs of statements are
executed in an atomic way. Labeling is a key to the modeling of
concurrent and distributed algorithms where correctness heavily depends
on which statements can be executed atomically. PlusCal puts
restrictions on labels in order to let the translation in TLA+ be as
close as possible to the algorithm. These constraints are described in
section 3.7 of the PlusCal user manual (available at
~herbrete/public/TLA/c-manual.pdf
).
l1
in the previous algorithm. Draw the
corresponding transition system and check that the specification
hold.l1
result in an algorithm
that does not faithfully model the C
program multiprocess.cWe consider the following C program where the lock
is
initially free, and variables old
and new
are
8-bytes pointers. The unspecified condition ...
in the
if
instruction has been abstracted for simplicity and
should be modelled by a non-deterministic choice.
// lock is free, old and new are 8-bytes pointers
do {
();
lock= new;
old if (...) {
();
unlock++;
new}
}
while (new != old);
old
is equal to new
(i.e. the boolean
value of old == new
).lock()
instruction is never blocking from your model?