153 lines
4.0 KiB
Promela
153 lines
4.0 KiB
Promela
/*
|
|
* This model describes the interaction between ctx->notified
|
|
* and ctx->notifier.
|
|
*
|
|
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
|
*
|
|
* This file is in the public domain. If you really want a license,
|
|
* the WTFPL will do.
|
|
*
|
|
* To verify the buggy version:
|
|
* spin -a -DBUG1 docs/aio_notify_bug.promela
|
|
* gcc -O2 pan.c
|
|
* ./a.out -a -f
|
|
* (or -DBUG2)
|
|
*
|
|
* To verify the fixed version:
|
|
* spin -a docs/aio_notify_bug.promela
|
|
* gcc -O2 pan.c
|
|
* ./a.out -a -f
|
|
*
|
|
* Add -DCHECK_REQ to test an alternative invariant and the
|
|
* "notify_me" optimization.
|
|
*/
|
|
|
|
int notify_me;
|
|
bool notified;
|
|
bool event;
|
|
bool req;
|
|
bool notifier_done;
|
|
|
|
#ifdef CHECK_REQ
|
|
#define USE_NOTIFY_ME 1
|
|
#else
|
|
#define USE_NOTIFY_ME 0
|
|
#endif
|
|
|
|
#ifdef BUG
|
|
#error Please define BUG1 or BUG2 instead.
|
|
#endif
|
|
|
|
active proctype notifier()
|
|
{
|
|
do
|
|
:: true -> {
|
|
req = 1;
|
|
if
|
|
:: !USE_NOTIFY_ME || notify_me ->
|
|
#if defined BUG1
|
|
/* CHECK_REQ does not detect this bug! */
|
|
notified = 1;
|
|
event = 1;
|
|
#elif defined BUG2
|
|
if
|
|
:: !notified -> event = 1;
|
|
:: else -> skip;
|
|
fi;
|
|
notified = 1;
|
|
#else
|
|
event = 1;
|
|
notified = 1;
|
|
#endif
|
|
:: else -> skip;
|
|
fi
|
|
}
|
|
:: true -> break;
|
|
od;
|
|
notifier_done = 1;
|
|
}
|
|
|
|
#define AIO_POLL \
|
|
notify_me++; \
|
|
if \
|
|
:: !req -> { \
|
|
if \
|
|
:: event -> skip; \
|
|
fi; \
|
|
} \
|
|
:: else -> skip; \
|
|
fi; \
|
|
notify_me--; \
|
|
\
|
|
atomic { old = notified; notified = 0; } \
|
|
if \
|
|
:: old -> event = 0; \
|
|
:: else -> skip; \
|
|
fi; \
|
|
\
|
|
req = 0;
|
|
|
|
active proctype waiter()
|
|
{
|
|
bool old;
|
|
|
|
do
|
|
:: true -> AIO_POLL;
|
|
od;
|
|
}
|
|
|
|
/* Same as waiter(), but disappears after a while. */
|
|
active proctype temporary_waiter()
|
|
{
|
|
bool old;
|
|
|
|
do
|
|
:: true -> AIO_POLL;
|
|
:: true -> break;
|
|
od;
|
|
}
|
|
|
|
#ifdef CHECK_REQ
|
|
never {
|
|
do
|
|
:: req -> goto accept_if_req_not_eventually_false;
|
|
:: true -> skip;
|
|
od;
|
|
|
|
accept_if_req_not_eventually_false:
|
|
if
|
|
:: req -> goto accept_if_req_not_eventually_false;
|
|
fi;
|
|
assert(0);
|
|
}
|
|
|
|
#else
|
|
/* There must be infinitely many transitions of event as long
|
|
* as the notifier does not exit.
|
|
*
|
|
* If event stayed always true, the waiters would be busy looping.
|
|
* If event stayed always false, the waiters would be sleeping
|
|
* forever.
|
|
*/
|
|
never {
|
|
do
|
|
:: !event -> goto accept_if_event_not_eventually_true;
|
|
:: event -> goto accept_if_event_not_eventually_false;
|
|
:: true -> skip;
|
|
od;
|
|
|
|
accept_if_event_not_eventually_true:
|
|
if
|
|
:: !event && notifier_done -> do :: true -> skip; od;
|
|
:: !event && !notifier_done -> goto accept_if_event_not_eventually_true;
|
|
fi;
|
|
assert(0);
|
|
|
|
accept_if_event_not_eventually_false:
|
|
if
|
|
:: event -> goto accept_if_event_not_eventually_false;
|
|
fi;
|
|
assert(0);
|
|
}
|
|
#endif
|