I am trying to formally verify mutual exclusion on the following Promela Model:
/* Mutex with (bugged) Peterson algorithm */
bool flag[2];
int turn = -1;
active [2] proctype agent() {
bool in_cs;
int other = 1 - _pid;
do
:: true ->
/* trying section */
turn = other;
flag[_pid] = true;
!( flag[other] && (turn == other));
/* critical section */
in_cs = true;
printf("Agent %d in CS\n", _pid);
in_cs = false;
/* exit section */
flag[_pid] = false;
od;
}
ltl mutex { [] ! (agent[0]:in_cs && agent[1]:in_cs) }
To verify it, I use the following chain of commands:
~$ spin -a test.pml ; gcc -o run pan.c -DNOREDUCE ; ./run -a -N p1
which prints the following output:
...
Full statespace search for:
never claim + (mutex)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 40 byte, depth reached 53, errors: 0
...
Q: i know for a fact that the Promela Model does not satisfy mutual exclusion, but Spin
claims that it does. Why is it?
NOTE: I added option -DNOREDUCE
when compiling with gcc
because Spin 6.4.8
prints a warning message requesting me to do so if I don't:
~$ spin -a test.pml ; gcc -o run pan.c ; ./run -a -N p1
...
pan.c: In function ‘main’:
pan.c:9478:3: warning: #warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE) [-Wcpp]
#warning p.o. reduction not compatible with remote varrefs (use -DNOREDUCE)
^~~~~~~
...
Here is the solution:
The problem here is that to disable partial order reduction you not only have to compile with
-DNOREDUCE
but also generate the model while disabling statement merging (merging of subsequent local actions) -- which is also a partial order reduction strategy. So, for the second example you also get the counter-example if you do:spin -o3 -a test.pml; gcc -DNOREDUCE -o pan pan.c; ./pan -a
[G. H., private communication]
The output of the suggested solution matches our expectation:
~$ spin -o3 -a test.pml ; gcc -o run pan.c -DNOREDUCE ; ./run -a -N p1
...
pan:1: assertion violated !( !( !((agent[0].in_cs&&agent[1].in_cs)))) (at depth 55)
pan: wrote test.pml.trail
...
Full statespace search for:
never claim + (mutex)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 40 byte, depth reached 69, errors: 1
...
Alternatively, one can circumvent the problem by using label remote references instead of variable remote references, e.g.:
/* Mutex with (bugged) Peterson algorithm */
bool flag[2];
int turn = -1;
active [2] proctype agent() {
int other = 1 - _pid;
do
:: true ->
/* trying section */
turn = other;
flag[_pid] = true;
!( flag[other] && (turn == other));
/* critical section */
cs:
printf("Agent %d in CS\n", _pid);
/* exit section */
flag[_pid] = false;
od;
}
ltl mutex { [] ! (agent[0]@cs && agent[1]@cs) }
With this Model, we find a counter-example, as expected:
~$ spin -search test.pml
...
pan:1: assertion violated !( !( !(((agent[0]._p==cs)&&(agent[1]._p==cs))))) (at depth 28)
pan: wrote test.pml.trail
...