The promela language used to construct spin models is relatively easy to pick up. However, composing a
model, which doesn’t introduce unnecessary states, is still black magic. Here is a list of seemingly pointless changes, that cuts down the exploration
space significantly.
The following is a model of Peterson’s mutex algorithm; the commented code in monitor is
enabled all the time, then spin needs to explore its interleaving with all other statements, while the alternative way has the guard, which become
true only when the assertion is doomed to fail.
In theory, I can put the content of checker right after done++. However, then all N processes are alive while performing d_step.
Alternatively, having a separate process to do final checking reduces the size of the state space.
byte prev; byte x; if :: _pid == 0 -> prev = N - 1 :: else -> prev = _pid - 1 fi
if :: goto initiator :: goto forwarder fi
initiator: channel[_pid] ! _pid byte min = _pid loop: channel[prev] ? x if :: x < min -> min = x :: x == _pid -> candidates[_pid] = min goto exit :: else -> skip fi channel[_pid] ! x goto loop
forwarder: do :: channel[prev] ? x ; channel[_pid] ! x :: timeout -> goto exit od
exit: done++ }
active proctype checker() { done == N d_step { byte leader int i /* check single leader */ for (i in candidates) { if :: candidates[i] != -1 && leader == 0 -> leader = candidates[i] :: candidates[i] != -1 && leader != 0 -> assert(leader == candidates[i]) :: else fi } } }