Model verifying that wakeup really
can be called after release without causing deadlock.
This commit is contained in:
parent
943fd378a1
commit
949352af66
2 changed files with 150 additions and 0 deletions
134
sleep1.p
Normal file
134
sleep1.p
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
This file defines a Promela model for xv6's
|
||||
acquire, release, sleep, and wakeup, along with
|
||||
a model of a simple producer/consumer queue.
|
||||
|
||||
To run:
|
||||
spinp sleep1.p
|
||||
|
||||
(You may need to install Spin, available at http://spinroot.com/.)
|
||||
|
||||
After a successful run spin prints something like:
|
||||
|
||||
unreached in proctype consumer
|
||||
(0 of 37 states)
|
||||
unreached in proctype producer
|
||||
(0 of 23 states)
|
||||
|
||||
After an unsuccessful run, the spinp script prints
|
||||
an execution trace that causes a deadlock.
|
||||
|
||||
The safe body of producer reads:
|
||||
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
wakeup(0);
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
|
||||
If this is changed to:
|
||||
|
||||
x = value; value = x + 1; x = 0;
|
||||
acquire(lk);
|
||||
wakeup(0);
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
|
||||
then a deadlock can happen, because the non-atomic
|
||||
increment of value conflicts with the non-atomic
|
||||
decrement in consumer, causing value to have a bad value.
|
||||
Try this.
|
||||
|
||||
If it is changed to:
|
||||
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
release(lk);
|
||||
wakeup(0);
|
||||
i = i + 1;
|
||||
|
||||
then nothing bad happens: it is okay to wakeup after release
|
||||
instead of before, although it seems morally wrong.
|
||||
*/
|
||||
|
||||
#define ITER 4
|
||||
#define N 2
|
||||
|
||||
bit lk;
|
||||
byte value;
|
||||
bit sleeping[N];
|
||||
|
||||
inline acquire(x)
|
||||
{
|
||||
atomic { x == 0; x = 1 }
|
||||
}
|
||||
|
||||
inline release(x)
|
||||
{
|
||||
assert x==1;
|
||||
x = 0
|
||||
}
|
||||
|
||||
inline sleep(cond, lk)
|
||||
{
|
||||
assert !sleeping[_pid];
|
||||
if
|
||||
:: cond ->
|
||||
skip
|
||||
:: else ->
|
||||
atomic { release(lk); sleeping[_pid] = 1 };
|
||||
sleeping[_pid] == 0;
|
||||
acquire(lk)
|
||||
fi
|
||||
}
|
||||
|
||||
inline wakeup()
|
||||
{
|
||||
w = 0;
|
||||
do
|
||||
:: w < N ->
|
||||
sleeping[w] = 0;
|
||||
w = w + 1
|
||||
:: else ->
|
||||
break
|
||||
od
|
||||
}
|
||||
|
||||
active[N] proctype consumer()
|
||||
{
|
||||
byte i, x;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
:: i < ITER ->
|
||||
acquire(lk);
|
||||
sleep(value > 0, lk);
|
||||
x = value; value = x - 1; x = 0;
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
:: else ->
|
||||
break
|
||||
od;
|
||||
i = 0;
|
||||
skip
|
||||
}
|
||||
|
||||
active[N] proctype producer()
|
||||
{
|
||||
byte i, x, w;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
:: i < ITER ->
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
release(lk);
|
||||
wakeup();
|
||||
i = i + 1;
|
||||
:: else ->
|
||||
break
|
||||
od;
|
||||
i = 0;
|
||||
skip
|
||||
}
|
||||
|
16
spinp
Executable file
16
spinp
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ $# != 1 ] || [ ! -f "$1" ]; then
|
||||
echo 'usage: spinp file.p' 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f $1.trail
|
||||
spin -a $1 || exit 1
|
||||
cc -DSAFETY -DREACH -DMEMLIM=500 -o pan pan.c
|
||||
pan -i
|
||||
rm pan.* pan
|
||||
if [ -f $1.trail ]; then
|
||||
spin -t -p $1
|
||||
fi
|
||||
|
Loading…
Reference in a new issue