|
@@ -0,0 +1,140 @@
|
|
|
|
+/*
|
|
|
|
+ * 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
|