diff options
Diffstat (limited to 'theories/basic_queue_proof.v')
| -rw-r--r-- | theories/basic_queue_proof.v | 652 |
1 files changed, 652 insertions, 0 deletions
diff --git a/theories/basic_queue_proof.v b/theories/basic_queue_proof.v new file mode 100644 index 0000000..f169878 --- /dev/null +++ b/theories/basic_queue_proof.v | |||
| @@ -0,0 +1,652 @@ | |||
| 1 | From iris.program_logic Require Import atomic. | ||
| 2 | From iris.heap_lang Require Import notation proofmode. | ||
| 3 | From iris.algebra.lib Require Import excl_auth. | ||
| 4 | From iris.base_logic.lib Require Import invariants. | ||
| 5 | From iris.base_logic.lib Require Import invariants mono_nat mono_list. | ||
| 6 | From iris_named_props Require Import custom_syntax. | ||
| 7 | |||
| 8 | From lmpmc Require Import basic_queue_spec upstream util. | ||
| 9 | |||
| 10 | Definition new_node : val := λ: "data", | ||
| 11 | let: "ℓ_node" := AllocN #2 #() in | ||
| 12 | "ℓ_node" <- "data";; | ||
| 13 | "ℓ_node" +ₗ #1 <- NONE;; | ||
| 14 | "ℓ_node". | ||
| 15 | |||
| 16 | Definition new : val := λ: <>, | ||
| 17 | let: "ℓ_node" := new_node #() in | ||
| 18 | AllocN #2 "ℓ_node". | ||
| 19 | |||
| 20 | Definition set_tail : val := rec: "go" "ℓ_q" "ℓ_node" := | ||
| 21 | let: "ℓ_tail" := !("ℓ_q" +ₗ #1) in | ||
| 22 | match: !("ℓ_tail" +ₗ #1) with | ||
| 23 | NONE => | ||
| 24 | if: CAS ("ℓ_tail" +ₗ #1) NONE (SOME "ℓ_node") then | ||
| 25 | #() | ||
| 26 | else | ||
| 27 | "go" "ℓ_q" "ℓ_node" | ||
| 28 | | SOME "ℓ_next" => | ||
| 29 | CAS ("ℓ_q" +ₗ #1) "ℓ_tail" "ℓ_next";; | ||
| 30 | "go" "ℓ_q" "ℓ_node" | ||
| 31 | end. | ||
| 32 | |||
| 33 | Definition enqueue : val := λ: "ℓ_q" "data", | ||
| 34 | set_tail "ℓ_q" (new_node "data"). | ||
| 35 | |||
| 36 | Definition try_dequeue : val := rec: "go" "ℓ_q" := | ||
| 37 | let: "ℓ_head" := !"ℓ_q" in | ||
| 38 | match: !("ℓ_head" +ₗ #1) with | ||
| 39 | NONE => NONE | ||
| 40 | | SOME "ℓ_next" => | ||
| 41 | let: "v" := !"ℓ_next" in | ||
| 42 | if: CAS "ℓ_q" "ℓ_head" "ℓ_next" then | ||
| 43 | SOME "v" | ||
| 44 | else | ||
| 45 | "go" "ℓ_q" | ||
| 46 | end. | ||
| 47 | |||
| 48 | Class basic_queueG Σ := BasicQueueG | ||
| 49 | { basic_queue_mono_natG :: mono_natG Σ; | ||
| 50 | basic_queue_mono_listG :: mono_listG (prodO locO valO) Σ; }. | ||
| 51 | |||
| 52 | Definition basic_queueΣ : gFunctors := | ||
| 53 | #[ mono_natΣ; mono_listΣ (prodO locO valO) ]. | ||
| 54 | |||
| 55 | #[global] Instance subG_basic_queueΣ {Σ} : subG basic_queueΣ Σ → basic_queueG Σ. | ||
| 56 | Proof. solve_inG. Qed. | ||
| 57 | |||
| 58 | Section basic_queue. | ||
| 59 | Context `{!heapGS Σ, !basic_queueG Σ}. | ||
| 60 | |||
| 61 | Record gstate := | ||
| 62 | { γ_hist : gname; | ||
| 63 | γ_hpos : gname; }. | ||
| 64 | Definition gstate_to_pair (γ : gstate) := | ||
| 65 | (γ.(γ_hist), γ.(γ_hpos)). | ||
| 66 | Definition gstate_of_pair '(γ_hist, γ_hpos) := | ||
| 67 | {| γ_hist := γ_hist; γ_hpos := γ_hpos |}. | ||
| 68 | Instance gstate_eq_dec : EqDecision gstate := ltac:(solve_decision). | ||
| 69 | Instance gstate_countable : Countable gstate. | ||
| 70 | Proof. | ||
| 71 | refine {| encode := encode ∘ gstate_to_pair; | ||
| 72 | decode := fmap gstate_of_pair ∘ decode; |}. | ||
| 73 | intros []. by rewrite /= decode_encode. | ||
| 74 | Qed. | ||
| 75 | |||
| 76 | Definition node_repr (ℓ : loc) (data : val) (mℓ_next : option loc) : iProp Σ := | ||
| 77 | ("Hndata" ∷ ℓ ↦□ data) ∗ | ||
| 78 | ("Hnnext" ∷ (ℓ +ₗ 1) ↦ loc_opt_hl mℓ_next). | ||
| 79 | |||
| 80 | Definition loc_at (hist : list (loc * val)) i := | ||
| 81 | hist.*1 !! i. | ||
| 82 | Definition val_at (hist : list (loc * val)) i := | ||
| 83 | hist.*2 !! i. | ||
| 84 | |||
| 85 | Lemma loc_at_prefix xs xs' i ℓ : | ||
| 86 | loc_at xs i = Some ℓ → xs `prefix_of` xs' → loc_at xs' i = Some ℓ. | ||
| 87 | Proof. | ||
| 88 | unfold loc_at. intros Hxs Hpre. | ||
| 89 | eapply prefix_lookup_Some; first exact Hxs. | ||
| 90 | by apply prefix_of_fmap. | ||
| 91 | Qed. | ||
| 92 | |||
| 93 | Lemma val_at_prefix xs xs' i ℓ : | ||
| 94 | val_at xs i = Some ℓ → xs `prefix_of` xs' → val_at xs' i = Some ℓ. | ||
| 95 | Proof. | ||
| 96 | unfold val_at. intros Hxs Hpre. | ||
| 97 | eapply prefix_lookup_Some; first exact Hxs. | ||
| 98 | by apply prefix_of_fmap. | ||
| 99 | Qed. | ||
| 100 | |||
| 101 | Lemma loc_at_val_at_Some xs i ℓ : | ||
| 102 | loc_at xs i = Some ℓ → ∃ v, val_at xs i = Some v. | ||
| 103 | Proof. | ||
| 104 | unfold loc_at, val_at. rewrite [xs.*2 !! i]list_lookup_fmap. | ||
| 105 | intros [[ℓ' v] [-> ->]]%list_lookup_fmap_Some_1. by exists v. | ||
| 106 | Qed. | ||
| 107 | |||
| 108 | Lemma loc_at_val_at_combine {hist i ℓ v} : | ||
| 109 | loc_at hist i = Some ℓ → | ||
| 110 | val_at hist i = Some v → | ||
| 111 | hist !! i = Some (ℓ, v). | ||
| 112 | Proof. | ||
| 113 | unfold loc_at, val_at. intros Hloc Hval. | ||
| 114 | rewrite list_lookup_fmap in Hloc. | ||
| 115 | rewrite list_lookup_fmap in Hval. | ||
| 116 | destruct (hist !! i) as [[ℓ' v']|]; last done. | ||
| 117 | simpl in *. congruence. | ||
| 118 | Qed. | ||
| 119 | |||
| 120 | Lemma loc_at_Some_length hist i ℓ : | ||
| 121 | loc_at hist i = Some ℓ → i < length hist. | ||
| 122 | Proof. | ||
| 123 | intros Hloc%lookup_lt_Some. | ||
| 124 | by rewrite length_fmap in Hloc. | ||
| 125 | Qed. | ||
| 126 | |||
| 127 | Lemma loc_at_drop hist n i ℓ : | ||
| 128 | loc_at hist (n + i) = Some ℓ → | ||
| 129 | loc_at (drop n hist) i = Some ℓ. | ||
| 130 | Proof. | ||
| 131 | unfold loc_at. intros Hloc. | ||
| 132 | by rewrite list_lookup_fmap lookup_drop -list_lookup_fmap. | ||
| 133 | Qed. | ||
| 134 | |||
| 135 | Lemma loc_at_drop_2 hist n i : | ||
| 136 | loc_at (drop n hist) i = loc_at hist (n + i). | ||
| 137 | Proof. by rewrite /loc_at list_lookup_fmap lookup_drop -list_lookup_fmap. Qed. | ||
| 138 | |||
| 139 | Lemma val_at_drop hist n i ℓ : | ||
| 140 | val_at hist (n + i) = Some ℓ → | ||
| 141 | val_at (drop n hist) i = Some ℓ. | ||
| 142 | Proof. | ||
| 143 | unfold val_at. intros Hval. | ||
| 144 | by rewrite list_lookup_fmap lookup_drop -list_lookup_fmap. | ||
| 145 | Qed. | ||
| 146 | |||
| 147 | Definition hist_repr (hist : list (loc * val)) : iProp Σ := | ||
| 148 | [∗ list] i ↦ '(ℓ, data) ∈ hist, node_repr ℓ data (loc_at hist (S i)). | ||
| 149 | |||
| 150 | Lemma hist_repr_peek_1 hist i ℓ data : | ||
| 151 | loc_at hist i = Some ℓ → | ||
| 152 | val_at hist i = Some data → | ||
| 153 | hist_repr hist -∗ | ||
| 154 | node_repr ℓ data (loc_at hist (S i)) ∗ | ||
| 155 | (node_repr ℓ data (loc_at hist (S i)) -∗ hist_repr hist). | ||
| 156 | Proof. | ||
| 157 | iIntros "%Hloc %Hval Hrepr". | ||
| 158 | pose proof (loc_at_val_at_combine Hloc Hval) as Hi. | ||
| 159 | iPoseProof (big_sepL_lookup_acc with "Hrepr") as "[Hnode Hclose]". | ||
| 160 | { apply Hi. } | ||
| 161 | iFrame. | ||
| 162 | Qed. | ||
| 163 | |||
| 164 | Lemma hist_repr_peek_2 hist i ℓ : | ||
| 165 | loc_at hist i = Some ℓ → | ||
| 166 | hist_repr hist -∗ | ||
| 167 | ∃ data, node_repr ℓ data (loc_at hist (S i)) ∗ | ||
| 168 | (node_repr ℓ data (loc_at hist (S i)) -∗ hist_repr hist). | ||
| 169 | Proof. | ||
| 170 | iIntros "%Hloc Hrepr". | ||
| 171 | assert (∃ v, val_at hist i = Some v) as [v Hval]. | ||
| 172 | { by apply loc_at_val_at_Some in Hloc. } | ||
| 173 | iExists v. by iApply hist_repr_peek_1. | ||
| 174 | Qed. | ||
| 175 | |||
| 176 | Lemma hist_repr_peek_3 hist i ℓ : | ||
| 177 | loc_at hist i = Some ℓ → | ||
| 178 | hist_repr hist -∗ | ||
| 179 | (ℓ +ₗ 1) ↦ loc_opt_hl (loc_at hist (S i)) ∗ | ||
| 180 | ((ℓ +ₗ 1) ↦ loc_opt_hl (loc_at hist (S i)) -∗ hist_repr hist). | ||
| 181 | Proof. | ||
| 182 | iIntros "%Hloc Hrepr". | ||
| 183 | iDestruct (hist_repr_peek_2 with "[$]") as "(%v & @ & Hclose)"; first done. | ||
| 184 | iFrame. iIntros "Hnnext". iApply "Hclose". iFrame. | ||
| 185 | Qed. | ||
| 186 | |||
| 187 | Lemma loc_at_None hist pos : | ||
| 188 | loc_at hist pos = None → hist !! pos = None. | ||
| 189 | Proof. | ||
| 190 | rewrite /loc_at list_lookup_fmap. | ||
| 191 | by destruct (hist !! pos). | ||
| 192 | Qed. | ||
| 193 | |||
| 194 | Lemma loc_at_length hist pos : | ||
| 195 | loc_at hist pos = None → length hist ≤ pos. | ||
| 196 | Proof. | ||
| 197 | intros H%loc_at_None. | ||
| 198 | by apply lookup_ge_None. | ||
| 199 | Qed. | ||
| 200 | |||
| 201 | Lemma hist_repr_proj hist i ℓ : | ||
| 202 | loc_at hist i = Some ℓ → | ||
| 203 | hist_repr hist -∗ | ||
| 204 | (∃ v, (ℓ +ₗ 1) ↦ v) ∗ hist_repr (drop (S i) hist). | ||
| 205 | Proof. | ||
| 206 | iIntros "%Hℓi Hrepr". | ||
| 207 | iDestruct (big_sepL_take_drop _ _ (S i) with "Hrepr") as "[Hrepr1 Hrepr2]". | ||
| 208 | |||
| 209 | rewrite /loc_at list_lookup_fmap in Hℓi. | ||
| 210 | destruct (hist !! i) as [[ℓ' v]|] eqn:Hi; last done. | ||
| 211 | simpl in *. injection Hℓi as ->. | ||
| 212 | |||
| 213 | iDestruct (big_sepL_lookup_acc with "Hrepr1") as "[Hrepr Hclose]". | ||
| 214 | { rewrite lookup_take_lt //. lia. } | ||
| 215 | iSimplifyEq. iDestruct "Hrepr" as "@". iFrame. | ||
| 216 | iClear "Hndata Hclose". | ||
| 217 | |||
| 218 | iAssert (hist_repr (drop (S i) hist)) with "[Hrepr2]" as "Hrepr2". | ||
| 219 | { unfold hist_repr. iApply (big_sepL_mono with "Hrepr2"). | ||
| 220 | iIntros (k [ℓ' v'] Hk) "Hrepr". | ||
| 221 | by rewrite loc_at_drop_2 Nat.add_succ_l Nat.add_succ_r. } | ||
| 222 | done. | ||
| 223 | Qed. | ||
| 224 | |||
| 225 | Lemma loc_at_inj_aux hist i j ℓ : | ||
| 226 | i < j → | ||
| 227 | loc_at hist i = Some ℓ → | ||
| 228 | loc_at hist j = Some ℓ → | ||
| 229 | hist_repr hist -∗ | ||
| 230 | False. | ||
| 231 | Proof. | ||
| 232 | iIntros "%ij %Hloci %Hlocj Hrepr". | ||
| 233 | assert (i < length hist). { by eapply loc_at_Some_length. } | ||
| 234 | assert (j < length hist). { by eapply loc_at_Some_length. } | ||
| 235 | assert (∃ n, j = S i + n) as [n ->]. | ||
| 236 | { exists (j - i - 1). lia. } | ||
| 237 | iPoseProof (hist_repr_proj hist i ℓ Hloci with "Hrepr") as "[[%ni Hℓi] Hrepr']". | ||
| 238 | |||
| 239 | assert (Hlocj' : loc_at (drop (S i) hist) n = Some ℓ). | ||
| 240 | { by apply loc_at_drop. } | ||
| 241 | |||
| 242 | iPoseProof (hist_repr_proj _ n ℓ Hlocj' with "Hrepr'") as "[[%nj Hℓj] _]". | ||
| 243 | by iCombine "Hℓi Hℓj" gives %[? _]. | ||
| 244 | Qed. | ||
| 245 | |||
| 246 | Lemma loc_at_inj {hist i j ℓ} : | ||
| 247 | loc_at hist i = Some ℓ → | ||
| 248 | loc_at hist j = Some ℓ → | ||
| 249 | hist_repr hist -∗ | ||
| 250 | ⌜i = j⌝. | ||
| 251 | Proof. | ||
| 252 | iIntros "%Hloci %Hlocj Hrepr". | ||
| 253 | destruct (loc_at_val_at_Some hist i ℓ Hloci) as [vi Hvali]. | ||
| 254 | destruct (loc_at_val_at_Some hist j ℓ Hlocj) as [vj Hvalj]. | ||
| 255 | destruct (decide (i < j)) as [Hij|Hij]. | ||
| 256 | - (* i < j *) | ||
| 257 | by iPoseProof (loc_at_inj_aux with "Hrepr") as "H". | ||
| 258 | - (* i ≥ j *) | ||
| 259 | destruct (decide (j < i)) as [Hji|Hji]. | ||
| 260 | + (* j < i *) | ||
| 261 | by iPoseProof (loc_at_inj_aux with "Hrepr") as "H". | ||
| 262 | + (* i = j *) | ||
| 263 | iPureIntro. lia. | ||
| 264 | Qed. | ||
| 265 | |||
| 266 | Definition queue_repr_1 (γs : gstate) ℓ_q (vs : list val) : iProp Σ := | ||
| 267 | ∃ (hist : list (loc * val)) (ℓ_head ℓ_tail : loc) (hpos tpos : nat), | ||
| 268 | ("Hhead" ∷ ℓ_q ↦ #ℓ_head) ∗ | ||
| 269 | ("Htail" ∷ (ℓ_q +ₗ 1) ↦ #ℓ_tail) ∗ | ||
| 270 | ("Hrepr" ∷ hist_repr hist) ∗ | ||
| 271 | ("Hhist●" ∷ γs.(γ_hist) ↪●ML hist) ∗ | ||
| 272 | ("Hhpos●" ∷ γs.(γ_hpos) ↪●MN hpos) ∗ | ||
| 273 | ("%Hℓhpos" ∷ ⌜loc_at hist hpos = Some ℓ_head⌝) ∗ | ||
| 274 | ("%Hℓtpos" ∷ ⌜loc_at hist tpos = Some ℓ_tail⌝) ∗ | ||
| 275 | ("%Hvs" ∷ ⌜vs = (drop (S hpos) hist).*2⌝). | ||
| 276 | |||
| 277 | Definition queue_repr ℓ_q (vs : list val) : iProp Σ := | ||
| 278 | ∃ (γs : gstate), meta ℓ_q nroot γs ∗ queue_repr_1 γs ℓ_q vs. | ||
| 279 | |||
| 280 | #[global] Instance queue_repr_timeless ℓ_q vs : Timeless (queue_repr ℓ_q vs) := _. | ||
| 281 | |||
| 282 | Lemma new_node_spec data : | ||
| 283 | {{{ True }}} | ||
| 284 | new_node data | ||
| 285 | {{{ (ℓ : loc), RET #ℓ; node_repr ℓ data None }}}. | ||
| 286 | Proof. | ||
| 287 | iIntros "%Φ _ HΦ". iUnfold new_node. wp_lam. | ||
| 288 | wp_alloc ℓ_node as "Hℓ_node"; first done. | ||
| 289 | iDestruct (array_cons with "Hℓ_node") as "[Hℓ_node0 Hℓ_node1]". | ||
| 290 | iDestruct (array_cons with "Hℓ_node1") as "[Hℓ_node1 _]". | ||
| 291 | wp_let. do 2 wp_store. | ||
| 292 | iMod (pointsto_persist with "Hℓ_node0") as "Hℓ_node0". | ||
| 293 | iModIntro. iApply "HΦ". by iFrame. | ||
| 294 | Qed. | ||
| 295 | |||
| 296 | Lemma new_spec : | ||
| 297 | {{{ True }}} | ||
| 298 | new #() | ||
| 299 | {{{ (ℓ : loc), RET #ℓ; queue_repr ℓ [] }}}. | ||
| 300 | Proof. | ||
| 301 | iIntros "%Φ _ HΦ". iUnfold new. | ||
| 302 | wp_lam. wp_apply (new_node_spec with "[//]"). | ||
| 303 | iIntros (ℓ_node) "Hnode". wp_let. | ||
| 304 | |||
| 305 | set ℓ_head := ℓ_node. set ℓ_tail := ℓ_node. | ||
| 306 | set hpos := 0. set tpos := 0. | ||
| 307 | set hist := [(ℓ_node, #())] : list (loc * val). | ||
| 308 | iAssert (hist_repr hist) with "[$Hnode //]" as "Hrepr". | ||
| 309 | |||
| 310 | iMod (mono_list_own_alloc hist) as "[%γ_hist [Hhist● _]]". | ||
| 311 | iMod (mono_nat_own_alloc hpos) as "[%γ_hpos [Hhpos● _]]". | ||
| 312 | set γs := {| γ_hist := γ_hist; γ_hpos := γ_hpos |}. | ||
| 313 | |||
| 314 | iApply wp_fupd. iApply wp_allocN; [lia|done|]. | ||
| 315 | iIntros "!> %ℓ [Hℓ [Htok _]]". | ||
| 316 | iDestruct (array_cons with "Hℓ") as "[Hℓ0 Hℓ1]". | ||
| 317 | iDestruct (array_cons with "Hℓ1") as "[Hℓ1 _]". | ||
| 318 | rewrite Loc.add_0. | ||
| 319 | |||
| 320 | iMod (meta_set ⊤ ℓ γs nroot with "Htok") as "Hmeta"; first done. | ||
| 321 | |||
| 322 | iApply "HΦ". iModIntro. iFrame. by iExists tpos. | ||
| 323 | Qed. | ||
| 324 | |||
| 325 | Lemma try_bump_tail_spec hist0 tpos ℓ_old ℓ_new ℓ_q γs : | ||
| 326 | loc_at hist0 tpos = Some ℓ_old → | ||
| 327 | loc_at hist0 (S tpos) = Some ℓ_new → | ||
| 328 | γs.(γ_hist) ↪◯ML hist0 -∗ | ||
| 329 | <<{ ∀∀ vs, queue_repr_1 γs ℓ_q vs }>> | ||
| 330 | CAS #(ℓ_q +ₗ 1) #ℓ_old #ℓ_new @ ∅ | ||
| 331 | <<{ ∃∃ (b : bool), queue_repr_1 γs ℓ_q vs | RET #b }>>. | ||
| 332 | Proof. | ||
| 333 | iIntros "%Hold %Hnew #Hhist◯ %Φ AU". | ||
| 334 | wp_bind (CmpXchg _ _ _)%E. | ||
| 335 | iMod "AU" as "(%vs & (%hist1 & %ℓ_head & %ℓ_tail & %hpos & %tpos' & @) & [_ Hclose])". | ||
| 336 | iAssert ⌜hist0 `prefix_of` hist1⌝%I as %Hhist01. | ||
| 337 | { by iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist◯") as "[_ ?]". } | ||
| 338 | |||
| 339 | destruct (decide (ℓ_tail = ℓ_old)) as [->|]. | ||
| 340 | - (* The tail has not been updated yet *) | ||
| 341 | wp_cmpxchg_suc. | ||
| 342 | |||
| 343 | iAssert (queue_repr_1 γs ℓ_q vs) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 344 | { iPureIntro. | ||
| 345 | repeat split; try done || lia. | ||
| 346 | exists (S tpos). repeat split; try done. | ||
| 347 | by eapply loc_at_prefix. } | ||
| 348 | iMod ("Hclose" with "[$Hq]") as "HΦ". | ||
| 349 | iModIntro. wp_pures. iApply "HΦ". | ||
| 350 | - (* The tail has been updated in the meantime *) | ||
| 351 | wp_cmpxchg_fail. | ||
| 352 | iAssert (queue_repr_1 γs ℓ_q vs) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 353 | { iExists tpos'. by iFrameNamed. } | ||
| 354 | iMod ("Hclose" with "[$Hq]") as "HΦ". | ||
| 355 | iModIntro. wp_pures. iApply "HΦ". | ||
| 356 | Qed. | ||
| 357 | |||
| 358 | (* Given a history that is represented, after updating the [next] | ||
| 359 | pointer of the current tail node to a new tail node, we obtain a | ||
| 360 | new (extended) history (with the new tail node appended). *) | ||
| 361 | Lemma hist_repr_snoc hist tpos (ℓ_tail ℓ_new : loc) data : | ||
| 362 | length hist = S tpos → | ||
| 363 | loc_at hist tpos = Some ℓ_tail → | ||
| 364 | node_repr ℓ_new data None -∗ | ||
| 365 | hist_repr hist -∗ | ||
| 366 | (ℓ_tail +ₗ 1) ↦ loc_opt_hl None ∗ | ||
| 367 | ((ℓ_tail +ₗ 1) ↦ loc_opt_hl (Some ℓ_new) -∗ hist_repr (hist ++ [(ℓ_new, data)])). | ||
| 368 | Proof. | ||
| 369 | unfold loc_at. | ||
| 370 | iIntros "%Hhistlen %Hloc @ Hhist". | ||
| 371 | rewrite list_lookup_fmap in Hloc. | ||
| 372 | destruct (hist !! tpos) as [[ℓ_tail' v_tail]|] eqn:Htpos; last done. | ||
| 373 | injection Hloc as ->. | ||
| 374 | apply list_elem_of_split_length in Htpos as (hist' & empty & -> & Htpos). | ||
| 375 | rewrite length_app length_cons -Htpos in Hhistlen. | ||
| 376 | assert (empty = []) as ->. { apply nil_length_inv. lia. } | ||
| 377 | clear Hhistlen Htpos. | ||
| 378 | |||
| 379 | iPoseProof (big_sepL_snoc with "Hhist") as "[Hinit Htail]". | ||
| 380 | iNamedSuffix "Htail" "_tail". | ||
| 381 | |||
| 382 | iSimplifyEq. | ||
| 383 | assert (loc_at (hist' ++ [(ℓ_tail, v_tail)]) (S (length hist')) = None) as ->. | ||
| 384 | { apply lookup_ge_None_2. rewrite length_fmap length_app length_cons /=. lia. } | ||
| 385 | iFrame "Hnnext_tail". iIntros "Hnnext_tail". | ||
| 386 | iSimplifyEq. rewrite /hist_repr. | ||
| 387 | iApply big_sepL_snoc. | ||
| 388 | iSplitR "Hndata Hnnext". | ||
| 389 | - iApply big_sepL_snoc. iSplitL "Hinit". | ||
| 390 | + iApply (big_sepL_mono with "Hinit"). | ||
| 391 | iIntros (k [ℓ v] Hk) "Hrepr". | ||
| 392 | assert (loc_at ((hist' ++ [(ℓ_tail, v_tail)]) ++ [(ℓ_new, data)]) (S k) = loc_at (hist' ++ [(ℓ_tail, v_tail)]) (S k)) as ->. | ||
| 393 | { rewrite /loc_at !list_lookup_fmap lookup_app_l // length_app /=. | ||
| 394 | apply lookup_lt_Some in Hk. lia. } | ||
| 395 | done. | ||
| 396 | + iFrame. | ||
| 397 | rewrite /loc_at list_lookup_fmap lookup_app_r; last first. | ||
| 398 | { rewrite length_app /=. lia. } | ||
| 399 | by rewrite length_app /= Nat.add_1_r Nat.sub_diag. | ||
| 400 | - iFrame. | ||
| 401 | rewrite /= /loc_at length_app /= Nat.add_1_r list_lookup_fmap lookup_app_r. | ||
| 402 | * by rewrite !length_app /= !Nat.add_1_r Nat.sub_succ -Arith_base.minus_Sn_m_stt // Nat.sub_diag. | ||
| 403 | * rewrite length_app /= Nat.add_1_r. lia. | ||
| 404 | Qed. | ||
| 405 | |||
| 406 | Lemma set_tail_spec (ℓ_q ℓ_node : loc) data : | ||
| 407 | node_repr ℓ_node data None -∗ | ||
| 408 | <<{ ∀∀ vs, queue_repr ℓ_q vs }>> | ||
| 409 | set_tail #ℓ_q #ℓ_node @ ∅ | ||
| 410 | <<{ queue_repr ℓ_q (vs ++ [data]) | RET #() }>>. | ||
| 411 | Proof. | ||
| 412 | iIntros "@ %Φ AU". iLöb as "IH". wp_rec. | ||
| 413 | |||
| 414 | wp_pures. wp_bind (! _)%E. | ||
| 415 | iMod "AU" as "(%vs & (%γs & #Hγs & %hist0 & %ℓ_head & %ℓ_tail & %hpos & %tpos & @) & [Hclose _])". | ||
| 416 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist0◯". | ||
| 417 | wp_load. iSimpl in "Hrepr". | ||
| 418 | rename Hℓtpos into Hℓtpos0. | ||
| 419 | iAssert (queue_repr_1 γs ℓ_q vs) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 420 | { iExists tpos. by iFrameNamed. } | ||
| 421 | iMod ("Hclose" with "[$]") as "AU". clear Hvs Hℓhpos hpos vs ℓ_head. | ||
| 422 | iModIntro. wp_let. wp_pure. | ||
| 423 | |||
| 424 | wp_bind (! _)%E. | ||
| 425 | iMod "AU" as "(%vs & (%γs' & Hγs' & %hist2 & %ℓ_head & %ℓ_tail'' & %hpos & %tpos'' & @) & [Hclose _])". | ||
| 426 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 427 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist0◯") as %Hhist02. | ||
| 428 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist2◯". | ||
| 429 | destruct Hhist02 as [_ Hhist02]. rename Hℓtpos into Hℓtpos2. | ||
| 430 | assert (Hℓtpos0'' : loc_at hist2 tpos = Some ℓ_tail). | ||
| 431 | { by eapply loc_at_prefix. } clear Hℓtpos0. | ||
| 432 | wp_pures. | ||
| 433 | iDestruct (hist_repr_peek_2 with "[$Hrepr]") as "(%w & Hnrepr & Hrepr)"; first done. | ||
| 434 | iNamedSuffix "Hnrepr" "_tail". | ||
| 435 | wp_load. | ||
| 436 | iPoseProof ("Hrepr" with "[$]") as "Hrepr". | ||
| 437 | iAssert (queue_repr_1 γs ℓ_q vs) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 438 | { iExists tpos''. by iFrameNamed. } | ||
| 439 | iMod ("Hclose" with "[$]") as "AU". clear Hvs Hℓhpos Hℓtpos2 hpos vs ℓ_head ℓ_tail''. | ||
| 440 | iModIntro. | ||
| 441 | |||
| 442 | destruct (loc_at hist2 (S tpos)) as [ℓ_next|] eqn:Htpos2. | ||
| 443 | + (* it is not the last node *) | ||
| 444 | simpl. wp_pures. wp_bind (Snd (CmpXchg _ _ _))%E. | ||
| 445 | awp_apply (try_bump_tail_spec with "[//]"). | ||
| 446 | { apply Hℓtpos0''. } | ||
| 447 | { done. } | ||
| 448 | rewrite /atomic_acc /=. | ||
| 449 | iMod "AU" as "(%vs & (%γs' & Hγs' & Hrepr) & [Hclose _])". | ||
| 450 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 451 | iModIntro. iExists vs. iFrame "Hrepr". iSplit. | ||
| 452 | * iFrame. iIntros "Hrepr". by iApply ("Hclose" with "[$Hrepr]"). | ||
| 453 | * iIntros "%b Hrepr". | ||
| 454 | iMod ("Hclose" with "[$Hrepr //]") as "AU". | ||
| 455 | iModIntro. wp_pures. | ||
| 456 | by iApply ("IH" with "[$] [$] [$]"). | ||
| 457 | + (* it is the last (non-final) node *) | ||
| 458 | simpl. wp_pures. wp_bind (CmpXchg _ _ _)%E. | ||
| 459 | |||
| 460 | iMod "AU" as "(%vs & (%γs' & Hγs' & %hist3 & %ℓ_head & %ℓ_tail''' & %hpos & %tpos''' & @) & Hclose)". | ||
| 461 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 462 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist2◯") as %Hhist23. | ||
| 463 | destruct Hhist23 as [_ Hhist23]. rename Hℓtpos into Hℓtpos3. | ||
| 464 | |||
| 465 | destruct (loc_at hist3 (S tpos)) as [ℓ_tail''''|] eqn:Hrace. | ||
| 466 | * (* Another item has been enqueued in the meantime, so the CAS is poised to fail. *) | ||
| 467 | iDestruct "Hclose" as "[Hclose _]". | ||
| 468 | |||
| 469 | iDestruct (hist_repr_peek_3 with "[$Hrepr]") as "[Hℓ_tail1 Hrepr]". | ||
| 470 | { eapply prefix_lookup_Some; first apply Hℓtpos0''. by apply prefix_of_fmap. } | ||
| 471 | rewrite Hrace /=. | ||
| 472 | wp_cmpxchg_fail. | ||
| 473 | iSpecialize ("Hrepr" with "Hℓ_tail1"). | ||
| 474 | iAssert (queue_repr_1 γs ℓ_q vs) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 475 | { iExists tpos'''. by iFrameNamed. } | ||
| 476 | iMod ("Hclose" with "[$]") as "AU". iModIntro. | ||
| 477 | wp_pures. iApply ("IH" with "Hndata Hnnext AU"). | ||
| 478 | * (* This node is still the tail, so the CAS will succeed. *) | ||
| 479 | iDestruct "Hclose" as "[_ Hclose]". | ||
| 480 | |||
| 481 | iAssert ⌜length hist3 = S tpos⌝%I as %Hhist3. | ||
| 482 | { apply loc_at_length in Hrace as Hhist3. | ||
| 483 | iPureIntro. apply loc_at_prefix with (xs':=hist3) in Hℓtpos0''; last done. | ||
| 484 | apply lookup_lt_Some in Hℓtpos0''. rewrite length_fmap in Hℓtpos0''. lia. } | ||
| 485 | |||
| 486 | iDestruct (hist_repr_peek_2 with "[$Hrepr]") as "(%w' & Hℓ_tail3 & Hrepr)". | ||
| 487 | { eapply prefix_lookup_Some; first apply Hℓtpos0''. by apply prefix_of_fmap. } | ||
| 488 | iNamedSuffix "Hℓ_tail3" "_tail3". | ||
| 489 | iPoseProof ("Hrepr" with "[$]") as "Hrepr". clear w. | ||
| 490 | |||
| 491 | iPoseProof (hist_repr_snoc with "[$Hndata $Hnnext] [$Hrepr]") as "[Htnode Henq]". | ||
| 492 | { apply Hhist3. } | ||
| 493 | { eapply prefix_lookup_Some. apply Hℓtpos0''. by apply prefix_of_fmap. } | ||
| 494 | wp_cmpxchg_suc. iSpecialize ("Henq" with "Htnode"). | ||
| 495 | |||
| 496 | iMod (mono_list_auth_own_update_app [(ℓ_node, data)] with "Hhist●") as "[Hhist● _]". | ||
| 497 | |||
| 498 | iAssert (queue_repr_1 γs ℓ_q (vs ++ [data])) with "[$Hhead $Htail $Hhist● $Hhpos● $Henq]" as "Hq". | ||
| 499 | { iExists tpos'''. iPureIntro. | ||
| 500 | repeat split; try done. | ||
| 501 | - eapply loc_at_prefix; first done. | ||
| 502 | by apply prefix_app_r. | ||
| 503 | - eapply loc_at_prefix; first done. | ||
| 504 | by apply prefix_app_r. | ||
| 505 | - rewrite drop_app fmap_app -Hvs. | ||
| 506 | f_equal. | ||
| 507 | assert (Hhpos : hpos < length hist3). | ||
| 508 | { apply lookup_lt_Some in Hℓhpos. | ||
| 509 | by rewrite length_fmap in Hℓhpos. } | ||
| 510 | by assert (S hpos - length hist3 = 0) as -> by lia. } | ||
| 511 | iMod ("Hclose" with "[$]") as "HΦ". | ||
| 512 | iModIntro. wp_pures. done. | ||
| 513 | Qed. | ||
| 514 | |||
| 515 | Lemma enqueue_spec (ℓ_q : loc) (data : val) : | ||
| 516 | ⊢ <<{ ∀∀ vs, queue_repr ℓ_q vs }>> | ||
| 517 | enqueue #ℓ_q data @ ∅ | ||
| 518 | <<{ queue_repr ℓ_q (vs ++ [data]) | RET #() }>>. | ||
| 519 | Proof. | ||
| 520 | iIntros "%Φ AU". wp_lam. wp_pures. | ||
| 521 | wp_apply (new_node_spec with "[//]") as "%ℓ_node Hnode". | ||
| 522 | awp_apply (set_tail_spec with "[$Hnode]"). | ||
| 523 | rewrite /atomic_acc /=. iMod "AU" as "(%vs & Hrepr & Hclose)". | ||
| 524 | iModIntro. iFrame. | ||
| 525 | Qed. | ||
| 526 | |||
| 527 | Lemma try_dequeue_spec (ℓ_q : loc) : | ||
| 528 | ⊢ <<{ ∀∀ vs, queue_repr ℓ_q vs }>> | ||
| 529 | try_dequeue #ℓ_q @ ∅ | ||
| 530 | <<{ queue_repr ℓ_q (tail vs) | RET (val_opt_hl (head vs)) }>>. | ||
| 531 | Proof. | ||
| 532 | iIntros "%Φ AU". iLöb as "IH". wp_rec. | ||
| 533 | |||
| 534 | wp_bind (! _)%E. | ||
| 535 | iMod "AU" as "(%vs0 & (%γs & #Hγs & %hist0 & %ℓ_head0 & %ℓ_tail0 & %hpos0 & %tpos0 & @) & [Hclose _])". | ||
| 536 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist0◯". | ||
| 537 | iDestruct (mono_nat_lb_own_get with "Hhpos●") as "#Hhpos0◯". | ||
| 538 | rename Hℓhpos into Hℓhpos0. | ||
| 539 | wp_load. | ||
| 540 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 541 | { iExists tpos0. by iFrameNamed. } | ||
| 542 | iModIntro. clear vs0 Hvs tpos0 Hℓtpos ℓ_tail0. | ||
| 543 | |||
| 544 | wp_pures. wp_bind (! _)%E. | ||
| 545 | iMod "AU" as "(%vs1 & (%γs' & Hγs' & %hist1 & %ℓ_head1 & %ℓ_tail1 & %hpos1 & %tpos1 & @) & Hclose)". | ||
| 546 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 547 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist1◯". | ||
| 548 | iDestruct (mono_nat_auth_lb_own_valid with "Hhpos● Hhpos0◯") as %Hhpos01. | ||
| 549 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist0◯") as %Hhist01. | ||
| 550 | destruct Hhpos01 as [_ Hhpos01]. destruct Hhist01 as [_ Hhist01]. | ||
| 551 | assert (Hℓhpos0' : loc_at hist1 hpos0 = Some ℓ_head0). | ||
| 552 | { by eapply loc_at_prefix. } clear Hℓhpos0. | ||
| 553 | iDestruct (hist_repr_peek_2 with "[$Hrepr]") as "(%u & @ & Hrepr)"; first done. | ||
| 554 | wp_load. | ||
| 555 | iSpecialize ("Hrepr" with "[$]"). | ||
| 556 | |||
| 557 | destruct (loc_at hist1 (S hpos0)) as [ℓ_headS0|] eqn:Hℓ_headS0. | ||
| 558 | - simpl. iDestruct "Hclose" as "[Hclose _]". | ||
| 559 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 560 | { iExists tpos1. by iFrameNamed. } | ||
| 561 | iModIntro. clear u vs1 Hvs tpos1 Hℓtpos ℓ_tail1. | ||
| 562 | |||
| 563 | wp_pures. wp_bind (! _)%E. | ||
| 564 | iMod "AU" as "(%vs2 & (%γs' & Hγs' & %hist2 & %ℓ_head2 & %ℓ_tail2 & %hpos2 & %tpos2 & @) & Hclose)". | ||
| 565 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 566 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist2◯". | ||
| 567 | iDestruct (mono_nat_auth_lb_own_valid with "Hhpos● Hhpos0◯") as %Hhpos02. | ||
| 568 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist1◯") as %Hhist12. | ||
| 569 | destruct Hhpos02 as [_ Hhpos02]. destruct Hhist12 as [_ Hhist12]. | ||
| 570 | assert (Hℓ_headS0' : loc_at hist2 (S hpos0) = Some ℓ_headS0). | ||
| 571 | { by eapply loc_at_prefix. } clear Hℓ_headS0. | ||
| 572 | apply loc_at_val_at_Some in Hℓ_headS0' as Hval_headS0'. | ||
| 573 | destruct Hval_headS0' as [w Hval_headS0']. | ||
| 574 | iDestruct (hist_repr_peek_1 with "[$Hrepr]") as "(@ & Hrepr)"; try done. | ||
| 575 | iDestruct "Hndata" as "#Hndata_head". | ||
| 576 | wp_load. | ||
| 577 | |||
| 578 | iDestruct "Hclose" as "[Hclose _]". | ||
| 579 | |||
| 580 | iSpecialize ("Hrepr" with "[$]"). | ||
| 581 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 582 | { iExists tpos2. by iFrameNamed. } | ||
| 583 | iModIntro. | ||
| 584 | clear vs2 Hvs tpos2 Hℓtpos ℓ_tail2. | ||
| 585 | |||
| 586 | wp_pures. | ||
| 587 | |||
| 588 | wp_bind (CmpXchg _ _ _)%E. | ||
| 589 | iMod "AU" as "(%vs3 & (%γs' & Hγs' & %hist3 & %ℓ_head3 & %ℓ_tail3 & %hpos3 & %tpos3 & @) & Hclose)". | ||
| 590 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 591 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist2◯") as %Hhist23. | ||
| 592 | destruct Hhist23 as [_ Hhist23]. | ||
| 593 | assert (Hhist13 : hist1 `prefix_of` hist3). | ||
| 594 | { by trans hist2. } | ||
| 595 | |||
| 596 | destruct (decide (ℓ_head0 = ℓ_head3)) as [->|Hhead03]. | ||
| 597 | + iDestruct "Hclose" as "[_ Hclose]". | ||
| 598 | |||
| 599 | wp_cmpxchg_suc. | ||
| 600 | |||
| 601 | assert (Hℓhpos0'' : loc_at hist3 hpos0 = Some ℓ_head3). | ||
| 602 | { by eapply loc_at_prefix. } clear Hℓhpos0'. | ||
| 603 | iAssert ⌜hpos0 = hpos3⌝%I as %->. | ||
| 604 | { by iApply (loc_at_inj with "Hrepr"). } | ||
| 605 | |||
| 606 | simplify_eq/=. | ||
| 607 | |||
| 608 | iMod (mono_nat_own_update (S hpos3) with "Hhpos●") as "[Hhpos● _]". { lia. } | ||
| 609 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "HΦ". | ||
| 610 | { iExists tpos3. iFrameNamed. | ||
| 611 | iPureIntro. repeat split; try done. | ||
| 612 | - by eapply loc_at_prefix. | ||
| 613 | - by rewrite -[S (S _)]Nat.add_1_r -drop_drop [(drop 1 _).*2]fmap_drop. } | ||
| 614 | |||
| 615 | iModIntro. wp_pures. | ||
| 616 | |||
| 617 | assert (Hval_headS0'' : val_at hist3 (S hpos3) = Some w). | ||
| 618 | { by eapply val_at_prefix. } clear Hval_headS0'. | ||
| 619 | unfold val_at in Hval_headS0''. | ||
| 620 | rewrite -[S hpos3]Nat.add_0_r -lookup_drop -fmap_drop /= -head_lookup in Hval_headS0''. | ||
| 621 | by rewrite Hval_headS0'' /=. | ||
| 622 | |||
| 623 | + iDestruct "Hclose" as "[Hclose _]". | ||
| 624 | |||
| 625 | wp_cmpxchg_fail. | ||
| 626 | |||
| 627 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 628 | { iExists tpos3. by iFrameNamed. } | ||
| 629 | |||
| 630 | iModIntro. wp_pures. iApply ("IH" with "AU"). | ||
| 631 | - apply loc_at_length in Hℓ_headS0 as Hhistlen. | ||
| 632 | rewrite drop_ge /= in Hvs; last lia. destruct vs1; try done. | ||
| 633 | simplify_eq/=. | ||
| 634 | |||
| 635 | iDestruct "Hclose" as "[_ Hclose]". | ||
| 636 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "HΦ". | ||
| 637 | { iExists tpos1. iFrameNamed. iPureIntro. | ||
| 638 | rewrite drop_ge //=. lia. } | ||
| 639 | |||
| 640 | iModIntro. wp_pures. iApply "HΦ". | ||
| 641 | Qed. | ||
| 642 | |||
| 643 | End basic_queue. | ||
| 644 | |||
| 645 | Definition basic_queue `{!heapGS Σ, !basic_queueG Σ} : basic_queue_spec.basic_queue Σ. | ||
| 646 | Proof. | ||
| 647 | refine {| basic_queue_spec.new_spec := new_spec; | ||
| 648 | basic_queue_spec.enqueue_spec := enqueue_spec; | ||
| 649 | basic_queue_spec.try_dequeue_spec := try_dequeue_spec |}. | ||
| 650 | Defined. | ||
| 651 | |||
| 652 | #[global] Typeclasses Opaque queue_repr. | ||