diff --git a/theories/experimental/helping/helping_wrapper.v b/theories/experimental/helping/helping_wrapper.v new file mode 100644 index 0000000000000000000000000000000000000000..b29f084ee3af2c0d097bdc27e08b4f9e315c7520 --- /dev/null +++ b/theories/experimental/helping/helping_wrapper.v @@ -0,0 +1,549 @@ +(* ReLoC -- Relational logic for fine-grained concurrency *) +(** Generic operations for helping. + The operation mk_helping : (() → ( () → option A ) × ( A → () )) → ( () → option A ) × ( A → () ) + takes a "constructor" that returns two closures, and returns two /wrapped/ closures. + The wrapped closures (for "pop" and "push" operations) synchronize on a side channel. + + This provides a somewhat generic proof of the refinement for stack with helping. + + TODO: support recursion/callbacks into the wrapped functions. + *) +From iris.algebra Require Import auth gmap agree list excl. +From iris.base_logic.lib Require Import auth. +From reloc Require Export reloc experimental.helping.offers. +From reloc Require Import lib.list. + +(* wrapper for pop *) +Definition wrap_pop : val := λ: "f" "mb", + match: !"mb" with + NONE => + (* there are no offers *) "f" #() + | SOME "off" => + (* trying to accept an offer instead of doing an actual POP *) + match: take_offer "off" with + NONE => (* did not succeed on accepting the offer *) + "f" #() + | SOME "x" => SOME "x" + end + end. + +(* wrapper for push *) +Definition wrap_push : val := λ: "f" "mb" "x", + let: "off" := mk_offer "x" in + "mb" <- SOME "off";; + match: revoke_offer "off" with + NONE => (* the offer was successfully taken *) #() + | SOME "x" => + (* nobody took the offer *) + "f" "x" (* TODO: add a way of restartnig "f" using mk_helping_2 *) + end. + +Definition mk_helping : val := λ: "new_ds", + let: "ff" := "new_ds" #() in + let: "f1" := Fst "ff" in + let: "f2" := Snd "ff" in + let: "mb" := ref NONE in + (λ: <>, wrap_pop "f1" "mb", + λ: "x", wrap_push "f2" "mb" "x"). + +Canonical Structure ectx_itemO := leibnizO ectx_item. +Definition offerReg := gmap loc (val * gname * nat * (list ectx_item)). +Definition offerRegR := + gmapUR loc (agreeR (prodO valO (prodO gnameO (prodO natO (listO ectx_itemO))))). + +Class offerRegPreG Σ := OfferRegPreG { + offerReg_inG :> authG Σ offerRegR +}. + +Definition offerize (x : (val * gname * nat * (list ectx_item))) : + (agreeR (prodO valO (prodO gnameO (prodO natO (listO ectx_itemO))))) := + match x with + | (v, γ, n, K) => to_agree (v, (γ, (n, K))) + end. + +Definition to_offer_reg : offerReg -> offerRegR := fmap offerize. + +Lemma to_offer_reg_valid N : ✓ to_offer_reg N. +Proof. + intros l. rewrite lookup_fmap. case (N !! l); simpl; try done. + intros [[[v γ] n] K]; simpl. done. +Qed. + +Section offerReg_rules. + Context `{!offerRegPreG Σ, !offerG Σ}. + + Lemma offerReg_alloc (o : loc) (v : val) (γ : gname) + (j : nat) (K : list ectx_item) (γo : gname) (N : offerReg) : + N !! o = None → + own γo (â— to_offer_reg N) + ==∗ own γo (â— to_offer_reg (<[o:=(v, γ, j, K)]> N)) + ∗ own γo (â—¯ {[o := offerize (v, γ, j, K)]}). + Proof. + iIntros (HNo) "HN". + iMod (own_update with "HN") as "[HN Hfrag]". + { eapply auth_update_alloc. + eapply (alloc_singleton_local_update _ o (to_agree (v, (γ, (j, K))))); try done. + by rewrite /to_offer_reg lookup_fmap HNo. } + iFrame. by rewrite /to_offer_reg fmap_insert. + Qed. + + Lemma offerReg_frag_lookup (o : loc) (v : val) (γ : gname) + (j : nat) (K : list ectx_item) (γo : gname) (N : offerReg) : + own γo (â— to_offer_reg N) + -∗ own γo (â—¯ {[o := to_agree (v, (γ, (j, K)))]}) + -∗ ⌜N !! o = Some (v, γ, j, K)âŒ. + Proof. + iIntros "HN Hfrag". + iDestruct (own_valid_2 with "HN Hfrag") as %Hfoo. + apply auth_both_valid in Hfoo. + simpl in Hfoo. destruct Hfoo as [Hfoo _]. + iAssert (⌜✓ ((to_offer_reg N) !! o)âŒ)%I as %Hvalid. + { iDestruct (own_valid with "HN") as %HNvalid. + iPureIntro. revert HNvalid. + rewrite auth_auth_valid. done. } + iPureIntro. + revert Hfoo. + rewrite singleton_included_l=> -[av []]. + revert Hvalid. + rewrite /to_offer_reg !lookup_fmap. + case: (N !! o)=> [av'|] Hvalid; last by inversion 1. + intros Hav. + rewrite -(inj Some _ _ Hav). + rewrite Some_included_total. + destruct av' as [[[??]?]?]. simpl. + rewrite to_agree_included. + fold_leibniz. + intros. by simplify_eq. + Qed. + + Lemma offerReg_lookup_frag (o : loc) (v : val) (γ : gname) + (j : nat) (K : list ectx_item) (γo : gname) (N : offerReg) : + N !! o = Some (v, γ, j, K) → + own γo (â— to_offer_reg N) + ==∗ own γo (â—¯ {[o := to_agree (v, (γ, (j, K)))]}) + ∗ own γo (â— to_offer_reg N). + Proof. + iIntros (HNo) "Hown". + iMod (own_update with "[Hown]") as "Hown". + { eapply (auth_update (to_offer_reg N) ∅). + eapply (op_local_update_discrete _ ∅ {[o := to_agree (v, (γ, (j, K)))]}). + intros. intros i. destruct (decide (i = o)); subst; rewrite lookup_op. + + rewrite lookup_singleton lookup_fmap HNo. + rewrite -Some_op. + rewrite Some_valid. + rewrite agree_idemp. done. + + rewrite lookup_singleton_ne; eauto. + rewrite left_id. + done. + } + { (** TODO: this is silly *) + assert (RightId (≡) (â—¯ ∅ : authUR offerRegR) (â‹…)). + { apply (@ucmra_unit_right_id (authUR offerRegR)). } + rewrite right_id. iFrame "Hown". } + iDestruct "Hown" as "[Ho Hown]". + rewrite right_id. iFrame. + assert ({[o := to_agree (v, (γ, (j, K)))]} â‹… to_offer_reg N ≡ to_offer_reg N) as Hfoo. + { intro i. + rewrite /to_offer_reg lookup_merge !lookup_fmap. + destruct (decide (i = o)); subst. + + rewrite HNo lookup_singleton /=. + rewrite -Some_op. + apply Some_proper. + symmetry. apply agree_included. + by apply to_agree_included. + + rewrite lookup_singleton_ne; eauto. + by rewrite left_id. } + by rewrite Hfoo. + Qed. + + (** The invariant that we will have. *) + Definition offerInv `{!relocG Σ} (N : offerReg) (f : val) : iProp Σ := + ([∗ map] l ↦ w ∈ N, + match w with + | (v,γ,j,K) => is_offer γ l + (j ⤇ fill K (f (of_val v))%E) + (j ⤇ fill K #()) + end)%I. + + Lemma offerInv_empty `{!relocG Σ} (f : val) : ⊢ offerInv ∅ f. + Proof. by rewrite /offerInv big_sepM_empty. Qed. + + Lemma offerInv_excl `{!relocG Σ} (N : offerReg) (f : val) (o : loc) γ P Q : + offerInv N f -∗ is_offer γ o P Q -∗ ⌜N !! o = NoneâŒ. + Proof. + rewrite /offerInv. iIntros "HN Ho". + iAssert (⌜is_Some (N !! o)⌠→ False)%I as %Hbaz. + { + iIntros ([[[[? ?] ?] ?] HNo]). + rewrite (big_sepM_lookup _ N o _ HNo). + iApply (is_offer_exclusive with "HN Ho"). + } + iPureIntro. revert Hbaz. case: (N !! o)=> [av'|]; naive_solver. + Qed. + +End offerReg_rules. + +(** * Refinement proof *) +Section refinement. + Context `{!relocG Σ, !offerRegPreG Σ, !offerG Σ}. + + Implicit Type A : lrel Σ. + + (** First we have the revoke_offer symbolic execution rule specialized for helping. + This is also the only place where we need to unfold the definition of the refinement proposition. *) + Lemma revoke_offer_l γ off E K (v : val) e1 e2 A : + offer_token γ -∗ + (∀ j K', (j ⤇ fill K' e1) ={E, ⊤, E}â–·=∗ + â–· is_offer γ off (j ⤇ fill K' e1) (j ⤇ fill K' e2) ∗ + â–· (is_offer γ off (j ⤇ fill K' e1) (j ⤇ fill K' e2) -∗ + ((REL fill K (of_val NONEV) << e2 @ E : A) + ∧ (REL fill K (of_val $ SOMEV v) << e1 @ E : A)))) -∗ + REL fill K (revoke_offer (v, #off)%V) << e1 @ E : A. + Proof. + iIntros "Hγ Hlog". + rewrite {3}refines_eq /refines_def. + iIntros (j K') "#Hs Hj". + iMod ("Hlog" with "Hj") as "Hlog". + iModIntro. iApply wp_bind. (* TODO: why do we have to use wp_bind here? *) + wp_apply (wp_revoke_offer with "Hγ [-]"). + iNext. iMod "Hlog" as "[Hoff Hcont]". iModIntro. + iFrame "Hoff". iNext. iSplit. + - iIntros "Hoff". iDestruct ("Hcont" with "Hoff") as "[Hcont _]". + rewrite refines_eq. by iApply "Hcont". + - iIntros "Hoff". iDestruct ("Hcont" with "Hoff") as "[_ Hcont]". + rewrite refines_eq. by iApply "Hcont". + Qed. + + (** We will also use the following instances for splitting and + compining read-only pointsto permissions. *) + Local Instance ro_pointsto_fromand (l : loc) (w : val) : + FromSep (∃ q, l ↦{q} w) (∃ q, l ↦{q} w) (∃ q, l ↦{q} w). + Proof. + rewrite /FromSep. iIntros "[$ _]". + Qed. + Local Instance ro_pointsto_intoand (l : loc) (w : val) : + IntoSep (∃ q, l ↦{q} w) (∃ q, l ↦{q} w) (∃ q, l ↦{q} w). + Proof. + rewrite /IntoSep /=. iDestruct 1 as (q) "[H1 H2]". + iSplitL "H1"; eauto with iFrame. + Qed. + + (* ************************************************** *) + (** ** The main invariant that we will use for the proof *) + Definition iN := nroot.@"hewwo". + Definition I A oname (mb : loc) (push_f : val) : iProp Σ := + (∃ (N : offerReg), + (mb ↦ NONEV (* the mailbox is either empty or contains an offer that is in the registry *) + ∨ (∃ (l : loc) v1 v2 γ j K, + A v1 v2 + ∗ mb ↦ SOMEV (v1, #l) + ∗ ⌜N !! l = Some (v2, γ, j, K)âŒ)) + ∗ own oname (â— to_offer_reg N) + ∗ offerInv N push_f)%I. + + Lemma wrap_pop_refinement A (pop1 pop2 push2 : val) γo mb : + inv iN (I A γo mb push2) -∗ + (∀ e v2 j K, j ⤇ fill K (push2 (of_val v2)) -∗ + (j ⤇ fill K #() -∗ REL e << SOMEV v2 @ ⊤∖↑iN : () + A) -∗ + REL e << pop2 #() @ ⊤∖↑iN : () + A) -∗ + (REL pop1 << pop2 : () → () + A) -∗ + REL wrap_pop pop1 #mb + << + pop2 #() : () + A. + Proof. + iIntros "#Hinv HpopGhost Hpop". + iLöb as "IH". + rel_rec_l. rel_pures_l. + rel_load_l_atomic. + iInv iN as (N) "(Hmb & HNown & HN)" "Hcl". + iDestruct "Hmb" as "[Hmb | Hmb]". + - (* NO OFFER *) + iModIntro. iExists _; iFrame. + iNext. iIntros "Hmb". + rel_pures_l. + iMod ("Hcl" with "[-Hpop HpopGhost]") as "_". + { iNext. iExists N; by iFrame. } + iApply (refines_app with "Hpop"). by rel_values. + - (* YES OFFER *) + iDestruct "Hmb" as (l v1 v2 γ j K) "(#Hv & Hmb & >HNl)". + iDestruct "HNl" as %HNl. + rewrite /offerInv big_sepM_lookup_acc; eauto. iSimpl in "HN". + iDestruct "HN" as "[HNl HN]". + iMod "HNown". + iMod (offerReg_lookup_frag with "HNown") as "[#Hlown HNown]"; eauto. + iModIntro. iExists _; iFrame. + iNext. iIntros "Hmb". + iMod ("Hcl" with "[-Hlown IH Hpop HpopGhost]") as "_". + { iNext. iExists _. iFrame. + iSplitL "Hmb". + - iRight. iExists _, _, _, _, _, _. + eauto with iFrame. + - by iApply "HN". } + + rel_pures_l. rel_apply_l (take_offer_l _ ). + iInv iN as (N') "(Hmb & HNown & HN)" "Hcl". + simplify_eq/=. + iMod "HNown". + iDestruct (offerReg_frag_lookup with "HNown Hlown") as %?. + rewrite /offerInv (big_sepM_lookup_acc _ _ l); eauto. iSimpl in "HN". + iDestruct "HN" as "[HNl HN]". iModIntro. + iFrame "HNl". iNext. iSplit. + + (* Did not manage to accept an offer *) + iIntros "HNl". + iMod ("Hcl" with "[-IH Hpop]") as "_". + { iNext. iExists _. iFrame. + by iApply "HN". } + rel_pures_l. + iApply (refines_app with "Hpop"). by rel_values. + + (* An offer was accepted *) + iIntros "Hj Hoff". rel_pures_l. + iApply ("HpopGhost" with "Hj"). + iIntros "Hj". + iSpecialize ("Hoff" with "Hj"). + iSpecialize ("HN" with "Hoff"). + iMod ("Hcl" with "[-]") as "_". + { iNext. iExists _. by eauto with iFrame. } + rel_values. iModIntro. iExists v1,v2. + iRight. repeat iSplit; eauto with iFrame. + Qed. + + Lemma wrap_push_refinement A (push1 push2 : val) γo mb (h1 h2 : val) : + inv iN (I A γo mb push2) -∗ + A h1 h2 -∗ + (REL push1 << push2 : A → ()) -∗ + REL wrap_push push1 #mb h1 + << + push2 h2 : (). + Proof. + iIntros "#Hinv #Hh Hpush". + iLöb as "IH". + rel_rec_l. + rel_pures_l. + rel_apply_l mk_offer_l. iIntros (γ off) "Hoff Htok". + rel_pures_l. + rel_store_l_atomic. (* we update the mailbox and store the offer in the registry *) + iInv iN as (N) "(Hmb & HNown & HN)" "Hcl". + iModIntro. + (* first we need to show that mb is allocated / owned *) + iAssert (∃ v, â–· mb ↦ v)%I with "[Hmb]" as (v) "Hmb". + { iDestruct "Hmb" as "[Hmb | Hmb]". + - iExists NONEV; eauto. + - iDestruct "Hmb" as (l m1 m2 γ' j K) "(Hm & Hmb & ?)". + iExists (SOMEV (m1, #l)); eauto. } + iExists _; iFrame; iNext. + iIntros "Hmb". + + rel_pures_l. + rel_apply_l (revoke_offer_l with "Htok"). + iIntros (j K') "Hj". iSpecialize ("Hoff" with "Hj"). + iDestruct (offerInv_excl with "HN Hoff") as %?. + iMod (offerReg_alloc off h2 γ j K' with "HNown") as "[HNown #Hfrag]"; eauto. + iMod ("Hcl" with "[-Hpush]") as "_". + { iNext. iExists _. iFrame "HNown". + iSplitL "Hmb". + - iRight. iExists off, h1, h2, _, _, _. iFrame "Hmb Hh". + iPureIntro. by rewrite lookup_insert. + - rewrite /offerInv big_sepM_insert; eauto with iFrame. } + iModIntro. iNext. + iInv iN as (N') "(Hmb & HNown & HN)" "Hcl". + simplify_eq/=. + iMod "HNown". iModIntro. + iDestruct (offerReg_frag_lookup with "HNown Hfrag") as %Hfoo. + rewrite /offerInv. + (* TODO: separate lemma *) + rewrite (big_sepM_lookup_acc _ N'); eauto. + iDestruct "HN" as "[$ HN]". + iNext. iIntros "Hoff". iSplit. + - (* The offer was already accepted *) + iSpecialize ("HN" with "Hoff"). + iMod ("Hcl" with "[-Hpush]") as "_". + { iNext. iExists _; iFrame. } + rel_pures_l. rel_values. + - (* The offer has been successfully revoked. We have to do the actual push. *) + iSpecialize ("HN" with "Hoff"). + iMod ("Hcl" with "[-Hpush]") as "_". + { iNext. iExists _; iFrame. } + clear. + rel_pures_l. iApply (refines_app with "Hpush"). + by rel_values. + Qed. + +End refinement. + +From reloc.examples.stack Require Import CG_stack refinement. +Definition stackN := nroot.@"stackhehehe". + +Section stack_example. + Context `{!relocG Σ, !offerRegPreG Σ, !offerG Σ, !lockG Σ}. + + Definition stackI stl lk := + (∃ γ, is_lock (nroot.@"LoCk") γ lk (∃ ls1, stl ↦{1/2} val_of_list ls1))%I. + Definition stackInv A st st' := + (∃ (stl : loc) lk, ⌜st = (#stl, lk)%V⌠∗ + stackI stl lk ∗ + inv stackN (∃ ls1 ls2, + (stl ↦{1/2} val_of_list ls1) + ∗ is_stack st' ls2 + ∗ ([∗ list] v1;v2 ∈ ls1;ls2, A v1 v2)))%I. + + Lemma pop_refinement A mb γo st (st' : val) : + inv iN (I A γo mb (λ: "x", CG_locked_push st' "x")) -∗ + stackInv A st st' -∗ + REL wrap_pop (λ: <>, CG_locked_pop st)%V #mb + << + (λ: <>, CG_locked_pop st')%V #() : () + A. + Proof. + iIntros "#Hinv #Hstack". + iApply (wrap_pop_refinement with "Hinv [] []"). + { iIntros (e v2 j K) "Hj Hcont". + rel_pures_r. + iDestruct "Hstack" as (st1l lk1 ->) "[#HstI #Hstack]". + iInv stackN as (ls1 ls2) "(Hst1 & >Hst2 & HA)" "Hcl". + iDestruct "Hst2" as (st2l lk2 ->) "[Hlk Hst2]". + tp_rec j. tp_pures j. tp_rec j. tp_pures j. tp_rec j. + unlock is_locked_r. iDestruct "Hlk" as (lk' ->) "Hl". + (* TODO: make all the tp_ tactics work without the need for an explicit Fupd *) + iApply refines_spec_ctx. iIntros "#HÏ". + iApply fupd_refines. + (* because we manually applied `fupd_refines`, the tactical `with_spec_ctx` doesn't work anymore *) + tp_cmpxchg_suc j. + tp_pures j. tp_rec j. tp_pures j. + tp_load j. tp_pures j. + tp_store j. tp_pures j. + tp_rec j. tp_store j. + iClear "HÏ". iModIntro. + rel_apply_r (refines_CG_pop_suc_r with "[Hst2 Hl]"). + { iExists st2l,#lk'. rewrite /is_locked_r. by eauto with iFrame. } + iIntros "Hst2". + iMod ("Hcl" with "[-Hcont Hj]") as "_". + { iNext. iExists _,_. by eauto with iFrame. } + by iApply "Hcont". } + { rel_arrow_val. iIntros (??) "[-> ->]". rel_pures_l. rel_pures_r. + iDestruct "Hstack" as (st1l lk1 ->) "[#HstI #Hstack]". rewrite /stackI. + iDestruct "HstI" as (γlk) "HstI". + rel_rec_l. rel_pures_l. rel_apply_l (refines_acquire_l with "HstI"). + iNext. iIntros "Hlocked". iDestruct 1 as (ls1) "Hl1". + rel_pures_l. rel_rec_l. rel_load_l. + destruct ls1 as [|h1 ls1]; simpl; rel_rec_l; rel_pures_l. + - iInv stackN as (ls1 ls2) "(>Hst1 & >Hst2 & HA)" "Hcl". + iDestruct "Hst2" as (st2l lk2 ->) "[Hlk Hst2]". + iDestruct (mapsto_agree with "Hl1 Hst1") as %Hfoo. + assert (ls1 = []) as ->. + { revert Hfoo; by destruct ls1. } + rel_rec_r. rel_pures_r. rel_apply_r (refines_acquire_r with "Hlk"). + iIntros "Hlk". rel_pures_r. rel_rec_r. rel_load_r. + rewrite big_sepL2_later_1. iMod "HA" as "HA". + rewrite big_sepL2_nil_inv_l. iDestruct "HA" as "->". + rel_rec_r. rel_pures_r. + rel_apply_r (refines_release_r with "Hlk"). iIntros "Hlk". + iMod ("Hcl" with "[-Hl1 Hlocked]") as "_". + { iNext. iExists [], []. simpl. iFrame. + rewrite right_id. iExists _,_. eauto with iFrame. } + rel_apply_l (refines_release_l with "HstI Hlocked [Hl1]"). + { iExists []. eauto with iFrame. } + iNext. rel_pures_l; rel_pures_r; rel_values. + iModIntro. iExists _,_. eauto with iFrame. + - rel_store_l_atomic. + iInv stackN as (ls1' ls2) "(>Hst1 & >Hst2 & HA)" "Hcl". + iDestruct "Hst2" as (st2l lk2 ->) "[Hlk Hst2]". + iDestruct (mapsto_agree with "Hl1 Hst1") as %Hfoo. + assert (ls1' = h1::ls1) as ->. + { destruct ls1'; simplify_eq/=; eauto. } + iModIntro. iExists _. iFrame. iNext. iIntros "Hl1". + rewrite big_sepL2_cons_inv_l. iDestruct "HA" as (h2 l2' ->) "[#Hh HA]". + rel_rec_r. rel_pures_r. rel_apply_r (refines_acquire_r with "Hlk"). + iIntros "Hlk". rel_pures_r. rel_rec_r. rel_load_r. + rel_rec_r. rel_pures_r. rel_store_r. rel_pures_r. + rel_apply_r (refines_release_r with "Hlk"). iIntros "Hlk". + iDestruct "Hl1" as "[Hl1 Hst1]". + iMod ("Hcl" with "[-Hl1 Hlocked]") as "_". + { iNext. iExists _,_. simpl. iFrame. + iExists _,_. eauto with iFrame. } + rel_pures_l. + rel_apply_l (refines_release_l with "HstI Hlocked [Hl1]"). + { iExists _. eauto with iFrame. } + iNext. rel_pures_l; rel_pures_r; rel_values. + iModIntro. iExists _,_. eauto with iFrame. } + Qed. + + Lemma push_refinement A mb γo st (st' : val) (h1 h2 : val) : + inv iN (I A γo mb (λ: "x", CG_locked_push st' "x")) -∗ + stackInv A st st' -∗ + A h1 h2 -∗ + REL wrap_push (λ: "x", CG_locked_push st "x")%V #mb h1 + << + (λ: "x", CG_locked_push st' "x")%V h2 : (). + Proof. + iIntros "#Hinv #Hstack Hh". + iApply (wrap_push_refinement with "Hinv Hh []"). + rel_arrow_val. + iIntros (v1 v2) "#Hv". rel_pures_l; rel_pures_r. + iDestruct "Hstack" as (st1l lk1 ->) "[#HstI #Hstack]". rewrite /stackI. + iDestruct "HstI" as (γlk) "HstI". + rel_rec_l. rel_pures_l. rel_apply_l (refines_acquire_l with "HstI"). + iNext. iIntros "Hlocked". iDestruct 1 as (ls1) "Hl1". + rel_pures_l. rel_rec_l. rel_load_l. rel_pures_l. + rel_store_l_atomic. + iInv stackN as (ls1' ls2) "(>Hst1 & >Hst2 & HA)" "Hcl". + iDestruct "Hst2" as (st2l lk2 ->) "[Hlk Hst2]". + iDestruct (mapsto_agree with "Hl1 Hst1") as %Hfoo. simplify_eq/=. + iModIntro. iExists _. iFrame. iNext. iIntros "Hl1". + rel_rec_r. rel_pures_r. rel_apply_r (refines_acquire_r with "Hlk"). + iIntros "Hlk". rel_pures_r. rel_rec_r. rel_load_r. + rel_pures_r. rel_store_r. rel_pures_r. + rel_apply_r (refines_release_r with "Hlk"). iIntros "Hlk". + iDestruct "Hl1" as "[Hl1 Hst1]". + iMod ("Hcl" with "[-Hl1 Hlocked]") as "_". + { iNext. iExists (v1::ls1'),(v2::ls2). simpl. iFrame "Hst1 HA Hv". + iExists _,_. eauto with iFrame. } + rel_pures_l. + rel_apply_l (refines_release_l with "HstI Hlocked [Hl1]"). + { iExists (_::_). eauto with iFrame. } + iNext. rel_pures_l; rel_pures_r; rel_values. + Qed. + + Definition CG_mkstack : val := λ: <>, + let: "st" := CG_new_stack #() in + (λ: <>, CG_locked_pop "st", λ: "x", CG_locked_push "st" "x"). + + Lemma stack_refinement : + ⊢ REL (Λ: mk_helping CG_mkstack)%V << CG_mkstack : ∀ A, (() → () + A) * (A → ()). + Proof. + rel_values. iModIntro. iIntros (A). iModIntro. + iIntros (? ?) "[-> ->]". + rel_rec_r. rel_pures_r. rel_rec_r. rel_pures_r. + rel_alloc_r st' as "Hst2". rel_pures_r. + rel_apply_r refines_newlock_r. iIntros (lk2) "Hlk2". + do 5 rel_pure_r. + rel_rec_l. rel_rec_l. rel_rec_l. rel_rec_l. + rel_alloc_l st as "[Hst1 Hl1]". rel_pures_l. + rel_apply_l (refines_newlock_l (nroot.@"LoCk") (∃ ls1, st ↦{1/2} val_of_list ls1)%I with "[Hl1]"). + { iExists []. iFrame. } + iNext. iIntros (lk1 γ) "#Hlk1". rel_pures_l. + rel_alloc_l mb as "Hmb". + do 4 rel_pure_l. + + iMod (own_alloc (â— to_offer_reg ∅ : authR offerRegR)) as (γo) "Hor". + { apply auth_auth_valid. apply to_offer_reg_valid. } + iMod (inv_alloc stackN _ (∃ ls1 ls2, + (st ↦{1/2} val_of_list ls1) + ∗ is_stack (#st',lk2) ls2 + ∗ ([∗ list] v1;v2 ∈ ls1;ls2, A v1 v2)) with "[Hst1 Hst2 Hlk2]") as "#Hinv". + { iNext. iExists [], []; simpl; iFrame. rewrite right_id. + iExists _,_. eauto with iFrame. } + iMod (inv_alloc iN _ (I A γo mb _) with "[-Hlk1]") as "#Hofferinv". + { iNext. iExists ∅. iFrame. iApply offerInv_empty. } + iApply refines_pair. + - rel_arrow_val. iIntros (? ?) "[% %]"; simplify_eq/=. + rel_pure_l. + iApply (pop_refinement with "Hofferinv"). + iExists _, _. rewrite /stackI. + eauto with iFrame. + - rel_arrow_val. iIntros (h1 h2) "#Hh"; simplify_eq/=. + rel_pures_l. + iApply (push_refinement with "Hofferinv [-] Hh"). + iExists _, _. rewrite /stackI. + eauto with iFrame. + Qed. + +End stack_example.