/* * This model describes the interaction between ctx->notified * and ctx->notifier. * * Author: Paolo Bonzini * * 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