| /* | |
| * This model describes a bug in aio_notify. If ctx->notifier is | |
| * cleared too late, a wakeup could be lost. | |
| * | |
| * 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 -DBUG docs/aio_notify_bug.promela | |
| * gcc -O2 pan.c | |
| * ./a.out -a -f | |
| * | |
| * 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 event; | |
| bool req; | |
| bool notifier_done; | |
| #ifdef CHECK_REQ | |
| #define USE_NOTIFY_ME 1 | |
| #else | |
| #define USE_NOTIFY_ME 0 | |
| #endif | |
| active proctype notifier() | |
| { | |
| do | |
| :: true -> { | |
| req = 1; | |
| if | |
| :: !USE_NOTIFY_ME || notify_me -> event = 1; | |
| :: else -> skip; | |
| fi | |
| } | |
| :: true -> break; | |
| od; | |
| notifier_done = 1; | |
| } | |
| #ifdef BUG | |
| #define AIO_POLL \ | |
| notify_me++; \ | |
| if \ | |
| :: !req -> { \ | |
| if \ | |
| :: event -> skip; \ | |
| fi; \ | |
| } \ | |
| :: else -> skip; \ | |
| fi; \ | |
| notify_me--; \ | |
| \ | |
| req = 0; \ | |
| event = 0; | |
| #else | |
| #define AIO_POLL \ | |
| notify_me++; \ | |
| if \ | |
| :: !req -> { \ | |
| if \ | |
| :: event -> skip; \ | |
| fi; \ | |
| } \ | |
| :: else -> skip; \ | |
| fi; \ | |
| notify_me--; \ | |
| \ | |
| event = 0; \ | |
| req = 0; | |
| #endif | |
| active proctype waiter() | |
| { | |
| do | |
| :: true -> AIO_POLL; | |
| od; | |
| } | |
| /* Same as waiter(), but disappears after a while. */ | |
| active proctype temporary_waiter() | |
| { | |
| 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 |