Skip to content
Snippets Groups Projects
Commit 34d9d94b authored by Ralf Jung's avatar Ralf Jung
Browse files

strengthen cinc spec: flag can be any GC'ed location

parent 787080a0
No related branches found
No related tags found
No related merge requests found
Pipeline #17805 passed
...@@ -15,22 +15,11 @@ Set Default Proof Using "Type". ...@@ -15,22 +15,11 @@ Set Default Proof Using "Type".
(* (*
new_counter() := new_counter() :=
let c = ref (injL 0) in let c = ref (injL 0) in
let f = ref false in ref c
ref (f, c)
*) *)
Definition new_counter : val := Definition new_counter : val :=
λ: <>, λ: <>,
let: "c" := ref (ref (InjL #0)) in ref (ref (InjL #0)).
let: "f" := ref #true in
("f", "c").
(*
set_flag(ctr, b) :=
ctr.1 <- b
*)
Definition set_flag : val :=
λ: "ctr" "b",
Fst "ctr" <- "b".
(* (*
complete(c, f, x, n, p) := complete(c, f, x, n, p) :=
...@@ -38,78 +27,82 @@ Definition set_flag : val := ...@@ -38,78 +27,82 @@ Definition set_flag : val :=
*) *)
Definition complete : val := Definition complete : val :=
λ: "c" "f" "x" "n" "p", λ: "c" "f" "x" "n" "p",
Resolve (CAS "c" "x" (ref (InjL (if: !"f" then "n" + #1 else "n")))) "p" (ref #()) ;; #(). let: "l_ghost" := ref #() in
(* Compare with #true to make this a total operation that never
gets stuck, no matter the value stored in [f]. *)
let: "new_n" := if: !"f" = #true then "n" + #1 else "n" in
Resolve (CAS "c" "x" (ref (InjL "new_n"))) "p" "l_ghost" ;; #().
(* (*
get(c, f) := get c :=
let x = !c in let x = !c in
match !x with match !x with
| injL n => n | injL n => n
| injR (n, p) => complete c f x n p; get(c, f) | injR (f, n, p) => complete c f x n p; get(c, f)
*) *)
Definition get : val := Definition get : val :=
rec: "get" "ctr" := rec: "get" "c" :=
let: "f" := Fst "ctr" in
let: "c" := Snd "ctr" in
let: "x" := !"c" in let: "x" := !"c" in
match: !"x" with match: !"x" with
InjL "n" => "n" InjL "n" => "n"
| InjR "args" => | InjR "args" =>
complete "c" "f" "x" (Fst "args") (Snd "args") ;; let: "f" := Fst (Fst "args") in
"get" "ctr" let: "n" := Snd (Fst "args") in
let: "p" := Snd "args" in
complete "c" "f" "x" "n" "p" ;;
"get" "c"
end. end.
(* (*
cinc(c, f) := cinc c f :=
let x = !c in let x = !c in
match !x with match !x with
| injL n => | injL n =>
let p := new proph in let p := new proph in
let y := ref (injR (n, p)) in let y := ref (injR (n, f, p)) in
if CAS(c, x, y) then complete(c, f, y, n, p) if CAS(c, x, y) then complete(c, f, y, n, p)
else cinc(c, f) else cinc c f
| injR (n, p) => | injR (f', n', p') =>
complete(c, f, x, n, p); complete(c, f', x, n', p');
cinc(c, f) cinc c f
*) *)
Definition cinc : val := Definition cinc : val :=
rec: "cinc" "ctr" := rec: "cinc" "c" "f" :=
let: "f" := Fst "ctr" in
let: "c" := Snd "ctr" in
let: "x" := !"c" in let: "x" := !"c" in
match: !"x" with match: !"x" with
InjL "n" => InjL "n" =>
let: "p" := NewProph in let: "p" := NewProph in
let: "y" := ref (InjR ("n", "p")) in let: "y" := ref (InjR ("f", "n", "p")) in
if: CAS "c" "x" "y" then complete "c" "f" "y" "n" "p" ;; Skip if: CAS "c" "x" "y" then complete "c" "f" "y" "n" "p" ;; Skip
else "cinc" "ctr" else "cinc" "c" "f"
| InjR "args'" => | InjR "args'" =>
complete "c" "f" "x" (Fst "args'") (Snd "args'") ;; let: "f'" := Fst (Fst "args'") in
"cinc" "ctr" let: "n'" := Snd (Fst "args'") in
let: "p'" := Snd "args'" in
complete "c" "f'" "x" "n'" "p'" ;;
"cinc" "c" "f"
end. end.
(** ** Proof setup *) (** ** Proof setup *)
Definition flagUR := authR $ optionUR $ exclR boolO.
Definition numUR := authR $ optionUR $ exclR ZO. Definition numUR := authR $ optionUR $ exclR ZO.
Definition tokenUR := exclR unitO. Definition tokenUR := exclR unitO.
Definition one_shotUR := csumR (exclR unitO) (agreeR unitO). Definition one_shotUR := csumR (exclR unitO) (agreeR unitO).
Class cincG Σ := ConditionalIncrementG { Class cincG Σ := ConditionalIncrementG {
cinc_flagG :> inG Σ flagUR;
cinc_numG :> inG Σ numUR; cinc_numG :> inG Σ numUR;
cinc_tokenG :> inG Σ tokenUR; cinc_tokenG :> inG Σ tokenUR;
cinc_one_shotG :> inG Σ one_shotUR; cinc_one_shotG :> inG Σ one_shotUR;
}. }.
Definition cincΣ : gFunctors := Definition cincΣ : gFunctors :=
#[GFunctor flagUR; GFunctor numUR; GFunctor tokenUR; GFunctor one_shotUR]. #[GFunctor numUR; GFunctor tokenUR; GFunctor one_shotUR].
Instance subG_cincΣ {Σ} : subG cincΣ Σ cincG Σ. Instance subG_cincΣ {Σ} : subG cincΣ Σ cincG Σ.
Proof. solve_inG. Qed. Proof. solve_inG. Qed.
Section conditional_counter. Section conditional_counter.
Context {Σ} `{!heapG Σ, !cincG Σ}. Context {Σ} `{!heapG Σ, !gcG Σ, !cincG Σ}.
Context (N : namespace). Context (N : namespace).
Local Definition stateN := N .@ "state". Local Definition stateN := N .@ "state".
...@@ -124,13 +117,6 @@ Section conditional_counter. ...@@ -124,13 +117,6 @@ Section conditional_counter.
by iDestruct "H" as %[H%Excl_included%leibniz_equiv _]%auth_both_valid. by iDestruct "H" as %[H%Excl_included%leibniz_equiv _]%auth_both_valid.
Qed. Qed.
Lemma sync_flag_values γ_n (b1 b2 : bool) :
own γ_n ( Excl' b1) -∗ own γ_n ( Excl' b2) -∗ b1 = b2⌝.
Proof.
iIntros "H● H◯". iCombine "H●" "H◯" as "H". iDestruct (own_valid with "H") as "H".
by iDestruct "H" as %[H%Excl_included%leibniz_equiv _]%auth_both_valid.
Qed.
Lemma update_counter_value γ_n (n1 n2 m : Z) : Lemma update_counter_value γ_n (n1 n2 m : Z) :
own γ_n ( Excl' n1) -∗ own γ_n ( Excl' n2) ==∗ own γ_n ( Excl' m) own γ_n ( Excl' m). own γ_n ( Excl' n1) -∗ own γ_n ( Excl' n2) ==∗ own γ_n ( Excl' m) own γ_n ( Excl' m).
Proof. Proof.
...@@ -138,16 +124,8 @@ Section conditional_counter. ...@@ -138,16 +124,8 @@ Section conditional_counter.
by apply auth_update, option_local_update, exclusive_local_update. by apply auth_update, option_local_update, exclusive_local_update.
Qed. Qed.
Lemma update_flag_value γ_n (b1 b2 b : bool) : Definition counter_content (γs : gname) (n : Z) :=
own γ_n ( Excl' b1) -∗ own γ_n ( Excl' b2) ==∗ own γ_n ( Excl' b) own γ_n ( Excl' b). (own γs ( Excl' n))%I.
Proof.
iIntros "H● H◯". iCombine "H●" "H◯" as "H". rewrite -own_op. iApply (own_update with "H").
by apply auth_update, option_local_update, exclusive_local_update.
Qed.
Definition counter_content (γs : gname * gname) (b : bool) (n : Z) :=
(own γs.1 ( Excl' b) own γs.2 ( Excl' n))%I.
(** Definition of the invariant *) (** Definition of the invariant *)
...@@ -160,12 +138,12 @@ Section conditional_counter. ...@@ -160,12 +138,12 @@ Section conditional_counter.
Inductive abstract_state : Set := Inductive abstract_state : Set :=
| Quiescent : Z abstract_state | Quiescent : Z abstract_state
| Updating : Z proph_id abstract_state. | Updating : loc Z proph_id abstract_state.
Definition state_to_val (s : abstract_state) : val := Definition state_to_val (s : abstract_state) : val :=
match s with match s with
| Quiescent n => InjLV #n | Quiescent n => InjLV #n
| Updating n p => InjRV (#n,#p) | Updating f n p => InjRV (#f,#n,#p)
end. end.
Global Instance state_to_val_inj : Inj (=) (=) state_to_val. Global Instance state_to_val_inj : Inj (=) (=) state_to_val.
...@@ -206,45 +184,42 @@ Section conditional_counter. ...@@ -206,45 +184,42 @@ Section conditional_counter.
accepted_state Q (val_to_some_loc vs) l_ghost_winner )) accepted_state Q (val_to_some_loc vs) l_ghost_winner ))
own γ_s (Cinr $ to_agree ()) done_state Q l l_ghost_winner γ_t))%I. own γ_s (Cinr $ to_agree ()) done_state Q l l_ghost_winner γ_t))%I.
Definition pau P Q γs := Definition pau P Q γs f :=
( P -∗ AU << (b : bool) (n : Z), counter_content γs b n >> @ ⊤∖↑N, ( P -∗ AU << (b : bool) (n : Z), counter_content γs n gc_mapsto f #b >> @ ⊤∖↑N∖↑gcN,
<< counter_content γs b (if b then n + 1 else n), COMM Q >>)%I. << counter_content γs (if b then n + 1 else n) gc_mapsto f #b, COMM Q >>)%I.
Definition counter_inv γ_b γ_n f c := Definition counter_inv γ_n c :=
( (b : bool) (l : loc) (q : Qp) (s : abstract_state), ( (l : loc) (q : Qp) (s : abstract_state),
(* We own *more than* half of [l], which shows that this cannot (* We own *more than* half of [l], which shows that this cannot
be the [l] of any [state] protocol in the [done] state. *) be the [l] of any [state] protocol in the [done] state. *)
f #b c {1/2} #l l {1/2 + q} (state_to_val s) c {1/2} #l l {1/2 + q} (state_to_val s)
own γ_b ( Excl' b)
match s with match s with
| Quiescent n => c {1/2} #l own γ_n ( Excl' n) | Quiescent n => c {1/2} #l own γ_n ( Excl' n)
| Updating n p => | Updating f n p =>
P Q l_ghost_winner γ_t γ_s, P Q l_ghost_winner γ_t γ_s,
(* There are two pieces of per-[state]-protocol ghost state: (* There are two pieces of per-[state]-protocol ghost state:
- [γ_t] is a token owned by whoever created this protocol and used - [γ_t] is a token owned by whoever created this protocol and used
to get out the [Q] in the end. to get out the [Q] in the end.
- [γ_s] reflects whether the protocol is [done] yet or not. *) - [γ_s] reflects whether the protocol is [done] yet or not. *)
inv stateN (state_inv P Q p n c l l_ghost_winner γ_n γ_t γ_s) inv stateN (state_inv P Q p n c l l_ghost_winner γ_n γ_t γ_s)
pau P Q (γ_b, γ_n) pau P Q γ_n f is_gc_loc f
end)%I. end)%I.
Local Hint Extern 0 (environments.envs_entails _ (counter_inv _ _ _ _)) => unfold counter_inv. Local Hint Extern 0 (environments.envs_entails _ (counter_inv _ _)) => unfold counter_inv.
Definition is_counter (γs : gname * gname) (ctr : val) := Definition is_counter (γ_n : gname) (ctr : val) :=
( (γ_b γ_n : gname) (f c : loc), ( (c : loc), ctr = #c N ## gcN gc_inv inv counterN (counter_inv γ_n c))%I.
γs = (γ_b, γ_n) ctr = (#f, #c)%V
inv counterN (counter_inv γ_b γ_n f c))%I.
Global Instance is_counter_persistent γs ctr : Persistent (is_counter γs ctr) := _. Global Instance is_counter_persistent γs ctr : Persistent (is_counter γs ctr) := _.
Global Instance counter_content_timeless γs b n : Timeless (counter_content γs b n) := _. Global Instance counter_content_timeless γs n : Timeless (counter_content γs n) := _.
Global Instance abstract_state_inhabited: Inhabited abstract_state := populate (Quiescent 0). Global Instance abstract_state_inhabited: Inhabited abstract_state := populate (Quiescent 0).
Lemma counter_content_exclusive γs f1 c1 f2 c2 : Lemma counter_content_exclusive γs c1 c2 :
counter_content γs f1 c1 -∗ counter_content γs f2 c2 -∗ False. counter_content γs c1 -∗ counter_content γs c2 -∗ False.
Proof. Proof.
iIntros "[Hb1 _] [Hb2 _]". iDestruct (own_valid_2 with "Hb1 Hb2") as %?. iIntros "Hb1 Hb2". iDestruct (own_valid_2 with "Hb1 Hb2") as %?.
done. done.
Qed. Qed.
...@@ -281,19 +256,18 @@ Section conditional_counter. ...@@ -281,19 +256,18 @@ Section conditional_counter.
(** ** Proof of [complete] *) (** ** Proof of [complete] *)
(** The part of [complete] for the succeeding thread that moves from [accepted] to [done] state *) (** The part of [complete] for the succeeding thread that moves from [accepted] to [done] state *)
Lemma complete_succeeding_thread_pending (γ_b γ_n γ_t γ_s : gname) f_l c_l P Q p Lemma complete_succeeding_thread_pending (γ_n γ_t γ_s : gname) c_l P Q p
(m n : Z) (l l_ghost l_new : loc) Φ : (m n : Z) (l l_ghost l_new : loc) Φ :
inv counterN (counter_inv γ_b γ_n f_l c_l) -∗ inv counterN (counter_inv γ_n c_l) -∗
inv stateN (state_inv P Q p m c_l l l_ghost γ_n γ_t γ_s) -∗ inv stateN (state_inv P Q p m c_l l l_ghost γ_n γ_t γ_s) -∗
pau P Q (γ_b, γ_n) -∗
l_ghost {1 / 2} #() -∗ l_ghost {1 / 2} #() -∗
((own_token γ_t ={}=∗ Q) -∗ Φ #()) -∗ ((own_token γ_t ={}=∗ Q) -∗ Φ #()) -∗
own γ_n ( Excl' n) -∗ own γ_n ( Excl' n) -∗
l_new InjLV #n -∗ l_new InjLV #n -∗
WP Resolve (CAS #c_l #l #l_new) #p #l_ghost ;; #() {{ v, Φ v }}. WP Resolve (CAS #c_l #l #l_new) #p #l_ghost ;; #() {{ v, Φ v }}.
Proof. Proof.
iIntros "#InvC #InvS PAU Hl_ghost HQ Hn● [Hl_new Hl_new']". wp_bind (Resolve _ _ _)%E. iIntros "#InvC #InvS Hl_ghost HQ Hn● [Hl_new Hl_new']". wp_bind (Resolve _ _ _)%E.
iInv counterN as (b' l' q s) "(>Hf & >Hc & >[Hl Hl'] & Hb● & Hrest)". iInv counterN as (l' q s) "(>Hc & >[Hl Hl'] & Hrest)".
iInv stateN as (vs) "(>Hp & [NotDone | Done])"; last first. iInv stateN as (vs) "(>Hp & [NotDone | Done])"; last first.
{ (* We cannot be [done] yet, as we own the "ghost location" that serves { (* We cannot be [done] yet, as we own the "ghost location" that serves
as token for that transition. *) as token for that transition. *)
...@@ -327,23 +301,22 @@ Section conditional_counter. ...@@ -327,23 +301,22 @@ Section conditional_counter.
iFrame "#∗". iSplitR "Hl"; iExists _; done. } iFrame "#∗". iSplitR "Hl"; iExists _; done. }
iModIntro. iSplitR "HQ". iModIntro. iSplitR "HQ".
{ iNext. iDestruct "Hc" as "[Hc1 Hc2]". { iNext. iDestruct "Hc" as "[Hc1 Hc2]".
iExists _, l_new, _, (Quiescent n). iFrame. } iExists l_new, _, (Quiescent n). iFrame. }
iApply wp_fupd. wp_seq. iApply "HQ". iApply wp_fupd. wp_seq. iApply "HQ".
iApply state_done_extract_Q; done. iApply state_done_extract_Q; done.
Qed. Qed.
(** The part of [complete] for the failing thread *) (** The part of [complete] for the failing thread *)
Lemma complete_failing_thread γ_b γ_n γ_t γ_s f_l c_l l P Q p m n l_ghost_inv l_ghost l_new Φ : Lemma complete_failing_thread γ_n γ_t γ_s c_l l P Q p m n l_ghost_inv l_ghost l_new Φ :
l_ghost_inv l_ghost l_ghost_inv l_ghost
inv counterN (counter_inv γ_b γ_n f_l c_l) -∗ inv counterN (counter_inv γ_n c_l) -∗
inv stateN (state_inv P Q p m c_l l l_ghost_inv γ_n γ_t γ_s) -∗ inv stateN (state_inv P Q p m c_l l l_ghost_inv γ_n γ_t γ_s) -∗
pau P Q (γ_b, γ_n) -∗
((own_token γ_t ={}=∗ Q) -∗ Φ #()) -∗ ((own_token γ_t ={}=∗ Q) -∗ Φ #()) -∗
l_new InjLV #n -∗ l_new InjLV #n -∗
WP Resolve (CAS #c_l #l #l_new) #p #l_ghost ;; #() {{ v, Φ v }}. WP Resolve (CAS #c_l #l #l_new) #p #l_ghost ;; #() {{ v, Φ v }}.
Proof. Proof.
iIntros (Hnl) "#InvC #InvS PAU HQ Hl_new". wp_bind (Resolve _ _ _)%E. iIntros (Hnl) "#InvC #InvS HQ Hl_new". wp_bind (Resolve _ _ _)%E.
iInv counterN as (b' l' q s) "(>Hf & >Hc & >[Hl Hl'] & >Hb● & Hrest)". iInv counterN as (l' q s) "(>Hc & >[Hl Hl'] & Hrest)".
iInv stateN as (vs) "(>Hp & [NotDone | [#Hs Done]])". iInv stateN as (vs) "(>Hp & [NotDone | [#Hs Done]])".
{ (* If the [state] protocol is not done yet, we can show that it { (* If the [state] protocol is not done yet, we can show that it
is the active protocol still (l = l'). But then the CAS would is the active protocol still (l = l'). But then the CAS would
...@@ -370,7 +343,7 @@ Section conditional_counter. ...@@ -370,7 +343,7 @@ Section conditional_counter.
wp_apply (wp_resolve with "Hp"); first done. wp_cas_fail. wp_apply (wp_resolve with "Hp"); first done. wp_cas_fail.
iIntros (vs' ->) "Hp". iModIntro. iIntros (vs' ->) "Hp". iModIntro.
iSplitL "Done Hp". { iNext. iExists _. iFrame "#∗". } iModIntro. iSplitL "Done Hp". { iNext. iExists _. iFrame "#∗". } iModIntro.
iSplitL "Hf Hc Hb● Hrest Hl Hl'". { eauto 10 with iFrame. } iSplitL "Hc Hrest Hl Hl'". { eauto 10 with iFrame. }
wp_seq. iApply "HQ". wp_seq. iApply "HQ".
iApply state_done_extract_Q; done. iApply state_done_extract_Q; done.
Qed. Qed.
...@@ -380,20 +353,23 @@ Section conditional_counter. ...@@ -380,20 +353,23 @@ Section conditional_counter.
this request, then you get [Q]. But we also try to complete other this request, then you get [Q]. But we also try to complete other
thread's requests, which is why we cannot ask for the token thread's requests, which is why we cannot ask for the token
as a precondition. *) as a precondition. *)
Lemma complete_spec (c f l : loc) (n : Z) (p : proph_id) γ_b γ_n γ_t γ_s l_ghost_inv P Q : Lemma complete_spec (c f l : loc) (n : Z) (p : proph_id) γ_n γ_t γ_s l_ghost_inv P Q :
inv counterN (counter_inv γ_b γ_n f c) -∗ N ## gcN
gc_inv -∗
inv counterN (counter_inv γ_n c) -∗
inv stateN (state_inv P Q p n c l l_ghost_inv γ_n γ_t γ_s) -∗ inv stateN (state_inv P Q p n c l l_ghost_inv γ_n γ_t γ_s) -∗
pau P Q (γ_b, γ_n) -∗ pau P Q γ_n f -∗
is_gc_loc f -∗
{{{ True }}} {{{ True }}}
complete #c #f #l #n #p complete #c #f #l #n #p
{{{ RET #(); (own_token γ_t ={}=∗ Q) }}}. {{{ RET #(); (own_token γ_t ={}=∗ Q) }}}.
Proof. Proof.
iIntros "#InvC #InvS #PAU". iIntros (?) "#GC #InvC #InvS #PAU #isGC".
iModIntro. iIntros (Φ) "_ HQ". wp_lam. wp_pures. iModIntro. iIntros (Φ) "_ HQ". wp_lam. wp_pures.
wp_alloc l_ghost as "[Hl_ghost' Hl_ghost'2]". wp_bind (! _)%E. simpl. wp_alloc l_ghost as "[Hl_ghost' Hl_ghost'2]". wp_pures.
(* open outer invariant to read `f` *) wp_bind (! _)%E. simpl.
iInv counterN as (b' l' q s) "(>Hf & >Hc & >Hl' & >Hb● & Hrest)". (* open outer invariant *)
wp_load. iInv counterN as (l' q s) "(>Hc & >Hl' & Hrest)".
(* two different proofs depending on whether we are succeeding thread *) (* two different proofs depending on whether we are succeeding thread *)
destruct (decide (l_ghost_inv = l_ghost)) as [-> | Hnl]. destruct (decide (l_ghost_inv = l_ghost)) as [-> | Hnl].
- (* we are the succeeding thread *) - (* we are the succeeding thread *)
...@@ -402,26 +378,29 @@ Section conditional_counter. ...@@ -402,26 +378,29 @@ Section conditional_counter.
+ (* Pending: update to accepted *) + (* Pending: update to accepted *)
iDestruct "Pending" as "[P >[Hvs Hn●]]". iDestruct "Pending" as "[P >[Hvs Hn●]]".
iDestruct ("PAU" with "P") as ">AU". iDestruct ("PAU" with "P") as ">AU".
iMod (gc_access with "GC") as "Hgc"; first solve_ndisj.
(* open and *COMMIT* AU, sync flag and counter *) (* open and *COMMIT* AU, sync flag and counter *)
iMod "AU" as (b2 n2) "[CC [_ Hclose]]". iMod "AU" as (b n2) "[[Hn◯ Hf] [_ Hclose]]".
iDestruct "CC" as "[Hb◯ Hn◯]". simpl. iDestruct ("Hgc" with "Hf") as "[Hf Hfclose]".
iDestruct (sync_flag_values with "Hb● Hb◯") as %->. wp_load.
iMod ("Hfclose" with "Hf") as "[Hf Hfclose]".
iDestruct (sync_counter_values with "Hn● Hn◯") as %->. iDestruct (sync_counter_values with "Hn● Hn◯") as %->.
iMod (update_counter_value _ _ _ (if b2 then n2 + 1 else n2) with "Hn● Hn◯") iMod (update_counter_value _ _ _ (if b then n2 + 1 else n2) with "Hn● Hn◯")
as "[Hn● Hn◯]". as "[Hn● Hn◯]".
iMod ("Hclose" with "[Hn◯ Hb◯]") as "Q"; first by iFrame. iMod ("Hclose" with "[Hn◯ Hf]") as "Q"; first by iFrame.
iModIntro. iMod "Hfclose" as "_".
(* close state inv *) (* close state inv *)
iModIntro. iSplitL "Q Hl_ghost' Hp Hvs Hs Hc'". iIntros "!> !>". iSplitL "Q Hl_ghost' Hp Hvs Hs Hc'".
{ iNext. iExists _. iFrame "Hp". iLeft. iFrame. { iNext. iExists _. iFrame "Hp". iLeft. iFrame.
iRight. iSplitL "Hl_ghost'"; first by iExists _. iRight. iSplitL "Hl_ghost'"; first by iExists _.
destruct (val_to_some_loc vs) eqn:Hvts; iFrame. } destruct (val_to_some_loc vs) eqn:Hvts; iFrame. }
(* close outer inv *) (* close outer inv *)
iModIntro. iSplitR "Hl_ghost'2 HQ Hn●". iModIntro. iSplitR "Hl_ghost'2 HQ Hn●".
{ eauto 12 with iFrame. } { eauto 12 with iFrame. }
destruct b2; wp_if; [ wp_op | .. ]; destruct b;
wp_alloc l_new as "Hl_new"; wp_pures; wp_alloc l_new as "Hl_new"; wp_pures;
iApply (complete_succeeding_thread_pending iApply (complete_succeeding_thread_pending
with "InvC InvS PAU Hl_ghost'2 HQ Hn● Hl_new"). with "InvC InvS Hl_ghost'2 HQ Hn● Hl_new").
+ (* Accepted: contradiction *) + (* Accepted: contradiction *)
iDestruct "Accepted" as "[>Hl_ghost_inv _]". iDestruct "Accepted" as "[>Hl_ghost_inv _]".
iDestruct "Hl_ghost_inv" as (v) "Hlghost". iDestruct "Hl_ghost_inv" as (v) "Hlghost".
...@@ -432,43 +411,51 @@ Section conditional_counter. ...@@ -432,43 +411,51 @@ Section conditional_counter.
iDestruct "Hlghost" as (v) "Hlghost". iDestruct "Hlghost" as (v) "Hlghost".
iCombine "Hl_ghost'" "Hl_ghost'2" as "Hl_ghost'". iCombine "Hl_ghost'" "Hl_ghost'2" as "Hl_ghost'".
by iDestruct (mapsto_valid_2 with "Hlghost Hl_ghost'") as %?. by iDestruct (mapsto_valid_2 with "Hlghost Hl_ghost'") as %?.
- (* we are the failing thread *) - (* we are the failing thread. exploit that [f] is a GC location. *)
iMod (is_gc_access with "GC isGC") as (b) "[H↦ Hclose]"; first solve_ndisj.
wp_load.
iMod ("Hclose" with "H↦") as "_". iModIntro.
(* close invariant *) (* close invariant *)
iModIntro. iModIntro. iSplitL "Hc Hrest Hl'". { eauto 10 with iFrame. }
iSplitL "Hf Hc Hrest Hl' Hb●". { eauto 10 with iFrame. }
(* two equal proofs depending on value of b1 *) (* two equal proofs depending on value of b1 *)
destruct b'; wp_if; [ wp_op | ..]; wp_alloc Hl_new as "Hl_new"; wp_pures; wp_pures.
destruct (bool_decide (val_for_compare b = #true));
wp_alloc Hl_new as "Hl_new"; wp_pures;
iApply (complete_failing_thread iApply (complete_failing_thread
with "InvC InvS PAU HQ Hl_new"); done. with "InvC InvS HQ Hl_new"); done.
Qed. Qed.
(** ** Proof of [cinc] *) (** ** Proof of [cinc] *)
Lemma cinc_spec γs v : Lemma cinc_spec γs v (f: loc) :
is_counter γs v -∗ is_counter γs v -∗
<<< (b : bool) (n : Z), counter_content γs b n >>> <<< (b : bool) (n : Z), counter_content γs n gc_mapsto f #b >>>
cinc v @⊤∖↑N cinc v #f @⊤∖↑N∖↑gcN
<<< counter_content γs b (if b then n + 1 else n), RET #() >>>. <<< counter_content γs (if b then n + 1 else n) gc_mapsto f #b, RET #() >>>.
Proof. Proof.
iIntros "#InvC". iDestruct "InvC" as (γ_b γ_n f_l c_l) "[Heq InvC]". iIntros "#InvC". iDestruct "InvC" as (c_l [-> ?]) "[#GC #InvC]".
iDestruct "Heq" as %[-> ->]. iIntros (Φ) "AU". iLöb as "IH". iIntros (Φ) "AU". iLöb as "IH".
wp_lam. wp_proj. wp_let. wp_proj. wp_let. wp_bind (!_)%E. wp_lam. wp_pures. wp_bind (!_)%E.
iInv counterN as (b' l' q s) "(>Hf & >Hc & >[Hl [Hl' Hl'']] & >Hb● & Hrest)". iInv counterN as (l' q s) "(>Hc & >[Hl [Hl' Hl'']] & Hrest)".
wp_load. destruct s as [n|n p]. wp_load. destruct s as [n|f' n' p'].
- iDestruct "Hrest" as "(Hc' & Hv)". - iDestruct "Hrest" as "(Hc' & Hv)".
iModIntro. iSplitR "AU Hl'". iModIntro. iSplitR "AU Hl'".
{ iModIntro. iExists _, _, (q/2)%Qp, (Quiescent n). iFrame. } { iModIntro. iExists _, (q/2)%Qp, (Quiescent n). iFrame. }
wp_let. wp_load. wp_match. wp_apply wp_new_proph; first done. wp_let. wp_load. wp_match. wp_apply wp_new_proph; first done.
iIntros (l_ghost p') "Hp'". iIntros (l_ghost p') "Hp'".
wp_let. wp_alloc ly as "Hly". wp_let. wp_alloc ly as "Hly".
wp_let. wp_bind (CAS _ _ _)%E. wp_let. wp_bind (CAS _ _ _)%E.
(* open outer invariant again to CAS c_l *) (* open outer invariant again to CAS c_l *)
iInv counterN as (b2 l'' q2 s) "(>Hf & >Hc & >Hl & >Hb● & Hrest)". iInv counterN as (l'' q2 s) "(>Hc & >Hl & Hrest)".
destruct (decide (l' = l'')) as [<- | Hn]. destruct (decide (l' = l'')) as [<- | Hn].
+ (* CAS succeeds *) + (* CAS succeeds *)
iDestruct (mapsto_agree with "Hl' Hl") as %<-%state_to_val_inj. iDestruct (mapsto_agree with "Hl' Hl") as %<-%state_to_val_inj.
iDestruct "Hrest" as ">[Hc' Hn●]". iCombine "Hc Hc'" as "Hc". iDestruct "Hrest" as ">[Hc' Hn●]". iCombine "Hc Hc'" as "Hc".
wp_cas_suc. wp_cas_suc.
(* Take a "peek" at [AU] and abort immediately to get [gc_is_gc f]. *)
iMod "AU" as (b' n') "[[CC Hf] [Hclose _]]".
iDestruct (gc_is_gc with "Hf") as "#Hgc".
iMod ("Hclose" with "[CC Hf]") as "AU"; first by iFrame.
(* Initialize new [state] protocol .*) (* Initialize new [state] protocol .*)
iDestruct (laterable with "AU") as (AU_later) "[AU #AU_back]". iDestruct (laterable with "AU") as (AU_later) "[AU #AU_back]".
iMod (own_alloc (Excl ())) as (γ_t) "Token"; first done. iMod (own_alloc (Excl ())) as (γ_t) "Token"; first done.
...@@ -481,103 +468,83 @@ Section conditional_counter. ...@@ -481,103 +468,83 @@ Section conditional_counter.
iFrame. destruct (val_to_some_loc l_ghost); simpl; done. } iFrame. destruct (val_to_some_loc l_ghost); simpl; done. }
iModIntro. iDestruct "Hly" as "[Hly1 Hly2]". iSplitR "Hl' Token". { iModIntro. iDestruct "Hly" as "[Hly1 Hly2]". iSplitR "Hl' Token". {
(* close invariant *) (* close invariant *)
iNext. iExists _, _, _, (Updating n p'). eauto 10 with iFrame. iNext. iExists _, _, (Updating f n p'). eauto 10 with iFrame.
} }
wp_if. wp_apply complete_spec; [done..|]. wp_if. wp_apply complete_spec; [done..|].
iIntros "Ht". iMod ("Ht" with "Token") as "Φ". wp_seq. by wp_lam. iIntros "Ht". iMod ("Ht" with "Token") as "Φ". wp_seq. by wp_lam.
+ (* CAS fails: closing invariant and invoking IH *) + (* CAS fails: closing invariant and invoking IH *)
wp_cas_fail. wp_cas_fail.
iModIntro. iSplitR "AU". iModIntro. iSplitR "AU".
{ iModIntro. iExists _, _, _, s. iFrame. } { iModIntro. iExists _, _, s. iFrame. }
wp_if. by iApply "IH". wp_if. by iApply "IH".
- (* l' ↦ injR *) - (* l' ↦ injR *)
iModIntro. iModIntro.
(* extract state invariant *) (* extract state invariant *)
iDestruct "Hrest" as (P Q l_ghost γ_t γ_s) "[#InvS #P_AU]". iDestruct "Hrest" as (P Q l_ghost γ_t γ_s) "(#InvS & #P_AU & #Hgc)".
iSplitR "Hl' AU". iSplitR "Hl' AU".
(* close invariant *) (* close invariant *)
{ iModIntro. iExists _, _, _, (Updating n p). iFrame. eauto 10 with iFrame. } { iModIntro. iExists _, _, (Updating f' n' p'). iFrame. eauto 10 with iFrame. }
wp_let. wp_load. wp_match. wp_pures. wp_let. wp_load. wp_match. wp_pures.
wp_apply complete_spec; [done..|]. wp_apply complete_spec; [done..|].
iIntros "_". wp_seq. by iApply "IH". iIntros "_". wp_seq. by iApply "IH".
Qed. Qed.
Lemma new_counter_spec : Lemma new_counter_spec :
N ## gcN
gc_inv -∗
{{{ True }}} {{{ True }}}
new_counter #() new_counter #()
{{{ ctr γs, RET ctr ; is_counter γs ctr counter_content γs true 0 }}}. {{{ ctr γs, RET ctr ; is_counter γs ctr counter_content γs 0 }}}.
Proof. Proof.
iIntros (Φ) "_ HΦ". wp_lam. wp_apply wp_fupd. iIntros (?) "#GC". iIntros (Φ) "!# _ HΦ". wp_lam. wp_apply wp_fupd.
wp_alloc l_n as "Hl_n". wp_alloc l_n as "Hl_n".
wp_alloc l_c as "Hl_c". wp_let. wp_alloc l_c as "Hl_c".
wp_alloc l_f as "Hl_f". wp_let. wp_pair.
iMod (own_alloc ( Excl' true Excl' true)) as (γ_b) "[Hb● Hb◯]".
{ by apply auth_both_valid. }
iMod (own_alloc ( Excl' 0 Excl' 0)) as (γ_n) "[Hn● Hn◯]". iMod (own_alloc ( Excl' 0 Excl' 0)) as (γ_n) "[Hn● Hn◯]".
{ by apply auth_both_valid. } { by apply auth_both_valid. }
iMod (inv_alloc counterN _ (counter_inv γ_b γ_n l_f l_c) iMod (inv_alloc counterN _ (counter_inv γ_n l_c)
with "[Hl_f Hl_c Hl_n Hb● Hn●]") as "#InvC". with "[Hl_c Hl_n Hn●]") as "#InvC".
{ iNext. iDestruct "Hl_c" as "[Hl_c1 Hl_c2]". { iNext. iDestruct "Hl_c" as "[Hl_c1 Hl_c2]".
iDestruct "Hl_n" as "[??]". iDestruct "Hl_n" as "[??]".
iExists true, l_n, (1/2)%Qp, (Quiescent 0). iFrame. } iExists l_n, (1/2)%Qp, (Quiescent 0). iFrame. }
iModIntro. iModIntro.
iApply ("HΦ" $! (#l_f, #l_c)%V (γ_b, γ_n)). iApply ("HΦ" $! #l_c γ_n).
iSplitR; last by iFrame. iExists γ_b, γ_n, l_f, l_c. iSplit; done. iSplitR; last by iFrame. iExists _. eauto with iFrame.
Qed.
Lemma set_flag_spec γs v (new_b : bool) :
is_counter γs v -∗
<<< (b : bool) (n : Z), counter_content γs b n >>>
set_flag v #new_b @⊤∖↑N
<<< counter_content γs new_b n, RET #() >>>.
Proof.
iIntros "#InvC" (Φ) "AU". iDestruct "InvC" as (γ_b γ_n f_l c_l) "[Heq InvC]".
iDestruct "Heq" as %[-> ->]. wp_lam. wp_let. wp_proj.
iInv counterN as (b c q s) "(>Hf & >Hc & >[Hl Hl'] & >Hb● & Hrest)".
iMod "AU" as (b' n') "[[Hb◯ Hn◯] [_ Hclose]]"; simpl.
wp_store.
iDestruct (sync_flag_values with "Hb● Hb◯") as %HEq; subst b.
iDestruct (update_flag_value with "Hb● Hb◯") as ">[Hb● Hb◯]".
iMod ("Hclose" with "[Hn◯ Hb◯]") as "HΦ"; first by iFrame.
iModIntro. iModIntro. iSplitR "HΦ"; last done.
iNext. iExists new_b, c, q, _. iFrame. done.
Qed. Qed.
Lemma get_spec γs v : Lemma get_spec γs v :
is_counter γs v -∗ is_counter γs v -∗
<<< (b : bool) (n : Z), counter_content γs b n >>> <<< (n : Z), counter_content γs n >>>
get v @⊤∖↑N get v @⊤∖↑N∖↑gcN
<<< counter_content γs b n, RET #n >>>. <<< counter_content γs n, RET #n >>>.
Proof. Proof.
iIntros "#InvC" (Φ) "AU". iDestruct "InvC" as (γ_b γ_n f_l c_l) "[Heq InvC]". iIntros "#InvC" (Φ) "AU". iDestruct "InvC" as (c_l [-> ?]) "[GC InvC]".
iDestruct "Heq" as %[-> ->]. iLöb as "IH". wp_lam. repeat (wp_proj; wp_let). wp_bind (! _)%E. iLöb as "IH". wp_lam. wp_bind (! _)%E.
iInv counterN as (b c q s) "(>Hf & >Hc & >[Hl [Hl' Hl'']] & >Hb● & Hrest)". iInv counterN as (c q s) "(>Hc & >[Hl [Hl' Hl'']] & Hrest)".
wp_load. wp_load.
destruct s as [n|n p]. destruct s as [n|f n p].
- iMod "AU" as (au_b au_n) "[[Hb◯ Hn◯] [_ Hclose]]"; simpl. - iMod "AU" as (au_n) "[Hn◯ [_ Hclose]]"; simpl.
iDestruct "Hrest" as "[Hc' Hn●]". iDestruct "Hrest" as "[Hc' Hn●]".
iDestruct (sync_counter_values with "Hn● Hn◯") as %->. iDestruct (sync_counter_values with "Hn● Hn◯") as %->.
iMod ("Hclose" with "[Hn◯ Hb◯]") as "HΦ"; first by iFrame. iMod ("Hclose" with "Hn◯") as "HΦ".
iModIntro. iSplitR "HΦ Hl'". { iModIntro. iSplitR "HΦ Hl'". {
iNext. iExists b, c, (q/2)%Qp, (Quiescent au_n). iFrame. iNext. iExists c, (q/2)%Qp, (Quiescent au_n). iFrame.
} }
wp_let. wp_load. wp_match. iApply "HΦ". wp_let. wp_load. wp_match. iApply "HΦ".
- iDestruct "Hrest" as (P Q l_ghost γ_t γ_s) "[#InvS #PAU]". - iDestruct "Hrest" as (P Q l_ghost γ_t γ_s) "(#InvS & #PAU & #Hgc)".
iModIntro. iSplitR "AU Hl'". { iModIntro. iSplitR "AU Hl'". {
iNext. iExists b, c, (q/2)%Qp, (Updating n p). eauto 10 with iFrame. iNext. iExists c, (q/2)%Qp, (Updating _ _ p). eauto 10 with iFrame.
} }
wp_let. wp_load. wp_match. repeat wp_proj. wp_bind (complete _ _ _ _ _)%E. wp_let. wp_load. wp_pures. wp_bind (complete _ _ _ _ _)%E.
wp_apply complete_spec; [done..|]. wp_apply complete_spec; [done..|].
iIntros "Ht". wp_seq. iApply "IH". iApply "AU". iIntros "Ht". wp_seq. iApply "IH". iApply "AU".
Qed. Qed.
End conditional_counter. End conditional_counter.
Definition atomic_cinc `{!heapG Σ, cincG Σ} : Definition atomic_cinc `{!heapG Σ, !cincG Σ, !gcG Σ} :
spec.atomic_cinc Σ := spec.atomic_cinc Σ :=
{| spec.new_counter_spec := new_counter_spec; {| spec.new_counter_spec := new_counter_spec;
spec.cinc_spec := cinc_spec; spec.cinc_spec := cinc_spec;
spec.set_flag_spec := set_flag_spec;
spec.get_spec := get_spec; spec.get_spec := get_spec;
spec.counter_content_exclusive := counter_content_exclusive |}. spec.counter_content_exclusive := counter_content_exclusive |}.
......
From stdpp Require Import namespaces. From stdpp Require Import namespaces.
From iris.heap_lang Require Export lifting notation. From iris.heap_lang Require Export lifting notation.
From iris.program_logic Require Export atomic. From iris.program_logic Require Export atomic.
From iris_examples.logatom.lib Require Export gc.
Set Default Proof Using "Type". Set Default Proof Using "Type".
(** A general logically atomic interface for conditional increment. *) (** A general logically atomic interface for conditional increment. *)
Record atomic_cinc {Σ} `{!heapG Σ} := AtomicCinc { Record atomic_cinc {Σ} `{!heapG Σ, !gcG Σ} := AtomicCinc {
(* -- operations -- *) (* -- operations -- *)
new_counter : val; new_counter : val;
cinc : val; cinc : val;
set_flag : val;
get : val; get : val;
(* -- other data -- *) (* -- other data -- *)
name : Type; name : Type;
...@@ -16,34 +16,31 @@ Record atomic_cinc {Σ} `{!heapG Σ} := AtomicCinc { ...@@ -16,34 +16,31 @@ Record atomic_cinc {Σ} `{!heapG Σ} := AtomicCinc {
name_countable : Countable name; name_countable : Countable name;
(* -- predicates -- *) (* -- predicates -- *)
is_counter (N : namespace) (γs : name) (v : val) : iProp Σ; is_counter (N : namespace) (γs : name) (v : val) : iProp Σ;
counter_content (γs : name) (flag : bool) (c : Z) : iProp Σ; counter_content (γs : name) (c : Z) : iProp Σ;
(* -- predicate properties -- *) (* -- predicate properties -- *)
is_counter_persistent N γs v : Persistent (is_counter N γs v); is_counter_persistent N γs v : Persistent (is_counter N γs v);
counter_content_timeless γs f c : Timeless (counter_content γs f c); counter_content_timeless γs c : Timeless (counter_content γs c);
counter_content_exclusive γs f1 c1 f2 c2 : counter_content_exclusive γs c1 c2 :
counter_content γs f1 c1 -∗ counter_content γs f2 c2 -∗ False; counter_content γs c1 -∗ counter_content γs c2 -∗ False;
(* -- operation specs -- *) (* -- operation specs -- *)
new_counter_spec N : new_counter_spec N :
N ## gcN
gc_inv -∗
{{{ True }}} {{{ True }}}
new_counter #() new_counter #()
{{{ ctr γs, RET ctr ; is_counter N γs ctr counter_content γs true 0 }}}; {{{ ctr γs, RET ctr ; is_counter N γs ctr counter_content γs 0 }}};
cinc_spec N γs v : cinc_spec N γs v (f : loc) :
is_counter N γs v -∗ is_counter N γs v -∗
<<< (b : bool) (n : Z), counter_content γs b n >>> <<< (b : bool) (n : Z), counter_content γs n gc_mapsto f #b >>>
cinc v @⊤∖↑N cinc v #f @⊤∖↑N∖↑gcN
<<< counter_content γs b (if b then n + 1 else n), RET #() >>>; <<< counter_content γs (if b then n + 1 else n) gc_mapsto f #b, RET #() >>>;
set_flag_spec N γs v (new_b: bool) :
is_counter N γs v -∗
<<< (b : bool) (n : Z), counter_content γs b n >>>
set_flag v #new_b @⊤∖↑N
<<< counter_content γs new_b n, RET #() >>>;
get_spec N γs v: get_spec N γs v:
is_counter N γs v -∗ is_counter N γs v -∗
<<< (b : bool) (n : Z), counter_content γs b n >>> <<< (n : Z), counter_content γs n >>>
get v @⊤∖↑N get v @⊤∖↑N∖↑gcN
<<< counter_content γs b n, RET #n >>>; <<< counter_content γs n, RET #n >>>;
}. }.
Arguments atomic_cinc _ {_}. Arguments atomic_cinc _ {_ _}.
Existing Instances Existing Instances
is_counter_persistent counter_content_timeless is_counter_persistent counter_content_timeless
......
...@@ -7,7 +7,7 @@ Import uPred. ...@@ -7,7 +7,7 @@ Import uPred.
Definition gcN: namespace := nroot .@ "gc". Definition gcN: namespace := nroot .@ "gc".
Definition gc_mapUR : ucmraT := gmapUR loc $ optionR $ exclR $ valC. Definition gc_mapUR : ucmraT := gmapUR loc $ optionR $ exclR $ valO.
Definition to_gc_map (gcm: gmap loc val) : gc_mapUR := (λ v : val, Excl' v) <$> gcm. Definition to_gc_map (gcm: gmap loc val) : gc_mapUR := (λ v : val, Excl' v) <$> gcm.
...@@ -112,7 +112,9 @@ Section to_gc_map. ...@@ -112,7 +112,9 @@ Section to_gc_map.
End to_gc_map. End to_gc_map.
Section gc. Section gc.
Context `{!invG Σ, !heapG Σ, gG: gcG Σ}. Context `{!invG Σ, !heapG Σ, !gcG Σ}.
(* FIXME: still needs a constructor. *)
Global Instance is_gc_loc_persistent (l: loc): Persistent (is_gc_loc l). Global Instance is_gc_loc_persistent (l: loc): Persistent (is_gc_loc l).
Proof. rewrite /is_gc_loc. apply _. Qed. Proof. rewrite /is_gc_loc. apply _. Qed.
...@@ -158,7 +160,7 @@ Section gc. ...@@ -158,7 +160,7 @@ Section gc.
iFrame. iFrame.
Qed. Qed.
Lemma is_gc_lookup_Some l gcm: is_gc_loc l -∗ own (gc_name gG) ( to_gc_map gcm) -∗ ⌜∃ v, gcm !! l = Some v⌝. Lemma is_gc_lookup_Some l gcm: is_gc_loc l -∗ own (gc_name _) ( to_gc_map gcm) -∗ ⌜∃ v, gcm !! l = Some v⌝.
iIntros "Hgc_l H◯". iIntros "Hgc_l H◯".
iCombine "H◯ Hgc_l" as "Hcomb". iCombine "H◯ Hgc_l" as "Hcomb".
iDestruct (own_valid with "Hcomb") as %Hvalid. iDestruct (own_valid with "Hcomb") as %Hvalid.
...@@ -170,7 +172,7 @@ Section gc. ...@@ -170,7 +172,7 @@ Section gc.
by apply leibniz_equiv_iff in Hsome. by apply leibniz_equiv_iff in Hsome.
Qed. Qed.
Lemma gc_mapsto_lookup_Some l v gcm: gc_mapsto l v -∗ own (gc_name gG) ( to_gc_map gcm) -∗ gcm !! l = Some v ⌝. Lemma gc_mapsto_lookup_Some l v gcm: gc_mapsto l v -∗ own (gc_name _) ( to_gc_map gcm) -∗ gcm !! l = Some v ⌝.
Proof. Proof.
iIntros "Hgc_l H●". iIntros "Hgc_l H●".
iCombine "H● Hgc_l" as "Hcomb". iCombine "H● Hgc_l" as "Hcomb".
...@@ -180,12 +182,17 @@ Section gc. ...@@ -180,12 +182,17 @@ Section gc.
by apply gc_map_singleton_included. by apply gc_map_singleton_included.
Qed. Qed.
Lemma gc_access l v E: (** An accessor to make use of [gc_mapsto].
gcN E gc_inv -∗ gc_mapsto l v ={E, E gcN}=∗ (l v ( w, l w ={E gcN, E}=∗ gc_mapsto l w)). This opens the invariant *before* consuming [gc_mapsto] so that you can use
this before opening an atomic update that provides [gc_mapsto]!. *)
Lemma gc_access E:
gcN E
gc_inv ={E, E gcN}=∗ l v, gc_mapsto l v -∗
(l v ( w, l w ==∗ gc_mapsto l w |={E gcN, E}=> True)).
Proof. Proof.
iIntros (HN) "#Hinv Hgc_l". iIntros (HN) "#Hinv".
iMod (inv_open_timeless _ gcN _ with "Hinv") as "[P Hclose]"=>//. iMod (inv_open_timeless _ gcN _ with "Hinv") as "[P Hclose]"=>//.
iModIntro. iIntros "!>" (l v) "Hgc_l".
iDestruct "P" as (gcm) "[H● HsepM]". iDestruct "P" as (gcm) "[H● HsepM]".
iDestruct (gc_mapsto_lookup_Some with "Hgc_l H●") as %Hsome. iDestruct (gc_mapsto_lookup_Some with "Hgc_l H●") as %Hsome.
iDestruct (big_sepM_delete with "HsepM") as "[Hl HsepM]"=>//. iDestruct (big_sepM_delete with "HsepM") as "[Hl HsepM]"=>//.
...@@ -199,6 +206,7 @@ Section gc. ...@@ -199,6 +206,7 @@ Section gc.
iDestruct (big_sepM_insert with "[Hl HsepM]") as "HsepM"; [ | iFrame | ]. iDestruct (big_sepM_insert with "[Hl HsepM]") as "HsepM"; [ | iFrame | ].
{ apply lookup_delete. } { apply lookup_delete. }
rewrite insert_delete. rewrite <- to_gc_map_insert. rewrite insert_delete. rewrite <- to_gc_map_insert.
iModIntro. iFrame.
iMod ("Hclose" with "[H● HsepM]"); [ iExists _; by iFrame | by iModIntro]. iMod ("Hclose" with "[H● HsepM]"); [ iExists _; by iFrame | by iModIntro].
Qed. Qed.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment