2016-04-21 14:28:08 +00:00
|
|
|
(** Formal Reasoning About Programs <http://adam.chlipala.net/frap/>
|
2021-03-28 21:03:56 +00:00
|
|
|
* Chapter 19: Operational Semantics for Shared-Memory Concurrency
|
2016-04-21 14:28:08 +00:00
|
|
|
* Author: Adam Chlipala
|
|
|
|
* License: https://creativecommons.org/licenses/by-nc-nd/4.0/ *)
|
|
|
|
|
|
|
|
Require Import Frap.
|
|
|
|
|
|
|
|
Set Implicit Arguments.
|
|
|
|
Set Asymmetric Patterns.
|
|
|
|
|
|
|
|
(** * Shared notations and definitions; main material starts afterward. *)
|
|
|
|
|
|
|
|
Notation "m $! k" := (match m $? k with Some n => n | None => O end) (at level 30).
|
|
|
|
Definition heap := fmap nat nat.
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
Local Hint Extern 1 (_ <= _) => linear_arithmetic : core.
|
|
|
|
Local Hint Extern 1 (@eq nat _ _) => linear_arithmetic : core.
|
2016-04-21 14:28:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
(** * An object language with shared-memory concurrency *)
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* We're going to start investigating how to verify concurrent programs whose
|
|
|
|
* behavior is given with operational semantics. There are a variety of
|
|
|
|
* different concurrency styles out there, with their distinctive practical and
|
|
|
|
* theoretical benefits; we'll start with the most venerable style, shared
|
|
|
|
* memory. *)
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
(* We'll build on the mixed-embedding languages from the last two chapters.
|
2016-04-24 19:29:21 +00:00
|
|
|
* Let's simplify the encoding by only working with commands that generate
|
2016-04-21 17:42:30 +00:00
|
|
|
* [nat]. *)
|
|
|
|
Inductive cmd :=
|
|
|
|
| Return (r : nat)
|
|
|
|
| Bind (c1 : cmd) (c2 : nat -> cmd)
|
|
|
|
| Read (a : nat)
|
|
|
|
| Write (a v : nat)
|
|
|
|
| Fail
|
2016-04-21 14:28:08 +00:00
|
|
|
|
|
|
|
(* Now here's the new part: parallel composition of commands. *)
|
2016-04-21 17:42:30 +00:00
|
|
|
| Par (c1 c2 : cmd)
|
2016-04-21 14:28:08 +00:00
|
|
|
|
|
|
|
(* Let's also add locking commands, where locks are named by [nat]s. *)
|
2016-04-21 17:42:30 +00:00
|
|
|
| Lock (a : nat)
|
|
|
|
| Unlock (a : nat).
|
2016-04-21 14:28:08 +00:00
|
|
|
|
|
|
|
Notation "x <- c1 ; c2" := (Bind c1 (fun x => c2)) (right associativity, at level 80).
|
|
|
|
Infix "||" := Par.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* As the program runs, it has not just a heap but also a set of locks that are
|
|
|
|
* taken at that moment. *)
|
2016-04-21 14:28:08 +00:00
|
|
|
Definition locks := set nat.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* The first few rules below are basically the same as in last chapter, except
|
|
|
|
* that we relax the restriction on only reading/writing addresses that are
|
|
|
|
* explicitly mapped into the heap. *)
|
2016-04-21 17:42:30 +00:00
|
|
|
Inductive step : heap * locks * cmd -> heap * locks * cmd -> Prop :=
|
|
|
|
| StepBindRecur : forall c1 c1' c2 h h' l l',
|
2016-04-21 14:28:08 +00:00
|
|
|
step (h, l, c1) (h', l', c1')
|
|
|
|
-> step (h, l, Bind c1 c2) (h', l', Bind c1' c2)
|
2016-04-21 17:42:30 +00:00
|
|
|
| StepBindProceed : forall v c2 h l,
|
2016-04-21 14:28:08 +00:00
|
|
|
step (h, l, Bind (Return v) c2) (h, l, c2 v)
|
|
|
|
|
|
|
|
| StepRead : forall h l a,
|
|
|
|
step (h, l, Read a) (h, l, Return (h $! a))
|
|
|
|
| StepWrite : forall h l a v,
|
2016-04-21 17:42:30 +00:00
|
|
|
step (h, l, Write a v) (h $+ (a, v), l, Return 0)
|
2016-04-21 14:28:08 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* First interesting twist: we can "push steps through" the [Par] operator on
|
|
|
|
* either side. The choice of a side is the sole source of nondeterminism in
|
|
|
|
* this semantics, corresponding to the whims of a scheduler. *)
|
2016-04-21 14:28:08 +00:00
|
|
|
| StepParRecur1 : forall h l c1 c2 h' l' c1',
|
|
|
|
step (h, l, c1) (h', l', c1')
|
|
|
|
-> step (h, l, Par c1 c2) (h', l', Par c1' c2)
|
|
|
|
| StepParRecur2 : forall h l c1 c2 h' l' c2',
|
|
|
|
step (h, l, c2) (h', l', c2')
|
|
|
|
-> step (h, l, Par c1 c2) (h', l', Par c1 c2')
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* To take a lock, it must not be held; and vice versa for releasing a lock. *)
|
2016-04-21 14:28:08 +00:00
|
|
|
| StepLock : forall h l a,
|
|
|
|
~a \in l
|
2017-03-07 19:59:56 +00:00
|
|
|
-> step (h, l, Lock a) (h, {a} \cup l, Return 0)
|
2016-04-21 14:28:08 +00:00
|
|
|
| StepUnlock : forall h l a,
|
|
|
|
a \in l
|
2016-04-21 17:42:30 +00:00
|
|
|
-> step (h, l, Unlock a) (h, l \setminus {a}, Return 0).
|
2016-04-21 14:28:08 +00:00
|
|
|
|
2016-04-21 17:42:30 +00:00
|
|
|
Definition trsys_of (h : heap) (l : locks) (c : cmd) := {|
|
2016-04-21 14:28:08 +00:00
|
|
|
Initial := {(h, l, c)};
|
2016-04-21 17:42:30 +00:00
|
|
|
Step := step
|
2016-04-21 14:28:08 +00:00
|
|
|
|}.
|
2016-04-21 17:42:30 +00:00
|
|
|
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
|
|
|
|
(** * An example *)
|
|
|
|
|
|
|
|
(* In this lecture, we'll focus on model checking as our program-proof
|
|
|
|
* technique. Recall that model checking is all about reducing a problem to a
|
|
|
|
* reachability question in a finite-state system. Our programs here have the
|
|
|
|
* (perhaps surprising!) property that termination is guaranteed, for any
|
2021-05-02 16:56:47 +00:00
|
|
|
* initial state, regardless of how the scheduler behaves. However,
|
|
|
|
* counterintuitively, program execution does *not* need to be finite-state,
|
|
|
|
* and thus amenable to model checking, though we will stick to examples that are.
|
2016-04-24 19:29:21 +00:00
|
|
|
* Let's define a simple two-thread program and model-check it. *)
|
|
|
|
|
|
|
|
(* Throughout this file, we'll only be verifying that no thread could ever reach
|
|
|
|
* a [Fail] command that is next in line to execute, a property that is easy to
|
|
|
|
* phrase as an invariant of the transition system. Here's how to compute
|
|
|
|
* whether a command is about to fail. *)
|
|
|
|
Fixpoint notAboutToFail (c : cmd) : bool :=
|
|
|
|
match c with
|
|
|
|
| Fail => false
|
|
|
|
| Bind c1 _ => notAboutToFail c1
|
|
|
|
| Par c1 c2 => notAboutToFail c1 && notAboutToFail c2
|
|
|
|
| _ => true
|
|
|
|
end.
|
|
|
|
|
2016-04-21 17:42:30 +00:00
|
|
|
Example two_increments_thread :=
|
|
|
|
_ <- Lock 0;
|
|
|
|
n <- Read 0;
|
2016-04-24 17:01:16 +00:00
|
|
|
if n <=? 1 then
|
|
|
|
_ <- Write 0 (n + 1);
|
|
|
|
Unlock 0
|
2016-04-21 23:12:02 +00:00
|
|
|
else
|
|
|
|
Fail.
|
|
|
|
|
2016-04-24 17:01:16 +00:00
|
|
|
Example two_increments := two_increments_thread || two_increments_thread.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Next, we do one of our standard boring (and slow; sorry!) model-checking
|
|
|
|
* proofs, where tactics explore the finite state space for us. *)
|
2017-05-01 02:05:28 +00:00
|
|
|
Theorem two_increments_ok :
|
2016-04-21 17:42:30 +00:00
|
|
|
invariantFor (trsys_of $0 {} two_increments)
|
2016-04-21 23:12:02 +00:00
|
|
|
(fun p => let '(_, _, c) := p in
|
2016-04-22 00:35:34 +00:00
|
|
|
notAboutToFail c = true).
|
2016-04-21 17:42:30 +00:00
|
|
|
Proof.
|
2016-04-24 19:29:21 +00:00
|
|
|
unfold two_increments, two_increments_thread.
|
2016-04-21 17:42:30 +00:00
|
|
|
simplify.
|
|
|
|
eapply invariant_weaken.
|
|
|
|
apply multiStepClosure_ok; simplify.
|
|
|
|
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
2020-04-20 15:56:23 +00:00
|
|
|
model_check_step.
|
2016-04-21 17:42:30 +00:00
|
|
|
model_check_done.
|
|
|
|
|
|
|
|
simplify.
|
2016-04-22 00:35:34 +00:00
|
|
|
propositional; subst; equality.
|
2017-05-01 02:05:28 +00:00
|
|
|
Qed.
|
2016-04-24 19:29:21 +00:00
|
|
|
|
|
|
|
(* Notice how every step of the process needs to consider all possibilities of
|
|
|
|
* threads that could run next, which, in general, gives us state spaces of size
|
|
|
|
* *exponential* in the program-text length. That's really a shame from a
|
|
|
|
* performance perspective, isn't it? Our goal now will be to apply
|
|
|
|
* *optimizations* that show equivalence with alternative transition systems
|
|
|
|
* whose state spaces are smaller. By the end, we'll be able to check
|
|
|
|
* nontrivial concurrent programs by only computing state spaces with *linear*
|
|
|
|
* size in program-text length! (The catch is that the technique only applies
|
|
|
|
* for programs accepted by a simple static analysis.) *)
|
2016-04-21 23:12:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
(** * Optimization #1: always run all purely local actions first. *)
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Here's a function that, in a single go, performs all simplifications that are
|
|
|
|
* *thread-local*. That is, no other thread can observe those steps, as they
|
|
|
|
* don't touch the heap or lockset. *)
|
2016-04-21 23:12:02 +00:00
|
|
|
Fixpoint runLocal (c : cmd) : cmd :=
|
|
|
|
match c with
|
|
|
|
| Return _ => c
|
|
|
|
| Bind c1 c2 =>
|
|
|
|
match runLocal c1 with
|
|
|
|
| Return v => runLocal (c2 v)
|
|
|
|
| c1' => Bind c1' c2
|
|
|
|
end
|
|
|
|
| Read _ => c
|
|
|
|
| Write _ _ => c
|
|
|
|
| Fail => c
|
2016-04-24 17:01:16 +00:00
|
|
|
| Par c1 c2 => Par (runLocal c1) (runLocal c2)
|
2016-04-21 23:12:02 +00:00
|
|
|
| Lock _ => c
|
|
|
|
| Unlock _ => c
|
|
|
|
end.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* We can define an alternative step relation that always runs [runLocal] as a
|
|
|
|
* kind of postprocessing on the new command. This way, the model checker won't
|
|
|
|
* need to run separate exploration steps for all of those trivial
|
|
|
|
* simplifications. *)
|
2016-04-21 23:12:02 +00:00
|
|
|
Inductive stepL : heap * locks * cmd -> heap * locks * cmd -> Prop :=
|
|
|
|
| StepL : forall h l c h' l' c',
|
|
|
|
step (h, l, c) (h', l', c')
|
|
|
|
-> stepL (h, l, c) (h', l', runLocal c').
|
|
|
|
|
|
|
|
Definition trsys_ofL (h : heap) (l : locks) (c : cmd) := {|
|
|
|
|
Initial := {(h, l, runLocal c)};
|
|
|
|
Step := stepL
|
|
|
|
|}.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Now we prove some basic facts; commentary resumes before [step_runLocal]. *)
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
Local Hint Constructors step stepL : core.
|
2016-04-21 23:12:02 +00:00
|
|
|
|
|
|
|
Lemma run_Return : forall h l r h' l' c,
|
|
|
|
step^* (h, l, Return r) (h', l', c)
|
|
|
|
-> h' = h /\ l' = l /\ c = Return r.
|
|
|
|
Proof.
|
|
|
|
induct 1; eauto.
|
|
|
|
invert H.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma run_Bind : forall h l c1 c2 h' l' c',
|
|
|
|
step^* (h, l, Bind c1 c2) (h', l', c')
|
|
|
|
-> (exists c1', step^* (h, l, c1) (h', l', c1')
|
|
|
|
/\ c' = Bind c1' c2)
|
|
|
|
\/ (exists h'' l'' r, step^* (h, l, c1) (h'', l'', Return r)
|
|
|
|
/\ step^* (h'', l'', c2 r) (h', l', c')).
|
|
|
|
Proof.
|
|
|
|
induct 1; eauto.
|
|
|
|
invert H; eauto 10.
|
|
|
|
|
|
|
|
Ltac inst H :=
|
|
|
|
repeat match type of H with
|
|
|
|
| _ = _ -> _ => specialize (H eq_refl)
|
|
|
|
| forall x : ?T, _ =>
|
|
|
|
let y := fresh in evar (y : T); let y' := eval unfold y in y in
|
|
|
|
specialize (H y'); clear y
|
|
|
|
end.
|
|
|
|
|
|
|
|
inst IHtrc.
|
|
|
|
first_order; eauto 10.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma StepBindRecur_star : forall c1 c1' c2 h h' l l',
|
|
|
|
step^* (h, l, c1) (h', l', c1')
|
|
|
|
-> step^* (h, l, Bind c1 c2) (h', l', Bind c1' c2).
|
|
|
|
Proof.
|
|
|
|
induct 1; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma StepParRecur1_star : forall h l c1 c2 h' l' c1',
|
|
|
|
step^* (h, l, c1) (h', l', c1')
|
|
|
|
-> step^* (h, l, Par c1 c2) (h', l', Par c1' c2).
|
|
|
|
Proof.
|
|
|
|
induct 1; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma StepParRecur2_star : forall h l c1 c2 h' l' c2',
|
|
|
|
step^* (h, l, c2) (h', l', c2')
|
|
|
|
-> step^* (h, l, Par c1 c2) (h', l', Par c1 c2').
|
|
|
|
Proof.
|
|
|
|
induct 1; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
eauto.
|
|
|
|
Qed.
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
Local Hint Resolve StepBindRecur_star StepParRecur1_star StepParRecur2_star : core.
|
2016-04-21 23:12:02 +00:00
|
|
|
|
|
|
|
Lemma runLocal_idem : forall c, runLocal (runLocal c) = runLocal c.
|
|
|
|
Proof.
|
|
|
|
induct c; simplify; eauto.
|
|
|
|
cases (runLocal c); simplify; eauto.
|
|
|
|
rewrite IHc; auto.
|
|
|
|
rewrite IHc; auto.
|
2016-04-24 17:01:16 +00:00
|
|
|
equality.
|
2016-04-22 00:35:34 +00:00
|
|
|
Qed.
|
|
|
|
|
2017-05-01 00:05:19 +00:00
|
|
|
(* The key correctness property: when an original step takes place, either it
|
2016-04-24 19:29:21 +00:00
|
|
|
* has no effect or can be duplicated when we apply [runLocal] both *before* and
|
|
|
|
* *after* the step. *)
|
2016-04-21 23:12:02 +00:00
|
|
|
Lemma step_runLocal : forall h l c h' l' c',
|
|
|
|
step (h, l, c) (h', l', c')
|
|
|
|
-> (runLocal c = runLocal c' /\ h = h' /\ l = l')
|
|
|
|
\/ exists c'', step (h, l, runLocal c) (h', l', c'')
|
|
|
|
/\ runLocal c'' = runLocal c'.
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify; first_order; eauto.
|
|
|
|
|
|
|
|
rewrite H0; equality.
|
|
|
|
|
|
|
|
cases (runLocal c1).
|
|
|
|
invert H0.
|
|
|
|
rewrite <- H1; eauto.
|
|
|
|
rewrite <- H1; eauto.
|
|
|
|
rewrite <- H1; eauto.
|
|
|
|
rewrite <- H1; eauto.
|
|
|
|
rewrite <- H1; eauto.
|
|
|
|
rewrite <- H1; eauto.
|
|
|
|
rewrite <- H1; eauto.
|
|
|
|
|
|
|
|
rewrite H0; equality.
|
|
|
|
|
2016-04-24 17:01:16 +00:00
|
|
|
right; eexists; propositional.
|
2016-04-21 23:12:02 +00:00
|
|
|
eauto.
|
|
|
|
simplify.
|
2016-04-24 17:01:16 +00:00
|
|
|
rewrite runLocal_idem; equality.
|
2016-04-21 23:12:02 +00:00
|
|
|
equality.
|
2016-04-24 17:01:16 +00:00
|
|
|
right; eexists; propositional.
|
2016-04-21 23:12:02 +00:00
|
|
|
eauto.
|
|
|
|
simplify.
|
2016-04-24 17:01:16 +00:00
|
|
|
rewrite runLocal_idem; equality.
|
2016-04-21 23:12:02 +00:00
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* That was the main punchline. Commentary resumes at [step_stepL]. *)
|
|
|
|
|
2016-04-21 23:12:02 +00:00
|
|
|
Lemma step_stepL' : forall h l c h' l' c',
|
|
|
|
step^* (h, l, c) (h', l', c')
|
|
|
|
-> stepL^* (h, l, runLocal c) (h', l', runLocal c').
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
inst IHtrc.
|
|
|
|
apply step_runLocal in H; first_order; subst.
|
|
|
|
rewrite H; eauto.
|
|
|
|
econstructor.
|
|
|
|
econstructor.
|
|
|
|
eauto.
|
|
|
|
equality.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem notAboutToFail_runLocal : forall c,
|
2016-04-22 00:35:34 +00:00
|
|
|
notAboutToFail (runLocal c) = true
|
|
|
|
-> notAboutToFail c = true.
|
2016-04-21 23:12:02 +00:00
|
|
|
Proof.
|
|
|
|
induct c; simplify; auto.
|
|
|
|
cases (runLocal c); simplify; auto.
|
2016-04-22 21:58:14 +00:00
|
|
|
cases (runLocal c1); simplify; auto; propositional;
|
|
|
|
repeat match goal with
|
|
|
|
| [ H : _ |- _ ] => apply andb_true_iff in H; propositional
|
|
|
|
| [ H : _ = _ |- _ ] => rewrite H
|
|
|
|
end; try equality.
|
2016-04-21 23:12:02 +00:00
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* The key proof principle: to verify a can-never-fail invariant for the
|
|
|
|
* original semantics, it suffices to verify it for the new semantics
|
|
|
|
* instead. *)
|
2016-04-21 23:12:02 +00:00
|
|
|
Theorem step_stepL : forall h l c ,
|
|
|
|
invariantFor (trsys_ofL h l c) (fun p => let '(_, _, c) := p in
|
2016-04-22 00:35:34 +00:00
|
|
|
notAboutToFail c = true)
|
2016-04-21 23:12:02 +00:00
|
|
|
-> invariantFor (trsys_of h l c) (fun p =>
|
|
|
|
let '(_, _, c) := p in
|
2016-04-22 00:35:34 +00:00
|
|
|
notAboutToFail c = true).
|
2016-04-21 23:12:02 +00:00
|
|
|
Proof.
|
|
|
|
unfold invariantFor; simplify.
|
|
|
|
propositional; subst.
|
|
|
|
|
|
|
|
cases s'.
|
|
|
|
cases p.
|
|
|
|
apply step_stepL' in H1.
|
|
|
|
apply H in H1; eauto using notAboutToFail_runLocal.
|
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Now watch as we verify that last example in fewer steps, with a smaller
|
|
|
|
* invariant! *)
|
2017-05-01 02:05:28 +00:00
|
|
|
Theorem two_increments_ok_again :
|
2016-04-21 23:12:02 +00:00
|
|
|
invariantFor (trsys_of $0 {} two_increments)
|
|
|
|
(fun p => let '(_, _, c) := p in
|
2016-04-22 00:35:34 +00:00
|
|
|
notAboutToFail c = true).
|
2016-04-21 23:12:02 +00:00
|
|
|
Proof.
|
2016-04-24 19:29:21 +00:00
|
|
|
apply step_stepL.
|
2016-04-21 23:12:02 +00:00
|
|
|
unfold two_increments, two_increments_thread.
|
|
|
|
simplify.
|
|
|
|
eapply invariant_weaken.
|
|
|
|
apply multiStepClosure_ok; simplify.
|
|
|
|
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
2020-04-20 15:56:23 +00:00
|
|
|
model_check_step.
|
2016-04-21 23:12:02 +00:00
|
|
|
model_check_done.
|
|
|
|
|
2016-04-22 22:25:07 +00:00
|
|
|
simplify.
|
|
|
|
propositional; subst; equality.
|
2017-05-01 02:05:28 +00:00
|
|
|
Qed.
|
2016-04-22 22:25:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
(** * Optimization #2: partial-order reduction *)
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* There was a key property lurking behind the soundness proof of our last
|
|
|
|
* optimization: *commutativity*, one of the most common ways to tame the
|
|
|
|
* state-space explosion from concurrency scheduling. Specifically, the local
|
|
|
|
* steps performed by [runLocal] all *commute* with any steps taken in other
|
|
|
|
* threads, because they are agnostic to shared state. Can we generalize the
|
|
|
|
* technique to also harness commutativity of operations that *do* depend on the
|
|
|
|
* shared state, but in particular controlled ways? Why, yes we can! The most
|
|
|
|
* popular such technique from the model-checking world is
|
2018-05-01 23:59:02 +00:00
|
|
|
* *partial-order reduction*. *)
|
2016-04-24 19:29:21 +00:00
|
|
|
|
|
|
|
(* First, here's an example where we should be able to do better than allowing
|
|
|
|
* either thread to run in every step, as we model-check. *)
|
2016-04-22 22:25:07 +00:00
|
|
|
Example independent_threads :=
|
2016-04-22 23:11:42 +00:00
|
|
|
(a <- Read 0;
|
|
|
|
_ <- Write 1 (a + 1);
|
|
|
|
a <- Read 1;
|
|
|
|
if a ==n 1 then
|
|
|
|
Return 0
|
|
|
|
else
|
|
|
|
Fail)
|
|
|
|
|| (b <- Read 2;
|
2017-05-01 00:05:19 +00:00
|
|
|
Write 2 (b + 1)).
|
2016-04-22 22:25:07 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Unfortunately, our existing model-checker does in fact follow the
|
|
|
|
* "exponential" strategy to build the state space. *)
|
2017-05-01 02:05:28 +00:00
|
|
|
Theorem independent_threads_ok :
|
2016-04-22 22:25:07 +00:00
|
|
|
invariantFor (trsys_of $0 {} independent_threads)
|
|
|
|
(fun p => let '(_, _, c) := p in
|
|
|
|
notAboutToFail c = true).
|
|
|
|
Proof.
|
2016-04-24 19:29:21 +00:00
|
|
|
apply step_stepL.
|
2016-04-22 22:25:07 +00:00
|
|
|
unfold independent_threads.
|
|
|
|
simplify.
|
|
|
|
eapply invariant_weaken.
|
|
|
|
apply multiStepClosure_ok; simplify.
|
|
|
|
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
|
|
|
model_check_step.
|
2020-04-20 15:56:23 +00:00
|
|
|
model_check_step.
|
2016-04-22 22:25:07 +00:00
|
|
|
model_check_done.
|
|
|
|
|
2016-04-21 17:42:30 +00:00
|
|
|
simplify.
|
2016-04-22 00:35:34 +00:00
|
|
|
propositional; subst; equality.
|
2017-05-01 02:05:28 +00:00
|
|
|
Qed.
|
2016-04-22 23:11:42 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* It turns out that we can actually do model-checking where at each point we
|
|
|
|
* only explore the result of running *the first thread that is ready*! Such a
|
|
|
|
* strategy isn't sound for all programs, but it is for our example here. Why?
|
|
|
|
* Every pair of atomic actions between threads *commutes*. That is, neither
|
|
|
|
* one affects whether the other is enabled to execute (the way that one [Lock]
|
|
|
|
* can disable another), and running the two actions in either order modifies
|
|
|
|
* shared state identically. In such a case, we may always pick our favorite
|
|
|
|
* thread to step next. *)
|
|
|
|
|
2018-03-30 19:35:57 +00:00
|
|
|
(* To make all that formal, we will do some static program analysis to summarize
|
2016-04-24 19:29:21 +00:00
|
|
|
* which atomic actions a thread might take. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
Record summary := {
|
|
|
|
Reads : set nat;
|
|
|
|
Writes : set nat;
|
|
|
|
Locks : set nat
|
|
|
|
}.
|
|
|
|
|
2017-05-01 00:05:19 +00:00
|
|
|
(* Here is a relation to check the accuracy of a summary. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
Inductive summarize : cmd -> summary -> Prop :=
|
|
|
|
| SumReturn : forall r s,
|
|
|
|
summarize (Return r) s
|
|
|
|
| SumFail : forall s,
|
|
|
|
summarize Fail s
|
|
|
|
| SumBind : forall c1 c2 s,
|
|
|
|
summarize c1 s
|
|
|
|
-> (forall r, summarize (c2 r) s)
|
2017-05-01 00:05:19 +00:00
|
|
|
(* Note the approximation here: we consider all [r] values, even if some
|
|
|
|
* are semantically impossible. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
-> summarize (Bind c1 c2) s
|
|
|
|
| SumRead : forall a s,
|
|
|
|
a \in s.(Reads)
|
|
|
|
-> summarize (Read a) s
|
|
|
|
| SumWrite : forall a v s,
|
|
|
|
a \in s.(Writes)
|
|
|
|
-> summarize (Write a v) s
|
|
|
|
| SumLock : forall a s,
|
|
|
|
a \in s.(Locks)
|
|
|
|
-> summarize (Lock a) s
|
|
|
|
| SumUnlock : forall a s,
|
|
|
|
a \in s.(Locks)
|
2017-05-01 00:05:19 +00:00
|
|
|
-> summarize (Unlock a) s
|
|
|
|
| SumPar : forall c1 c2 s,
|
|
|
|
summarize c1 s
|
|
|
|
-> summarize c2 s
|
|
|
|
-> summarize (Par c1 c2) s.
|
2016-04-22 23:11:42 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* To check commutativity, it is helpful to know which atomic command a thread
|
2017-05-01 00:05:19 +00:00
|
|
|
* could run next. We skip coverage of parallel compositions to make things
|
|
|
|
* simple. *)
|
2016-04-22 23:11:42 +00:00
|
|
|
Inductive nextAction : cmd -> cmd -> Prop :=
|
2016-04-24 01:09:53 +00:00
|
|
|
| NaReturn : forall r,
|
|
|
|
nextAction (Return r) (Return r)
|
|
|
|
| NaFail :
|
|
|
|
nextAction Fail Fail
|
2016-04-22 23:11:42 +00:00
|
|
|
| NaRead : forall a,
|
|
|
|
nextAction (Read a) (Read a)
|
|
|
|
| NaWrite : forall a v,
|
|
|
|
nextAction (Write a v) (Write a v)
|
|
|
|
| NaLock : forall l,
|
|
|
|
nextAction (Lock l) (Lock l)
|
|
|
|
| NaUnlock : forall l,
|
|
|
|
nextAction (Unlock l) (Unlock l)
|
|
|
|
| NaBind : forall c1 c2 c,
|
|
|
|
nextAction c1 c
|
|
|
|
-> nextAction (Bind c1 c2) c.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* We can succinctly capture which summaries describe threads that will commute
|
|
|
|
* with a particular atomic action. The guarantee applies not just to the
|
|
|
|
* thread's first action but also to all others that it might reach later in
|
|
|
|
* execution. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
Definition commutes (c : cmd) (s : summary) : Prop :=
|
|
|
|
match c with
|
|
|
|
| Return _ => True
|
|
|
|
| Fail => True
|
|
|
|
| Read a => ~a \in s.(Writes)
|
|
|
|
| Write a _ => ~a \in s.(Reads) \cup s.(Writes)
|
|
|
|
| Lock a => ~a \in s.(Locks)
|
|
|
|
| Unlock a => ~a \in s.(Locks)
|
|
|
|
| _ => False
|
|
|
|
end.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Now the new semantics: *)
|
2022-04-18 16:58:53 +00:00
|
|
|
Inductive stepC (s : summary) : heap * locks * cmd -> heap * locks * cmd -> Prop :=
|
2016-04-24 19:29:21 +00:00
|
|
|
|
|
|
|
(* It is always OK to let the first thread run. *)
|
2017-05-01 00:05:19 +00:00
|
|
|
| StepFirst : forall h l c1 h' l' c1' c2,
|
|
|
|
step (h, l, c1) (h', l', c1')
|
|
|
|
-> stepC s (h, l, c1 || c2) (h', l', c1' || c2)
|
2016-04-24 19:29:21 +00:00
|
|
|
|
|
|
|
(* However, you may only pick another thread to run if it would be unsound to
|
|
|
|
* consider just the first thread. The negation of the soundness condition is
|
|
|
|
* expressed in the first premise below. *)
|
2017-05-01 00:05:19 +00:00
|
|
|
| StepAny : forall h l c2 h' l' c2' c1,
|
|
|
|
(forall c0 h'' l'' c1'', nextAction c1 c0
|
|
|
|
(* The first thread [c] has some atomic action [c0]
|
|
|
|
* ready to run. *)
|
|
|
|
-> commutes c0 s
|
|
|
|
(* All other threads only contain actions that commute
|
|
|
|
* with [c0]. *)
|
2016-04-24 19:29:21 +00:00
|
|
|
|
2017-05-01 00:05:19 +00:00
|
|
|
-> step (h, l, c1) (h'', l'', c1'')
|
2020-04-20 15:56:23 +00:00
|
|
|
(* Finally, [c] is actually enabled to run, which
|
2017-05-01 00:05:19 +00:00
|
|
|
* might not be the case if [c0] is a locking
|
|
|
|
* command. *)
|
2016-04-24 19:29:21 +00:00
|
|
|
|
2017-05-01 00:05:19 +00:00
|
|
|
-> False)
|
2016-04-24 19:29:21 +00:00
|
|
|
|
|
|
|
(* If we passed that check, then we can step a single thread as expected! *)
|
2017-05-01 00:05:19 +00:00
|
|
|
-> step (h, l, c2) (h', l', c2')
|
|
|
|
-> stepC s (h, l, c1 || c2) (h', l', c1 || c2').
|
2016-04-24 01:09:53 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Notice how this definition turns the partial-order-reduction optimization
|
|
|
|
* "off and on" during state-space exploration. We only restrict our attention
|
|
|
|
* to the first thread so long as the soundness condition above is true. *)
|
|
|
|
|
2017-05-01 00:05:19 +00:00
|
|
|
Definition trsys_ofC (s : summary) (h : heap) (l : locks) (c : cmd) := {|
|
|
|
|
Initial := {(h, l, c)};
|
|
|
|
Step := stepC s
|
2016-04-24 01:09:53 +00:00
|
|
|
|}.
|
|
|
|
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Now we come to quite a few fairly complex lemmas.
|
|
|
|
* First, [commutes] really does allow other commands to swap order with the
|
|
|
|
* atomic action in question. *)
|
2016-04-24 17:56:33 +00:00
|
|
|
Lemma commutes_sound' : forall h l c2 h' l' c2',
|
2016-04-22 23:11:42 +00:00
|
|
|
step (h, l, c2) (h', l', c2')
|
2016-04-24 01:09:53 +00:00
|
|
|
-> forall s c1 h'' l'' c1', step (h', l', c1) (h'', l'', c1')
|
|
|
|
-> summarize c2 s
|
|
|
|
-> commutes c1 s
|
2016-04-22 23:11:42 +00:00
|
|
|
-> exists h1 l1, step (h, l, c1) (h1, l1, c1')
|
|
|
|
/\ step (h1, l1, c2) (h'', l'', c2').
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
|
|
|
|
invert H1.
|
2016-04-24 01:09:53 +00:00
|
|
|
eapply IHstep in H0; eauto; first_order.
|
2016-04-22 23:11:42 +00:00
|
|
|
eauto.
|
|
|
|
|
2016-04-24 01:09:53 +00:00
|
|
|
invert H0; invert H; simplify; propositional; eauto.
|
2016-04-22 23:11:42 +00:00
|
|
|
do 2 eexists; propositional.
|
|
|
|
eauto.
|
2016-04-24 01:09:53 +00:00
|
|
|
assert (a <> a0) by sets.
|
|
|
|
replace (h' $! a) with (h' $+ (a0, v) $! a) by (simplify; equality).
|
2016-04-22 23:11:42 +00:00
|
|
|
eauto.
|
|
|
|
|
2016-04-24 01:09:53 +00:00
|
|
|
invert H0; invert H; simplify; propositional; eauto.
|
2016-04-22 23:11:42 +00:00
|
|
|
do 2 eexists; propositional.
|
|
|
|
eauto.
|
2016-04-24 01:09:53 +00:00
|
|
|
assert (a <> a0) by sets.
|
|
|
|
replace (h $+ (a, v) $+ (a0, v0)) with (h $+ (a0, v0) $+ (a, v)) by maps_equal.
|
2016-04-22 23:11:42 +00:00
|
|
|
eauto.
|
|
|
|
|
|
|
|
invert H1.
|
2017-05-01 01:26:05 +00:00
|
|
|
eapply IHstep in H0; clear IHstep; eauto.
|
|
|
|
first_order.
|
|
|
|
eauto 10.
|
2016-04-22 23:11:42 +00:00
|
|
|
|
|
|
|
invert H1.
|
2017-05-01 01:26:05 +00:00
|
|
|
eapply IHstep in H0; clear IHstep; eauto.
|
|
|
|
first_order.
|
|
|
|
eauto 10.
|
2016-04-22 23:11:42 +00:00
|
|
|
|
2016-04-24 01:09:53 +00:00
|
|
|
invert H1; invert H0; simplify; propositional; eauto.
|
2016-04-22 23:11:42 +00:00
|
|
|
do 2 eexists; propositional.
|
|
|
|
constructor.
|
|
|
|
sets.
|
2017-03-07 19:59:56 +00:00
|
|
|
replace ({a0, a} \cup l) with ({a} \cup ({a0} \cup l)) by sets.
|
2016-04-22 23:11:42 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
do 2 eexists; propositional.
|
|
|
|
constructor.
|
|
|
|
sets; propositional.
|
2017-03-07 19:59:56 +00:00
|
|
|
replace ({a} \cup l \setminus {a0}) with ({a} \cup (l \setminus {a0})) by sets.
|
2016-04-22 23:11:42 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
|
2016-04-24 01:09:53 +00:00
|
|
|
invert H1; invert H0; simplify; propositional; eauto.
|
2016-04-22 23:11:42 +00:00
|
|
|
do 2 eexists; propositional.
|
|
|
|
constructor.
|
|
|
|
sets.
|
2017-03-07 19:59:56 +00:00
|
|
|
replace ({a0} \cup (l \setminus {a})) with (({a0} \cup l) \setminus {a}) by sets.
|
2016-04-22 23:11:42 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
do 2 eexists; propositional.
|
|
|
|
constructor.
|
|
|
|
sets; propositional.
|
2016-04-24 01:09:53 +00:00
|
|
|
replace ((l \setminus {a}) \setminus {a0}) with ((l \setminus {a0}) \setminus {a}) by sets.
|
2016-04-22 23:11:42 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Commentary now resumes at [commutes_sound]. *)
|
|
|
|
|
2016-04-24 17:56:33 +00:00
|
|
|
Lemma step_nextAction_Return : forall r h l c h' l' c',
|
|
|
|
step (h, l, c) (h', l', c')
|
|
|
|
-> nextAction c (Return r)
|
|
|
|
-> h' = h /\ l' = l /\ (forall h'' l'', step (h'', l'', c) (h'', l'', c')).
|
|
|
|
Proof.
|
|
|
|
induct 1; invert 1; propositional; eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma step_nextAction_other : forall c0 h l c h' l' c',
|
|
|
|
step (h, l, c) (h', l', c')
|
|
|
|
-> nextAction c c0
|
|
|
|
-> (forall r, c0 <> Return r)
|
|
|
|
-> exists f c0', step (h, l, c0) (h', l', c0')
|
|
|
|
/\ c = f c0
|
|
|
|
/\ c' = f c0'
|
|
|
|
/\ forall h1 l1 h2 l2 c0'', step (h1, l1, c0) (h2, l2, c0'')
|
|
|
|
-> step (h1, l1, f c0) (h2, l2, f c0'').
|
|
|
|
Proof.
|
|
|
|
induct 1; invert 1; first_order; subst; eauto.
|
|
|
|
|
|
|
|
exists (fun X => x <- x X; c2 x); eauto 10.
|
|
|
|
|
|
|
|
invert H3.
|
|
|
|
unfold not in *; exfalso; eauto.
|
|
|
|
|
|
|
|
exists (fun X => X); eauto.
|
|
|
|
|
|
|
|
exists (fun X => X); eauto.
|
|
|
|
|
|
|
|
exists (fun X => X); eauto 10.
|
|
|
|
|
|
|
|
exists (fun X => X); eauto 10.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma nextAction_couldBe : forall c c0,
|
|
|
|
nextAction c c0
|
|
|
|
-> match c0 with
|
|
|
|
| Return _ => True
|
|
|
|
| Fail => True
|
|
|
|
| Read _ => True
|
|
|
|
| Write _ _ => True
|
|
|
|
| Lock _ => True
|
|
|
|
| Unlock _ => True
|
|
|
|
| _ => False
|
|
|
|
end.
|
|
|
|
Proof.
|
|
|
|
induct 1; auto.
|
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* [commutes] allows order-swapping even when the atomic action is embedded
|
|
|
|
* further within the structure of a larger command. *)
|
2016-04-24 17:56:33 +00:00
|
|
|
Lemma commutes_sound : forall h l c2 h' l' c2',
|
|
|
|
step (h, l, c2) (h', l', c2')
|
|
|
|
-> forall s c1 c0 h'' l'' c1', step (h', l', c1) (h'', l'', c1')
|
|
|
|
-> summarize c2 s
|
|
|
|
-> nextAction c1 c0
|
|
|
|
-> commutes c0 s
|
|
|
|
-> exists h1 l1, step (h, l, c1) (h1, l1, c1')
|
|
|
|
/\ step (h1, l1, c2) (h'', l'', c2').
|
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
|
|
|
|
assert (Hc : commutes c0 s) by assumption.
|
|
|
|
specialize (nextAction_couldBe H2).
|
|
|
|
cases c0; propositional.
|
|
|
|
|
|
|
|
assert (Hs : step (h', l', c1) (h'', l'', c1')) by assumption.
|
|
|
|
eapply step_nextAction_Return in H0; eauto; propositional; subst.
|
|
|
|
eauto.
|
|
|
|
|
|
|
|
eapply step_nextAction_other in H0; eauto; first_order; subst; try equality.
|
|
|
|
eapply commutes_sound' with (c2 := c2) (c1 := Read a) in H3; eauto.
|
|
|
|
first_order; eauto.
|
|
|
|
|
|
|
|
eapply step_nextAction_other in H0; eauto; first_order; subst; try equality.
|
2016-04-25 13:00:28 +00:00
|
|
|
eapply commutes_sound' with (c2 := c2) (c1 := Write a v) in H; eauto; try solve [ simplify; sets ].
|
2016-04-24 17:56:33 +00:00
|
|
|
first_order; eauto.
|
|
|
|
|
|
|
|
eapply step_nextAction_other in H0; eauto; first_order; subst; try equality.
|
|
|
|
invert H0.
|
|
|
|
|
|
|
|
eapply step_nextAction_other in H0; eauto; first_order; subst; try equality.
|
|
|
|
eapply commutes_sound' with (c2 := c2) (c1 := Lock a) in H3; eauto.
|
|
|
|
first_order; eauto.
|
|
|
|
|
|
|
|
eapply step_nextAction_other in H0; eauto; first_order; subst; try equality.
|
|
|
|
eapply commutes_sound' with (c2 := c2) (c1 := Unlock a) in H3; eauto.
|
|
|
|
first_order; eauto.
|
|
|
|
Qed.
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
Local Hint Constructors summarize : core.
|
2016-04-22 23:11:42 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* The next two lemmas show that, once a summary is accurate for a command, it
|
|
|
|
* remains accurate throughout the whole execution lifetime of the command. *)
|
|
|
|
|
2016-04-24 01:09:53 +00:00
|
|
|
Lemma summarize_step : forall h l c h' l' c' s,
|
|
|
|
step (h, l, c) (h', l', c')
|
|
|
|
-> summarize c s
|
|
|
|
-> summarize c' s.
|
|
|
|
Proof.
|
|
|
|
induct 1; invert 1; simplify; eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma summarize_steps : forall h l c h' l' c' s,
|
|
|
|
step^* (h, l, c) (h', l', c')
|
|
|
|
-> summarize c s
|
|
|
|
-> summarize c' s.
|
|
|
|
Proof.
|
|
|
|
induct 1; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
eauto using summarize_step.
|
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* The next technical device will require that we bound how many steps of
|
|
|
|
* execution particular commands could run for. We use a conservative
|
2017-05-14 19:43:21 +00:00
|
|
|
* overapproximation that is easy to compute, phrased as a relation. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
|
|
|
|
Inductive boundRunningTime : cmd -> nat -> Prop :=
|
2016-04-24 18:29:28 +00:00
|
|
|
| BrtReturn : forall r n,
|
|
|
|
boundRunningTime (Return r) n
|
|
|
|
| BrtFail : forall n,
|
|
|
|
boundRunningTime Fail n
|
|
|
|
| BrtRead : forall a n,
|
|
|
|
boundRunningTime (Read a) (S n)
|
|
|
|
| BrtWrite : forall a v n,
|
|
|
|
boundRunningTime (Write a v) (S n)
|
|
|
|
| BrtLock : forall a n,
|
|
|
|
boundRunningTime (Lock a) (S n)
|
|
|
|
| BrtUnlock : forall a n,
|
|
|
|
boundRunningTime (Unlock a) (S n)
|
2016-04-24 01:09:53 +00:00
|
|
|
| BrtBind : forall c1 c2 n1 n2,
|
|
|
|
boundRunningTime c1 n1
|
|
|
|
-> (forall r, boundRunningTime (c2 r) n2)
|
|
|
|
-> boundRunningTime (Bind c1 c2) (S (n1 + n2))
|
|
|
|
| BrtPar : forall c1 c2 n1 n2,
|
|
|
|
boundRunningTime c1 n1
|
|
|
|
-> boundRunningTime c2 n2
|
2017-05-14 19:43:21 +00:00
|
|
|
-> boundRunningTime (Par c1 c2) (S (n1 + n2)).
|
|
|
|
(* The extra [S] here may seem superfluous, but it helps make a certain
|
|
|
|
* induction well-founded. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Perhaps surprisingly, there exist commands that have no finite time bounds!
|
|
|
|
* Mixed-embedding languages often have these counterintuitive properties. *)
|
2020-02-02 22:16:19 +00:00
|
|
|
Fixpoint scribbly (n : nat) : cmd :=
|
|
|
|
match n with
|
|
|
|
| O => Return 0
|
|
|
|
| S n' => _ <- Write n' 0; scribbly n'
|
|
|
|
end.
|
|
|
|
|
|
|
|
Lemma scribbly_time : forall n m,
|
|
|
|
boundRunningTime (scribbly n) m
|
|
|
|
-> m >= n.
|
2016-04-24 18:38:05 +00:00
|
|
|
Proof.
|
2020-02-02 22:16:19 +00:00
|
|
|
induct n; invert 1; auto.
|
|
|
|
invert H2.
|
|
|
|
specialize (H4 n0).
|
|
|
|
apply IHn in H4.
|
|
|
|
linear_arithmetic.
|
|
|
|
Qed.
|
2016-04-24 18:38:05 +00:00
|
|
|
|
2020-02-02 22:16:19 +00:00
|
|
|
Theorem boundRunningTime_not_total : exists c, forall n, ~boundRunningTime c n.
|
|
|
|
Proof.
|
2016-04-24 18:38:05 +00:00
|
|
|
exists (n <- Read 0; scribbly n); propositional.
|
|
|
|
invert H.
|
|
|
|
specialize (H4 (S n2)).
|
|
|
|
apply scribbly_time in H4.
|
|
|
|
linear_arithmetic.
|
|
|
|
Qed.
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
Local Hint Constructors boundRunningTime : core.
|
2016-04-24 01:09:53 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Key property: taking a step of execution lowers the running-time bound. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
Lemma boundRunningTime_step : forall c n h l h' l',
|
|
|
|
boundRunningTime c n
|
|
|
|
-> forall c', step (h, l, c) (h', l', c')
|
|
|
|
-> exists n', boundRunningTime c' n' /\ n' < n.
|
2016-04-22 23:11:42 +00:00
|
|
|
Proof.
|
|
|
|
induct 1; invert 1; simplify; eauto.
|
2016-04-24 01:09:53 +00:00
|
|
|
|
|
|
|
apply IHboundRunningTime in H4; first_order; subst.
|
|
|
|
eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
linear_arithmetic.
|
|
|
|
|
|
|
|
apply IHboundRunningTime1 in H3; first_order; subst.
|
|
|
|
eauto 6.
|
|
|
|
|
|
|
|
apply IHboundRunningTime2 in H3; first_order; subst.
|
|
|
|
eauto 6.
|
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
Lemma boundRunningTime_steps : forall h l c h' l' c',
|
|
|
|
step^* (h, l, c) (h', l', c')
|
|
|
|
-> forall n, boundRunningTime c n
|
|
|
|
-> exists n', boundRunningTime c' n' /\ n' <= n.
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
specialize (boundRunningTime_step H1 H); first_order.
|
|
|
|
eapply IHtrc in H2; eauto.
|
|
|
|
first_order.
|
|
|
|
eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
(* Here we get a bit naughty and begin to depend on *classical logic*, as with
|
|
|
|
* the *law of the excluded middle*: [forall P, P \/ ~P]. You may not have
|
2021-05-02 16:56:47 +00:00
|
|
|
* noticed that we've rarely applied that principle explicitly so far! *)
|
2016-04-24 01:09:53 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* A very useful property: when a command has bounded running time, any
|
|
|
|
* execution starting from that command can be *completed* to one ending in a
|
|
|
|
* stuck state. This property definitely wouldn't be true without the bound,
|
|
|
|
* if our language had explicit, unbounded loops.
|
|
|
|
*
|
|
|
|
* The fun thing about this proof is that we are essentially using tactics to
|
|
|
|
* define an interpreter for the object language, making arbitrary scheduling
|
|
|
|
* choices. Implicit in the derivation is a proof that this interpreter always
|
|
|
|
* terminates, which we get by strong induction on the running-time bound. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
Theorem complete_trace : forall k c n,
|
|
|
|
boundRunningTime c n
|
|
|
|
-> n <= k
|
|
|
|
-> forall h l, exists h' l' c', step^* (h, l, c) (h', l', c')
|
|
|
|
/\ (forall h'' l'' c'',
|
|
|
|
step (h', l', c') (h'', l'', c'')
|
|
|
|
-> False).
|
|
|
|
Proof.
|
|
|
|
induct k; simplify.
|
|
|
|
invert H; try linear_arithmetic.
|
|
|
|
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
invert H.
|
|
|
|
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
invert H.
|
|
|
|
|
|
|
|
invert H.
|
|
|
|
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
invert H.
|
|
|
|
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
invert H.
|
|
|
|
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply trc_one.
|
|
|
|
eauto.
|
|
|
|
invert H.
|
|
|
|
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply trc_one.
|
|
|
|
eauto.
|
|
|
|
invert H.
|
|
|
|
|
2017-05-01 01:26:05 +00:00
|
|
|
excluded_middle (a \in l).
|
2016-04-24 01:09:53 +00:00
|
|
|
do 3 eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
invert H1.
|
|
|
|
sets.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply trc_one.
|
|
|
|
eauto.
|
|
|
|
invert H1.
|
|
|
|
|
2017-05-01 01:26:05 +00:00
|
|
|
excluded_middle (a \in l).
|
2016-04-24 01:09:53 +00:00
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply trc_one.
|
|
|
|
eauto.
|
|
|
|
invert H1.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
invert H1.
|
|
|
|
sets.
|
|
|
|
|
|
|
|
eapply IHk in H1; eauto; first_order.
|
|
|
|
cases x1.
|
|
|
|
specialize (H2 r).
|
|
|
|
eapply IHk in H2; eauto; first_order.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
eapply trc_trans.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
eapply TrcFront.
|
|
|
|
eauto.
|
|
|
|
eassumption.
|
|
|
|
eauto.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
invert H3.
|
|
|
|
eauto.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
invert H3.
|
|
|
|
eauto.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
invert H3.
|
|
|
|
eauto.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
invert H3.
|
|
|
|
eauto.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
invert H3.
|
|
|
|
eauto.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
invert H3.
|
|
|
|
eauto.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
apply StepBindRecur_star.
|
|
|
|
eassumption.
|
|
|
|
invert H3.
|
|
|
|
eauto.
|
|
|
|
|
|
|
|
assert (Hb1 : boundRunningTime c1 n1) by assumption.
|
|
|
|
assert (Hb2 : boundRunningTime c2 n2) by assumption.
|
2017-05-14 19:43:21 +00:00
|
|
|
eapply IHk in H1; try linear_arithmetic; first_order.
|
2016-04-24 01:09:53 +00:00
|
|
|
invert H.
|
2017-05-14 19:43:21 +00:00
|
|
|
eapply IHk in H2; try linear_arithmetic; first_order.
|
2016-04-24 01:09:53 +00:00
|
|
|
invert H.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
eauto.
|
|
|
|
invert H; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
specialize (boundRunningTime_step Hb2 H3); first_order.
|
2017-05-14 19:43:21 +00:00
|
|
|
assert (boundRunningTime (Par x1 c) (S (n1 + x3))) by eauto.
|
|
|
|
eapply IHk in H6; try linear_arithmetic; first_order.
|
2016-04-24 01:09:53 +00:00
|
|
|
do 3 eexists; propositional.
|
|
|
|
eapply TrcFront.
|
|
|
|
eauto.
|
|
|
|
eassumption.
|
|
|
|
eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
specialize (boundRunningTime_step Hb1 H3); first_order.
|
2017-05-14 19:43:21 +00:00
|
|
|
assert (boundRunningTime (Par c c2) (S (x2 + n2))) by eauto.
|
|
|
|
eapply IHk in H6; try linear_arithmetic; first_order.
|
2016-04-24 01:09:53 +00:00
|
|
|
do 3 eexists; propositional.
|
|
|
|
eapply TrcFront.
|
|
|
|
eauto.
|
|
|
|
eassumption.
|
|
|
|
eauto.
|
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* We will apply completion to traces that end in violation of the
|
|
|
|
* not-about-to-fail invariant. It is important that any extension of such a
|
|
|
|
* trace preserves that property. *)
|
2016-04-24 01:09:53 +00:00
|
|
|
Lemma notAboutToFail_step : forall h l c h' l' c',
|
|
|
|
step (h, l, c) (h', l', c')
|
|
|
|
-> notAboutToFail c = false
|
|
|
|
-> notAboutToFail c' = false.
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify; eauto; try equality.
|
|
|
|
|
|
|
|
apply andb_false_iff in H0.
|
|
|
|
apply andb_false_iff.
|
|
|
|
propositional.
|
|
|
|
|
|
|
|
apply andb_false_iff in H0.
|
|
|
|
apply andb_false_iff.
|
|
|
|
propositional.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma notAboutToFail_steps : forall h l c h' l' c',
|
|
|
|
step^* (h, l, c) (h', l', c')
|
|
|
|
-> notAboutToFail c = false
|
|
|
|
-> notAboutToFail c' = false.
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
cases y.
|
|
|
|
cases p.
|
|
|
|
eauto using notAboutToFail_step.
|
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* One last technical device: we define a variant of [step^*] that tracks how
|
|
|
|
* many steps were made, which will come in handy for induction shortly. *)
|
2016-04-24 12:30:25 +00:00
|
|
|
Inductive stepsi : nat -> heap * locks * cmd -> heap * locks * cmd -> Prop :=
|
|
|
|
| StepsiO : forall st,
|
|
|
|
stepsi O st st
|
|
|
|
| StepsiS : forall st1 st2 st3 i,
|
|
|
|
step st1 st2
|
|
|
|
-> stepsi i st2 st3
|
|
|
|
-> stepsi (S i) st1 st3.
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
Local Hint Constructors stepsi : core.
|
2016-04-24 12:30:25 +00:00
|
|
|
|
|
|
|
Theorem steps_stepsi : forall st1 st2,
|
|
|
|
step^* st1 st2
|
|
|
|
-> exists i, stepsi i st1 st2.
|
|
|
|
Proof.
|
|
|
|
induct 1; first_order; eauto.
|
|
|
|
Qed.
|
|
|
|
|
2021-05-02 16:56:47 +00:00
|
|
|
Local Hint Constructors stepC : core.
|
2016-04-24 19:29:21 +00:00
|
|
|
|
|
|
|
(* The next few lemmas are quite technical. Commentary resumes for
|
|
|
|
* [translate_trace]. *)
|
|
|
|
|
2016-04-24 17:01:16 +00:00
|
|
|
Lemma nextAction_det : forall c c0,
|
|
|
|
nextAction c c0
|
|
|
|
-> forall h l h' l' c', step (h, l, c) (h', l', c')
|
|
|
|
-> forall h'' l'' c'', step (h, l, c) (h'', l'', c'')
|
|
|
|
-> h' = h'' /\ l'' = l' /\ c'' = c'.
|
|
|
|
Proof.
|
|
|
|
induct 1; invert 1; invert 1; auto.
|
|
|
|
|
|
|
|
eapply IHnextAction in H2; eauto.
|
|
|
|
equality.
|
2016-04-24 12:30:25 +00:00
|
|
|
|
2016-04-24 17:01:16 +00:00
|
|
|
invert H2.
|
|
|
|
|
|
|
|
invert H2.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma commute_writes : forall c1 c a s h l1' h' l' v,
|
|
|
|
nextAction c1 c
|
|
|
|
-> a \in Writes s
|
|
|
|
-> commutes c s
|
|
|
|
-> forall c1', step (h, l1', c1) (h', l', c1')
|
|
|
|
-> step (h $+ (a, v), l1', c1) (h' $+ (a, v), l', c1').
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
replace (h' $! a0) with (h' $+ (a, v) $! a0).
|
|
|
|
eauto.
|
|
|
|
assert (a <> a0) by sets.
|
|
|
|
simplify; equality.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
replace (h $+ (a0, v0) $+ (a, v)) with (h $+ (a, v) $+ (a0, v0)).
|
|
|
|
eauto.
|
|
|
|
assert (a <> a0) by sets.
|
|
|
|
maps_equal.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
eauto.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
eauto.
|
|
|
|
|
|
|
|
invert H2; eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma commute_locks : forall c1 c a s h l1' h' l',
|
|
|
|
nextAction c1 c
|
|
|
|
-> a \in Locks s
|
|
|
|
-> commutes c s
|
|
|
|
-> forall c1', step (h, l1', c1) (h', l', c1')
|
2017-03-07 19:59:56 +00:00
|
|
|
-> step (h, {a} \cup l1', c1) (h', {a} \cup l', c1').
|
2016-04-24 17:01:16 +00:00
|
|
|
Proof.
|
|
|
|
induct 1; simplify.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
|
|
|
|
invert H1; eauto.
|
|
|
|
|
|
|
|
invert H1; eauto.
|
|
|
|
|
|
|
|
invert H1.
|
2017-03-07 19:59:56 +00:00
|
|
|
replace ({a} \cup ({l} \cup l1')) with ({l} \cup ({a} \cup l1')) by sets.
|
2016-04-24 17:01:16 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
|
|
|
|
invert H1.
|
2017-03-07 19:59:56 +00:00
|
|
|
replace ({a} \cup (l1' \setminus {l})) with (({a} \cup l1') \setminus {l}) by sets.
|
2016-04-24 17:01:16 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
|
|
|
|
invert H2; eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma commute_unlocks : forall c1 c a s h l1' h' l',
|
|
|
|
nextAction c1 c
|
|
|
|
-> a \in Locks s
|
|
|
|
-> commutes c s
|
|
|
|
-> forall c1', step (h, l1', c1) (h', l', c1')
|
|
|
|
-> step (h, l1' \setminus {a}, c1) (h', l' \setminus {a}, c1').
|
|
|
|
Proof.
|
|
|
|
induct 1; simplify.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
|
|
|
|
invert H1; eauto.
|
|
|
|
|
|
|
|
invert H1; eauto.
|
|
|
|
|
|
|
|
invert H1.
|
2017-03-07 19:59:56 +00:00
|
|
|
replace (({l} \cup l1') \setminus {a}) with ({l} \cup (l1' \setminus {a})) by sets.
|
2016-04-24 17:01:16 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
|
|
|
|
invert H1.
|
|
|
|
replace ((l1' \setminus {l}) \setminus {a}) with ((l1' \setminus {a}) \setminus {l}) by sets.
|
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
|
|
|
|
invert H2; eauto.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Lemma commutes_noblock : forall c c0,
|
|
|
|
nextAction c c0
|
|
|
|
-> forall h l h' l' c', step (h, l, c) (h', l', c')
|
|
|
|
-> forall c1 s, summarize c1 s
|
|
|
|
-> commutes c0 s
|
|
|
|
-> forall h1' l1' c1', step (h, l, c1) (h1', l1', c1')
|
2016-04-24 17:56:33 +00:00
|
|
|
-> exists h'' l'', step (h1', l1', c) (h'', l'', c').
|
2016-04-24 17:01:16 +00:00
|
|
|
Proof.
|
|
|
|
induct 1; invert 1.
|
|
|
|
|
|
|
|
induct 1; simplify; eauto.
|
2016-04-24 17:56:33 +00:00
|
|
|
invert H0.
|
|
|
|
invert H0.
|
|
|
|
invert H3; eauto.
|
|
|
|
invert H1; eauto.
|
|
|
|
invert H1; eauto.
|
|
|
|
assert (a0 <> a) by sets.
|
|
|
|
replace (h' $! a) with (h' $+ (a0, v) $! a) by (simplify; equality).
|
|
|
|
eauto.
|
|
|
|
invert H1; eauto.
|
|
|
|
invert H1; eauto.
|
2017-05-01 01:26:05 +00:00
|
|
|
invert H2; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
invert H0.
|
|
|
|
invert H0.
|
|
|
|
invert H4; eauto.
|
|
|
|
invert H2; eauto.
|
|
|
|
invert H2; eauto.
|
|
|
|
invert H2; eauto.
|
2016-04-24 17:56:33 +00:00
|
|
|
do 2 eexists.
|
2016-04-24 17:01:16 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
invert H2; eauto.
|
2016-04-24 17:56:33 +00:00
|
|
|
do 2 eexists.
|
2016-04-24 17:01:16 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
2017-05-01 01:26:05 +00:00
|
|
|
invert H3; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
invert H0.
|
|
|
|
invert H0.
|
|
|
|
invert H4; eauto.
|
|
|
|
invert H2; eauto.
|
|
|
|
invert H2; eauto.
|
|
|
|
invert H2; eauto.
|
2016-04-24 17:56:33 +00:00
|
|
|
do 2 eexists.
|
2016-04-24 17:01:16 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
|
|
|
invert H2; eauto.
|
2016-04-24 17:56:33 +00:00
|
|
|
do 2 eexists.
|
2016-04-24 17:01:16 +00:00
|
|
|
constructor.
|
|
|
|
sets.
|
2017-05-01 01:26:05 +00:00
|
|
|
invert H3; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
|
|
|
|
induct 1; simplify; eauto.
|
|
|
|
invert H1.
|
|
|
|
invert H1.
|
|
|
|
invert H5; eauto.
|
|
|
|
invert H3; eauto.
|
|
|
|
invert H3.
|
2016-04-24 17:56:33 +00:00
|
|
|
do 2 eexists; eapply commute_writes in H2; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
invert H3.
|
2016-04-24 17:56:33 +00:00
|
|
|
do 2 eexists; eapply commute_locks in H2; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
invert H3.
|
2016-04-24 17:56:33 +00:00
|
|
|
do 2 eexists; eapply commute_unlocks in H2; eauto.
|
2017-05-01 01:26:05 +00:00
|
|
|
invert H1; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
|
2016-04-24 12:30:25 +00:00
|
|
|
eauto.
|
|
|
|
Qed.
|
|
|
|
|
2017-05-01 01:26:05 +00:00
|
|
|
Lemma translate_trace_commute : forall i h l c1 c2 h' l' c',
|
|
|
|
stepsi (S i) (h, l, c1 || c2) (h', l', c')
|
2016-04-24 17:01:16 +00:00
|
|
|
-> (forall h'' l'' c'', step (h', l', c') (h'', l'', c'') -> False)
|
2017-05-01 01:26:05 +00:00
|
|
|
-> forall s, summarize c2 s
|
|
|
|
-> forall x, nextAction c1 x
|
|
|
|
-> commutes x s
|
|
|
|
-> forall x0 x1 x2, step (h, l, c1) (x0, x1, x2)
|
|
|
|
-> exists h'' l'', step (h, l, c1) (h'', l'', x2)
|
|
|
|
/\ stepsi i (h'', l'', x2 || c2) (h', l', c').
|
2016-04-24 17:01:16 +00:00
|
|
|
Proof.
|
|
|
|
induct 1; simplify.
|
|
|
|
invert H0.
|
2017-05-01 01:26:05 +00:00
|
|
|
|
2016-04-24 17:01:16 +00:00
|
|
|
invert H.
|
2017-05-01 01:26:05 +00:00
|
|
|
|
|
|
|
eapply nextAction_det in H5; try eapply H6; eauto; propositional; subst.
|
2016-04-24 17:01:16 +00:00
|
|
|
eauto 10.
|
|
|
|
|
|
|
|
eapply commutes_noblock in H3; eauto.
|
|
|
|
first_order.
|
2017-05-01 01:26:05 +00:00
|
|
|
exfalso; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
|
|
|
|
invert H.
|
2017-05-01 01:26:05 +00:00
|
|
|
|
|
|
|
eapply nextAction_det in H5; try eapply H12; eauto; propositional; subst.
|
2016-04-24 17:01:16 +00:00
|
|
|
eauto 10.
|
|
|
|
|
2017-05-01 01:26:05 +00:00
|
|
|
assert (Hnext : nextAction c1 x) by assumption.
|
|
|
|
eapply commutes_noblock in Hnext; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
first_order.
|
2017-05-01 01:26:05 +00:00
|
|
|
eapply IHstepsi in H; clear IHstepsi; eauto using summarize_step.
|
2016-04-24 17:01:16 +00:00
|
|
|
first_order.
|
2017-05-01 01:26:05 +00:00
|
|
|
eapply commutes_sound with (c1 := c1) (c2 := c2) (c0 := x) in H; eauto.
|
2016-04-24 17:56:33 +00:00
|
|
|
first_order.
|
|
|
|
eauto 10.
|
2016-04-24 17:01:16 +00:00
|
|
|
Qed.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* The heart of the soundness proof! When a length-[i] derivation gets us to a
|
|
|
|
* stuck state that is about to fail, and when we have summarized the program,
|
|
|
|
* we can run that summary in the optimized semantics and also arrive at a state
|
|
|
|
* that is about to fail. Thus, if we explore the optimized state space and
|
|
|
|
* find no failures, we can conclude lack of reachable failures in the original
|
|
|
|
* state space. *)
|
2017-05-01 01:26:05 +00:00
|
|
|
Lemma translate_trace : forall i h l c1 c2 h' l' c',
|
|
|
|
stepsi i (h, l, c1 || c2) (h', l', c')
|
2016-04-24 01:09:53 +00:00
|
|
|
-> (forall h'' l'' c'', step (h', l', c') (h'', l'', c'') -> False)
|
|
|
|
-> notAboutToFail c' = false
|
2017-05-01 01:26:05 +00:00
|
|
|
-> forall s, summarize c2 s
|
|
|
|
-> exists h' l' c'', (stepC s)^* (h, l, c1 || c2) (h', l', c'')
|
|
|
|
/\ notAboutToFail c'' = false.
|
2016-04-24 01:09:53 +00:00
|
|
|
Proof.
|
2016-04-24 12:30:25 +00:00
|
|
|
induct i; simplify.
|
|
|
|
|
|
|
|
invert H.
|
|
|
|
eauto 10.
|
|
|
|
|
2017-05-01 01:26:05 +00:00
|
|
|
excluded_middle (exists c0 h' l' c', nextAction c1 c0
|
|
|
|
/\ commutes c0 s
|
|
|
|
/\ step (h, l, c1) (h', l', c')).
|
2016-04-24 12:30:25 +00:00
|
|
|
|
2016-04-24 17:01:16 +00:00
|
|
|
first_order.
|
|
|
|
eapply translate_trace_commute in H; eauto.
|
|
|
|
first_order.
|
2017-05-01 01:26:05 +00:00
|
|
|
eapply IHi in H6; eauto.
|
2016-04-24 17:01:16 +00:00
|
|
|
first_order.
|
2017-05-01 01:26:05 +00:00
|
|
|
eauto 7.
|
|
|
|
|
|
|
|
invert H.
|
|
|
|
invert H5.
|
2016-04-24 12:30:25 +00:00
|
|
|
|
|
|
|
eapply IHi in H6; eauto.
|
|
|
|
first_order.
|
2017-05-01 01:26:05 +00:00
|
|
|
eauto 7.
|
2016-04-24 01:09:53 +00:00
|
|
|
|
2017-05-01 01:26:05 +00:00
|
|
|
eapply IHi in H6; eauto using summarize_step.
|
|
|
|
first_order.
|
|
|
|
do 3 eexists; propositional.
|
|
|
|
2: apply H4.
|
|
|
|
eauto 10.
|
2016-04-24 01:09:53 +00:00
|
|
|
Qed.
|
|
|
|
|
2017-05-01 01:26:05 +00:00
|
|
|
Require Import Classical.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* This theorem brings it all together, to reduce one invariant-proof problem to
|
|
|
|
* another that uses the optimized semantics. *)
|
2017-05-01 01:26:05 +00:00
|
|
|
Theorem step_stepC : forall h l c1 c2 s n,
|
|
|
|
summarize c2 s
|
|
|
|
-> boundRunningTime (c1 || c2) n
|
|
|
|
-> invariantFor (trsys_ofC s h l (c1 || c2))
|
|
|
|
(fun p => let '(_, _, c) := p in
|
|
|
|
notAboutToFail c = true)
|
|
|
|
-> invariantFor (trsys_of h l (c1 || c2))
|
|
|
|
(fun p =>
|
|
|
|
let '(_, _, c) := p in
|
|
|
|
notAboutToFail c = true).
|
2016-04-24 01:09:53 +00:00
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
apply NNPP; propositional.
|
|
|
|
unfold invariantFor in H2.
|
|
|
|
apply not_all_ex_not in H2; first_order.
|
|
|
|
apply imply_to_and in H2; propositional.
|
|
|
|
apply not_all_ex_not in H4; first_order.
|
|
|
|
apply imply_to_and in H2; propositional.
|
|
|
|
cases x0.
|
|
|
|
cases p.
|
|
|
|
subst.
|
|
|
|
simplify.
|
2017-05-01 01:26:05 +00:00
|
|
|
cases (notAboutToFail c); propositional.
|
|
|
|
assert (exists n', boundRunningTime c n' /\ n' <= n) by eauto using boundRunningTime_steps.
|
2016-04-24 01:09:53 +00:00
|
|
|
first_order.
|
|
|
|
eapply complete_trace in H2; eauto.
|
|
|
|
first_order.
|
|
|
|
specialize (trc_trans H4 H2); simplify.
|
|
|
|
assert (notAboutToFail x2 = false) by eauto using notAboutToFail_steps.
|
|
|
|
unfold invariantFor in H1; simplify.
|
2016-04-24 12:30:25 +00:00
|
|
|
apply steps_stepsi in H7; first_order.
|
2016-04-24 01:09:53 +00:00
|
|
|
eapply translate_trace in H7; eauto.
|
|
|
|
first_order.
|
|
|
|
apply H1 in H7; auto.
|
2017-05-01 01:26:05 +00:00
|
|
|
equality.
|
2016-04-21 17:42:30 +00:00
|
|
|
Qed.
|
2016-04-24 18:29:28 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* Now we define some tactics to help us apply this technique automatically for
|
|
|
|
* concrete programs. As usual, we won't explain how the tactics work. *)
|
|
|
|
|
2016-04-24 18:29:28 +00:00
|
|
|
Ltac analyzer := repeat (match goal with
|
|
|
|
| [ |- context[if ?E then _ else _] ] => cases E
|
|
|
|
| _ => econstructor
|
|
|
|
end; simplify; try solve [ sets ]).
|
|
|
|
|
|
|
|
Ltac por_solve := simplify; repeat econstructor; sets; linear_arithmetic.
|
|
|
|
|
|
|
|
Ltac por_invert1 :=
|
|
|
|
match goal with
|
|
|
|
| [ H : forall (c0 : cmd) (h'' : heap) (l'' : locks) (c'' : cmd), _ -> _ |- _ ] =>
|
2017-05-01 01:26:05 +00:00
|
|
|
(exfalso; eapply H; try por_solve; por_solve) || clear H
|
2016-04-24 18:29:28 +00:00
|
|
|
| _ => model_check_invert1
|
|
|
|
end.
|
|
|
|
|
|
|
|
Ltac por_invert := simplify; repeat por_invert1.
|
|
|
|
|
|
|
|
Ltac por_closure :=
|
|
|
|
repeat (apply oneStepClosure_empty
|
|
|
|
|| (apply oneStepClosure_split; [ por_invert; try equality; solve [ singletoner ] | ])).
|
|
|
|
|
2020-04-20 15:56:23 +00:00
|
|
|
Ltac por_step := eapply MscStep; [ por_closure | simplify ].
|
|
|
|
Ltac por_done := apply MscDone.
|
2016-04-24 18:29:28 +00:00
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* OK, ready to return to our last example! This time we will see state-space
|
|
|
|
* exploration that steps a single thread at a time, where the final invariant
|
|
|
|
* includes no states with multiple *partially executed* threads. *)
|
2016-04-24 18:29:28 +00:00
|
|
|
Theorem independent_threads_ok_again :
|
|
|
|
invariantFor (trsys_of $0 {} independent_threads)
|
|
|
|
(fun p => let '(_, _, c) := p in
|
|
|
|
notAboutToFail c = true).
|
|
|
|
Proof.
|
2016-04-24 19:31:34 +00:00
|
|
|
(* We need to supply the summary when invoking the proof principle, though we
|
2016-04-24 19:29:21 +00:00
|
|
|
* could also have used Ltac to compute it automatically. *)
|
2017-05-01 01:26:05 +00:00
|
|
|
eapply step_stepC with (s := {| Reads := {2};
|
|
|
|
Writes := {2};
|
|
|
|
Locks := {} |}).
|
2016-04-24 18:29:28 +00:00
|
|
|
analyzer.
|
|
|
|
analyzer.
|
|
|
|
|
|
|
|
eapply invariant_weaken.
|
|
|
|
apply multiStepClosure_ok; simplify.
|
|
|
|
|
|
|
|
por_step.
|
|
|
|
por_step.
|
|
|
|
por_step.
|
|
|
|
por_step.
|
|
|
|
por_step.
|
|
|
|
por_step.
|
|
|
|
por_step.
|
|
|
|
por_step.
|
|
|
|
por_step.
|
2020-04-20 15:56:23 +00:00
|
|
|
por_step.
|
2016-04-24 18:29:28 +00:00
|
|
|
por_done.
|
|
|
|
|
|
|
|
sets.
|
|
|
|
|
2016-04-24 19:29:21 +00:00
|
|
|
(* We computed an inexact running time. By filling in zeroes for some
|
|
|
|
* existential variables, we commit to a concrete bound. *)
|
2021-05-02 16:56:47 +00:00
|
|
|
Unshelve.
|
2016-04-24 18:29:28 +00:00
|
|
|
exact 0.
|
|
|
|
exact 0.
|
|
|
|
exact 0.
|
|
|
|
exact 0.
|
|
|
|
exact 0.
|
|
|
|
exact 0.
|
|
|
|
Qed.
|