diff options
Diffstat (limited to 'theories/fin_queue_proof.v')
| -rw-r--r-- | theories/fin_queue_proof.v | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/theories/fin_queue_proof.v b/theories/fin_queue_proof.v new file mode 100644 index 0000000..64646ae --- /dev/null +++ b/theories/fin_queue_proof.v | |||
| @@ -0,0 +1,932 @@ | |||
| 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 fin_queue_spec upstream util. | ||
| 9 | |||
| 10 | Definition new_node : val := λ: "data" "final", | ||
| 11 | let: "ℓ_node" := AllocN #3 #() in | ||
| 12 | "ℓ_node" <- "data";; | ||
| 13 | "ℓ_node" +ₗ #1 <- NONE;; | ||
| 14 | "ℓ_node" +ₗ #2 <- "final";; | ||
| 15 | "ℓ_node". | ||
| 16 | |||
| 17 | Definition new : val := λ: <>, | ||
| 18 | let: "ℓ_node" := new_node #() #false in | ||
| 19 | AllocN #2 "ℓ_node". | ||
| 20 | |||
| 21 | Definition set_tail : val := rec: "go" "ℓ_q" "ℓ_node" := | ||
| 22 | let: "ℓ_tail" := !("ℓ_q" +ₗ #1) in | ||
| 23 | match: !("ℓ_tail" +ₗ #1) with | ||
| 24 | NONE => | ||
| 25 | if: CAS ("ℓ_tail" +ₗ #1) NONE (SOME "ℓ_node") then | ||
| 26 | #() | ||
| 27 | else | ||
| 28 | "go" "ℓ_q" "ℓ_node" | ||
| 29 | | SOME "ℓ_next" => | ||
| 30 | CAS ("ℓ_q" +ₗ #1) "ℓ_tail" "ℓ_next";; | ||
| 31 | "go" "ℓ_q" "ℓ_node" | ||
| 32 | end. | ||
| 33 | |||
| 34 | Definition enqueue : val := λ: "ℓ_q" "data", | ||
| 35 | set_tail "ℓ_q" (new_node "data" #false). | ||
| 36 | |||
| 37 | Definition finalize : val := λ: "ℓ_q" "data", | ||
| 38 | set_tail "ℓ_q" (new_node "data" #true). | ||
| 39 | |||
| 40 | Definition try_dequeue : val := rec: "go" "ℓ_q" := | ||
| 41 | let: "ℓ_head" := !"ℓ_q" in | ||
| 42 | match: !("ℓ_head" +ₗ #1) with | ||
| 43 | NONE => InjR NONE | ||
| 44 | | SOME "ℓ_next" => | ||
| 45 | if: !("ℓ_next" +ₗ #2) then | ||
| 46 | (* The next node is the final node. We refuse to dequeue and | ||
| 47 | return the data of the final node. *) | ||
| 48 | InjR (SOME !"ℓ_next") | ||
| 49 | else (* [ℓ_next] node type = normal *) | ||
| 50 | let: "v" := !"ℓ_next" in | ||
| 51 | if: CAS "ℓ_q" "ℓ_head" "ℓ_next" then | ||
| 52 | InjL "v" | ||
| 53 | else | ||
| 54 | "go" "ℓ_q" | ||
| 55 | end. | ||
| 56 | |||
| 57 | Class fin_queueG Σ := FinQueueG | ||
| 58 | { fin_queue_mono_natG :: mono_natG Σ; | ||
| 59 | fin_queue_mono_listG :: mono_listG (prodO locO valO) Σ; }. | ||
| 60 | |||
| 61 | Definition fin_queueΣ : gFunctors := | ||
| 62 | #[ mono_natΣ; mono_listΣ (prodO locO valO) ]. | ||
| 63 | |||
| 64 | #[global] Instance subG_fin_queueΣ {Σ} : subG fin_queueΣ Σ → fin_queueG Σ. | ||
| 65 | Proof. solve_inG. Qed. | ||
| 66 | |||
| 67 | Section fin_queue. | ||
| 68 | Context `{!heapGS Σ, !fin_queueG Σ}. | ||
| 69 | |||
| 70 | Record gstate := | ||
| 71 | { γ_hist : gname; | ||
| 72 | γ_hpos : gname; }. | ||
| 73 | Definition gstate_to_pair (γ : gstate) := | ||
| 74 | (γ.(γ_hist), γ.(γ_hpos)). | ||
| 75 | Definition gstate_of_pair '(γ_hist, γ_hpos) := | ||
| 76 | {| γ_hist := γ_hist; γ_hpos := γ_hpos |}. | ||
| 77 | Instance gstate_eq_dec : EqDecision gstate := ltac:(solve_decision). | ||
| 78 | Instance gstate_countable : Countable gstate. | ||
| 79 | Proof. | ||
| 80 | refine {| encode := encode ∘ gstate_to_pair; | ||
| 81 | decode := fmap gstate_of_pair ∘ decode; |}. | ||
| 82 | intros []. by rewrite /= decode_encode. | ||
| 83 | Qed. | ||
| 84 | |||
| 85 | Variant node_next := | ||
| 86 | | Normal (mℓ_next : option loc) | ||
| 87 | | Final. | ||
| 88 | Instance node_next_eq_dec : EqDecision node_next. | ||
| 89 | Proof. solve_decision. Defined. | ||
| 90 | Definition node_final_hl (n : node_next) := #(bool_decide (n = Final)). | ||
| 91 | Definition node_next_hl (n : node_next) := | ||
| 92 | match n with | ||
| 93 | | Normal mℓ_next => loc_opt_hl mℓ_next | ||
| 94 | | Final => NONEV | ||
| 95 | end. | ||
| 96 | |||
| 97 | Definition node_repr (ℓ : loc) (data : val) (n : node_next) : iProp Σ := | ||
| 98 | ("Hndata" ∷ ℓ ↦□ data) ∗ | ||
| 99 | ("Hnnext" ∷ (ℓ +ₗ 1) ↦ node_next_hl n) ∗ | ||
| 100 | ("Hnfinal" ∷ (ℓ +ₗ 2) ↦□ #(bool_decide (n = Final))). | ||
| 101 | |||
| 102 | Definition loc_at (hist : list (loc * val)) i := | ||
| 103 | hist.*1 !! i. | ||
| 104 | Definition val_at (hist : list (loc * val)) i := | ||
| 105 | hist.*2 !! i. | ||
| 106 | |||
| 107 | Lemma loc_at_prefix xs xs' i ℓ : | ||
| 108 | loc_at xs i = Some ℓ → xs `prefix_of` xs' → loc_at xs' i = Some ℓ. | ||
| 109 | Proof. | ||
| 110 | unfold loc_at. intros Hxs Hpre. | ||
| 111 | eapply prefix_lookup_Some; first exact Hxs. | ||
| 112 | by apply prefix_of_fmap. | ||
| 113 | Qed. | ||
| 114 | |||
| 115 | Lemma val_at_prefix xs xs' i ℓ : | ||
| 116 | val_at xs i = Some ℓ → xs `prefix_of` xs' → val_at xs' i = Some ℓ. | ||
| 117 | Proof. | ||
| 118 | unfold val_at. intros Hxs Hpre. | ||
| 119 | eapply prefix_lookup_Some; first exact Hxs. | ||
| 120 | by apply prefix_of_fmap. | ||
| 121 | Qed. | ||
| 122 | |||
| 123 | Lemma loc_at_val_at_Some xs i ℓ : | ||
| 124 | loc_at xs i = Some ℓ → ∃ v, val_at xs i = Some v. | ||
| 125 | Proof. | ||
| 126 | unfold loc_at, val_at. rewrite [xs.*2 !! i]list_lookup_fmap. | ||
| 127 | intros [[ℓ' v] [-> ->]]%list_lookup_fmap_Some_1. by exists v. | ||
| 128 | Qed. | ||
| 129 | |||
| 130 | Lemma loc_at_val_at_combine {hist i ℓ v} : | ||
| 131 | loc_at hist i = Some ℓ → | ||
| 132 | val_at hist i = Some v → | ||
| 133 | hist !! i = Some (ℓ, v). | ||
| 134 | Proof. | ||
| 135 | unfold loc_at, val_at. intros Hloc Hval. | ||
| 136 | rewrite list_lookup_fmap in Hloc. | ||
| 137 | rewrite list_lookup_fmap in Hval. | ||
| 138 | destruct (hist !! i) as [[ℓ' v']|]; last done. | ||
| 139 | simpl in *. congruence. | ||
| 140 | Qed. | ||
| 141 | |||
| 142 | Lemma loc_at_Some_length hist i ℓ : | ||
| 143 | loc_at hist i = Some ℓ → i < length hist. | ||
| 144 | Proof. | ||
| 145 | intros Hloc%lookup_lt_Some. | ||
| 146 | by rewrite length_fmap in Hloc. | ||
| 147 | Qed. | ||
| 148 | |||
| 149 | Lemma loc_at_drop hist n i ℓ : | ||
| 150 | loc_at hist (n + i) = Some ℓ → | ||
| 151 | loc_at (drop n hist) i = Some ℓ. | ||
| 152 | Proof. | ||
| 153 | unfold loc_at. intros Hloc. | ||
| 154 | by rewrite list_lookup_fmap lookup_drop -list_lookup_fmap. | ||
| 155 | Qed. | ||
| 156 | |||
| 157 | Lemma val_at_drop hist n i ℓ : | ||
| 158 | val_at hist (n + i) = Some ℓ → | ||
| 159 | val_at (drop n hist) i = Some ℓ. | ||
| 160 | Proof. | ||
| 161 | unfold val_at. intros Hval. | ||
| 162 | by rewrite list_lookup_fmap lookup_drop -list_lookup_fmap. | ||
| 163 | Qed. | ||
| 164 | |||
| 165 | Definition next_from (hist : list (loc * val)) fin i := | ||
| 166 | if fin && bool_decide (S i = length hist) then Final else Normal (loc_at hist (S i)). | ||
| 167 | |||
| 168 | Definition next_from_normal hist fin i v : | ||
| 169 | next_from hist fin i = Normal v → | ||
| 170 | (fin = false ∨ S i ≠ length hist) ∧ loc_at hist (S i) = v. | ||
| 171 | Proof. | ||
| 172 | rewrite /next_from. intros H. | ||
| 173 | destruct fin, (decide (S i = length hist)). | ||
| 174 | - rewrite bool_decide_true //= in H. | ||
| 175 | - rewrite bool_decide_false //= in H. | ||
| 176 | injection H as <-. split; last done. by right. | ||
| 177 | - injection H as <-. split; last done. by left. | ||
| 178 | - injection H as <-. split; last done. by left. | ||
| 179 | Qed. | ||
| 180 | |||
| 181 | Lemma next_from_S ℓv ℓvs fin n : | ||
| 182 | next_from (ℓv :: ℓvs) fin (S n) = next_from ℓvs fin n. | ||
| 183 | Proof. | ||
| 184 | rewrite /next_from /loc_at list_lookup_fmap /= -list_lookup_fmap. | ||
| 185 | erewrite bool_decide_ext; first done. lia. | ||
| 186 | Qed. | ||
| 187 | |||
| 188 | Lemma node_next_hl_next_from hist fin i : | ||
| 189 | node_next_hl (next_from hist fin i) = loc_opt_hl (loc_at hist (S i)). | ||
| 190 | Proof. | ||
| 191 | unfold node_next_hl, next_from. | ||
| 192 | destruct fin, (decide (S i = length hist)) as [Hhist|Hhist]; simpl; try done. | ||
| 193 | - by rewrite bool_decide_true // /loc_at list_lookup_fmap lookup_ge_None_2; last lia. | ||
| 194 | - by rewrite bool_decide_false. | ||
| 195 | Qed. | ||
| 196 | |||
| 197 | Definition hist_repr (hist : list (loc * val)) (fin : bool) : iProp Σ := | ||
| 198 | [∗ list] i ↦ '(ℓ, data) ∈ hist, node_repr ℓ data (next_from hist fin i). | ||
| 199 | |||
| 200 | Lemma hist_repr_peek_1 hist fin i ℓ data : | ||
| 201 | loc_at hist i = Some ℓ → | ||
| 202 | val_at hist i = Some data → | ||
| 203 | hist_repr hist fin -∗ | ||
| 204 | node_repr ℓ data (next_from hist fin i) ∗ | ||
| 205 | (node_repr ℓ data (next_from hist fin i) -∗ hist_repr hist fin). | ||
| 206 | Proof. | ||
| 207 | iIntros "%Hloc %Hval Hrepr". | ||
| 208 | pose proof (loc_at_val_at_combine Hloc Hval) as Hi. | ||
| 209 | iPoseProof (big_sepL_lookup_acc with "Hrepr") as "[Hnode Hclose]". | ||
| 210 | { apply Hi. } | ||
| 211 | iFrame. | ||
| 212 | Qed. | ||
| 213 | |||
| 214 | Lemma hist_repr_peek_2 hist fin i ℓ : | ||
| 215 | loc_at hist i = Some ℓ → | ||
| 216 | hist_repr hist fin -∗ | ||
| 217 | ∃ data, node_repr ℓ data (next_from hist fin i) ∗ | ||
| 218 | (node_repr ℓ data (next_from hist fin i) -∗ hist_repr hist fin). | ||
| 219 | Proof. | ||
| 220 | iIntros "%Hloc Hrepr". | ||
| 221 | assert (∃ v, val_at hist i = Some v) as [v Hval]. | ||
| 222 | { by apply loc_at_val_at_Some in Hloc. } | ||
| 223 | iExists v. by iApply hist_repr_peek_1. | ||
| 224 | Qed. | ||
| 225 | |||
| 226 | Lemma hist_repr_peek_3 hist fin i ℓ : | ||
| 227 | loc_at hist i = Some ℓ → | ||
| 228 | hist_repr hist fin -∗ | ||
| 229 | (ℓ +ₗ 1) ↦ node_next_hl (next_from hist fin i) ∗ | ||
| 230 | ((ℓ +ₗ 1) ↦ node_next_hl (next_from hist fin i) -∗ hist_repr hist fin). | ||
| 231 | Proof. | ||
| 232 | iIntros "%Hloc Hrepr". | ||
| 233 | iDestruct (hist_repr_peek_2 with "[$]") as "(%v & @ & Hclose)"; first done. | ||
| 234 | iFrame. iIntros "Hnnext". iApply "Hclose". iFrame. | ||
| 235 | Qed. | ||
| 236 | |||
| 237 | Lemma loc_at_None hist pos : | ||
| 238 | loc_at hist pos = None → hist !! pos = None. | ||
| 239 | Proof. | ||
| 240 | rewrite /loc_at list_lookup_fmap. | ||
| 241 | by destruct (hist !! pos). | ||
| 242 | Qed. | ||
| 243 | |||
| 244 | Lemma loc_at_length hist pos : | ||
| 245 | loc_at hist pos = None → length hist ≤ pos. | ||
| 246 | Proof. | ||
| 247 | intros H%loc_at_None. | ||
| 248 | by apply lookup_ge_None. | ||
| 249 | Qed. | ||
| 250 | |||
| 251 | Lemma next_from_drop ℓvs fin n i : | ||
| 252 | next_from ℓvs fin (n + i) = next_from (drop n ℓvs) fin i. | ||
| 253 | Proof. | ||
| 254 | rewrite /next_from /loc_at list_lookup_fmap /= -list_lookup_fmap. | ||
| 255 | erewrite bool_decide_ext with (Q:=S i = length (drop n ℓvs)); last first. | ||
| 256 | { rewrite length_drop. lia. } | ||
| 257 | by rewrite fmap_drop list_lookup_fmap lookup_drop -list_lookup_fmap plus_n_Sm. | ||
| 258 | Qed. | ||
| 259 | |||
| 260 | Lemma hist_repr_proj hist i ℓ fin : | ||
| 261 | loc_at hist i = Some ℓ → | ||
| 262 | hist_repr hist fin -∗ | ||
| 263 | (∃ v, (ℓ +ₗ 1) ↦ v) ∗ hist_repr (drop (S i) hist) fin. | ||
| 264 | Proof. | ||
| 265 | iIntros "%Hℓi Hrepr". | ||
| 266 | iDestruct (big_sepL_take_drop _ _ (S i) with "Hrepr") as "[Hrepr1 Hrepr2]". | ||
| 267 | |||
| 268 | rewrite /loc_at list_lookup_fmap in Hℓi. | ||
| 269 | destruct (hist !! i) as [[ℓ' v]|] eqn:Hi; last done. | ||
| 270 | simpl in *. injection Hℓi as ->. | ||
| 271 | |||
| 272 | iDestruct (big_sepL_lookup_acc with "Hrepr1") as "[Hrepr Hclose]". | ||
| 273 | { rewrite lookup_take_lt //. lia. } | ||
| 274 | iSimplifyEq. iDestruct "Hrepr" as "@". iFrame. | ||
| 275 | iClear "Hndata Hnfinal Hclose". | ||
| 276 | |||
| 277 | iAssert (hist_repr (drop (S i) hist) fin) with "[Hrepr2]" as "Hrepr2". | ||
| 278 | { unfold hist_repr. iApply (big_sepL_mono with "Hrepr2"). | ||
| 279 | iIntros (k [ℓ' v'] Hk) "Hrepr". | ||
| 280 | rewrite -next_from_drop; first done. } | ||
| 281 | done. | ||
| 282 | Qed. | ||
| 283 | |||
| 284 | Lemma loc_at_inj_aux hist i j ℓ fin : | ||
| 285 | i < j → | ||
| 286 | loc_at hist i = Some ℓ → | ||
| 287 | loc_at hist j = Some ℓ → | ||
| 288 | hist_repr hist fin -∗ | ||
| 289 | False. | ||
| 290 | Proof. | ||
| 291 | iIntros "%ij %Hloci %Hlocj Hrepr". | ||
| 292 | assert (i < length hist). { by eapply loc_at_Some_length. } | ||
| 293 | assert (j < length hist). { by eapply loc_at_Some_length. } | ||
| 294 | assert (∃ n, j = S i + n) as [n ->]. | ||
| 295 | { exists (j - i - 1). lia. } | ||
| 296 | iPoseProof (hist_repr_proj hist i ℓ fin Hloci with "Hrepr") as "[[%ni Hℓi] Hrepr']". | ||
| 297 | |||
| 298 | assert (Hlocj' : loc_at (drop (S i) hist) n = Some ℓ). | ||
| 299 | { by apply loc_at_drop. } | ||
| 300 | |||
| 301 | iPoseProof (hist_repr_proj _ n ℓ fin Hlocj' with "Hrepr'") as "[[%nj Hℓj] _]". | ||
| 302 | by iCombine "Hℓi Hℓj" gives %[? _]. | ||
| 303 | Qed. | ||
| 304 | |||
| 305 | Lemma loc_at_inj {hist i j ℓ fin} : | ||
| 306 | loc_at hist i = Some ℓ → | ||
| 307 | loc_at hist j = Some ℓ → | ||
| 308 | hist_repr hist fin -∗ | ||
| 309 | ⌜i = j⌝. | ||
| 310 | Proof. | ||
| 311 | iIntros "%Hloci %Hlocj Hrepr". | ||
| 312 | destruct (loc_at_val_at_Some hist i ℓ Hloci) as [vi Hvali]. | ||
| 313 | destruct (loc_at_val_at_Some hist j ℓ Hlocj) as [vj Hvalj]. | ||
| 314 | destruct (decide (i < j)) as [Hij|Hij]. | ||
| 315 | - (* i < j *) | ||
| 316 | by iPoseProof (loc_at_inj_aux with "Hrepr") as "H". | ||
| 317 | - (* i ≥ j *) | ||
| 318 | destruct (decide (j < i)) as [Hji|Hji]. | ||
| 319 | + (* j < i *) | ||
| 320 | by iPoseProof (loc_at_inj_aux with "Hrepr") as "H". | ||
| 321 | + (* i = j *) | ||
| 322 | iPureIntro. lia. | ||
| 323 | Qed. | ||
| 324 | |||
| 325 | Definition queue_γs_dq (hist : list (loc * val)) (hpos : nat) (mfin : option val) : dfrac := | ||
| 326 | match mfin with | ||
| 327 | | None => DfracOwn 1 | ||
| 328 | | Some _ => | ||
| 329 | if decide (S (S hpos) = length hist) | ||
| 330 | then DfracDiscarded | ||
| 331 | else DfracOwn 1 | ||
| 332 | end. | ||
| 333 | |||
| 334 | Lemma queue_γs_dq_cases hist hpos mfin : | ||
| 335 | queue_γs_dq hist hpos mfin = DfracOwn 1 ∨ | ||
| 336 | queue_γs_dq hist hpos mfin = DfracDiscarded. | ||
| 337 | Proof. | ||
| 338 | unfold queue_γs_dq. | ||
| 339 | destruct mfin, (decide (S (S hpos) = length hist)); | ||
| 340 | (by left) || by right. | ||
| 341 | Qed. | ||
| 342 | |||
| 343 | Definition queue_repr_1 (γs : gstate) ℓ_q (vs : list val) (mfin : option val) : iProp Σ := | ||
| 344 | ∃ (hist : list (loc * val)) (ℓ_head ℓ_tail : loc) (hpos tpos : nat), | ||
| 345 | ("Hhead" ∷ ℓ_q ↦ #ℓ_head) ∗ | ||
| 346 | (* Not immediately clear whether this can be discarded when the queue is | ||
| 347 | finalized and otherwise emptied *) | ||
| 348 | ("Htail" ∷ (ℓ_q +ₗ 1) ↦ #ℓ_tail) ∗ | ||
| 349 | ("Hrepr" ∷ hist_repr hist (bool_decide (is_Some mfin))) ∗ | ||
| 350 | ("Hhist●" ∷ γs.(γ_hist) ↪●ML{queue_γs_dq hist hpos mfin} hist) ∗ | ||
| 351 | ("Hhpos●" ∷ γs.(γ_hpos) ↪●MN{queue_γs_dq hist hpos mfin} hpos) ∗ | ||
| 352 | ("%Hℓhpos" ∷ ⌜loc_at hist hpos = Some ℓ_head⌝) ∗ | ||
| 353 | ("%Hℓtpos" ∷ ⌜loc_at hist tpos = Some ℓ_tail⌝) ∗ | ||
| 354 | ("%Hvs" ∷ ⌜vs ++ option_list mfin = (drop (S hpos) hist).*2⌝). | ||
| 355 | |||
| 356 | Definition queue_fin_1 (γs : gstate) (fin : val) : iProp Σ := | ||
| 357 | ∃ (hist : list (loc * val)) (hpos : nat), | ||
| 358 | ("Hhist" ∷ γs.(γ_hist) ↪●ML□ hist) ∗ | ||
| 359 | ("Hhpos" ∷ γs.(γ_hpos) ↪●MN□ hpos) ∗ | ||
| 360 | ("%Hfin" ∷ ⌜val_at hist (S hpos) = Some fin⌝). | ||
| 361 | |||
| 362 | Definition queue_repr ℓ_q (vs : list val) (mfin : option val) : iProp Σ := | ||
| 363 | ∃ (γs : gstate), meta ℓ_q nroot γs ∗ queue_repr_1 γs ℓ_q vs mfin. | ||
| 364 | |||
| 365 | #[global] Instance queue_repr_timeless ℓ_q vs mfin : Timeless (queue_repr ℓ_q vs mfin) := _. | ||
| 366 | |||
| 367 | Definition queue_fin ℓ_q (fin : val) : iProp Σ := | ||
| 368 | ∃ (γs : gstate), meta ℓ_q nroot γs ∗ queue_fin_1 γs fin. | ||
| 369 | |||
| 370 | #[global] Instance queue_fin_persistent ℓ_q fin : Persistent (queue_fin ℓ_q fin) := _. | ||
| 371 | #[global] Instance queue_fin_timeless ℓ_q fin : Timeless (queue_fin ℓ_q fin) := _. | ||
| 372 | |||
| 373 | Lemma queue_fin_obtain ℓ fin : queue_repr ℓ [] (Some fin) -∗ queue_fin ℓ fin. | ||
| 374 | Proof. | ||
| 375 | iIntros "(%γs & Hγs & @)". simplify_eq/=. | ||
| 376 | assert (Hlen : length (drop (S hpos) hist).*2 = 1) by rewrite -Hvs //. | ||
| 377 | rewrite fmap_drop length_drop length_fmap in Hlen. | ||
| 378 | case_decide; last lia. iFrame. iPureIntro. | ||
| 379 | by rewrite /val_at -[S hpos]Nat.add_0_r -lookup_drop -fmap_drop -Hvs. | ||
| 380 | Qed. | ||
| 381 | |||
| 382 | Lemma queue_fin_agree ℓ vs fin mfin : | ||
| 383 | queue_fin ℓ fin -∗ | ||
| 384 | queue_repr ℓ vs mfin -∗ | ||
| 385 | ⌜vs = []⌝ ∗ ⌜mfin = Some fin⌝. | ||
| 386 | Proof. | ||
| 387 | iIntros "(%γs & Hγs & @) (%γs' & Hγs' & @)". | ||
| 388 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 389 | iDestruct (mono_list_auth_own_agree with "Hhist Hhist●") as %[Hfracs <-]. | ||
| 390 | iDestruct (mono_nat_auth_own_agree with "Hhpos Hhpos●") as %[_ <-]. | ||
| 391 | iPureIntro. | ||
| 392 | |||
| 393 | unfold queue_γs_dq in Hfracs. | ||
| 394 | destruct mfin as [fin'|]; last done. | ||
| 395 | destruct (decide (S (S hpos) = length hist)); last done. | ||
| 396 | |||
| 397 | assert (Hlen : S (length vs) = 1). | ||
| 398 | { rewrite -Nat.add_1_r -(length_app _ [fin']) Hvs length_fmap length_drop -e. lia. } | ||
| 399 | destruct vs; last done. simplify_eq/=. | ||
| 400 | |||
| 401 | by rewrite -Hfin /val_at -[S hpos]Nat.add_0_r -lookup_drop -fmap_drop -Hvs. | ||
| 402 | Qed. | ||
| 403 | |||
| 404 | Lemma hist_weaken {γs} hist hpos mfin : | ||
| 405 | γs.(γ_hist) ↪●ML hist ==∗ γs.(γ_hist) ↪●ML{queue_γs_dq hist hpos mfin} hist. | ||
| 406 | Proof. | ||
| 407 | iIntros "Hhist". | ||
| 408 | destruct (queue_γs_dq_cases hist hpos mfin) as [-> | ->]; first done. | ||
| 409 | by iApply mono_list_auth_own_persist. | ||
| 410 | Qed. | ||
| 411 | |||
| 412 | Lemma hpos_weaken {γs hpos} hist mfin : | ||
| 413 | γs.(γ_hpos) ↪●MN hpos ==∗ γs.(γ_hpos) ↪●MN{queue_γs_dq hist hpos mfin} hpos. | ||
| 414 | Proof. | ||
| 415 | iIntros "Hhist". | ||
| 416 | destruct (queue_γs_dq_cases hist hpos mfin) as [-> | ->]; first done. | ||
| 417 | by iApply mono_nat_own_persist. | ||
| 418 | Qed. | ||
| 419 | |||
| 420 | Variant new_node_next := NonFinalType | FinalType. | ||
| 421 | Definition new_node_next_red (n : new_node_next) := | ||
| 422 | match n with | ||
| 423 | | NonFinalType => Normal None | ||
| 424 | | FinalType => Final | ||
| 425 | end. | ||
| 426 | Instance new_node_next_eq_dec : EqDecision new_node_next. | ||
| 427 | Proof. solve_decision. Defined. | ||
| 428 | |||
| 429 | Lemma new_node_spec data (final : bool) : | ||
| 430 | {{{ True }}} | ||
| 431 | new_node data #final | ||
| 432 | {{{ (ℓ : loc), RET #ℓ; node_repr ℓ data (new_node_next_red (if final then FinalType else NonFinalType)) }}}. | ||
| 433 | Proof. | ||
| 434 | iIntros "%Φ _ HΦ". iUnfold new_node. wp_lam. | ||
| 435 | wp_alloc ℓ_node as "Hℓ_node"; first done. | ||
| 436 | iDestruct (array_cons with "Hℓ_node") as "[Hℓ_node0 Hℓ_node12]". | ||
| 437 | iDestruct (array_cons with "Hℓ_node12") as "[Hℓ_node1 Hℓ_node2]". | ||
| 438 | iDestruct (array_cons with "Hℓ_node2") as "[Hℓ_node2 _]". | ||
| 439 | rewrite Loc.add_assoc. | ||
| 440 | wp_let. do 3 wp_store. | ||
| 441 | iMod (pointsto_persist with "Hℓ_node0") as "Hℓ_node0". | ||
| 442 | iMod (pointsto_persist with "Hℓ_node2") as "Hℓ_node2". | ||
| 443 | iModIntro. iApply "HΦ". destruct final; by iFrame. | ||
| 444 | Qed. | ||
| 445 | |||
| 446 | Lemma new_spec : | ||
| 447 | {{{ True }}} | ||
| 448 | new #() | ||
| 449 | {{{ (ℓ : loc), RET #ℓ; queue_repr ℓ [] None }}}. | ||
| 450 | Proof. | ||
| 451 | iIntros "%Φ _ HΦ". iUnfold new. | ||
| 452 | wp_lam. wp_apply (new_node_spec with "[//]") as (ℓ_node) "Hnode". wp_let. | ||
| 453 | |||
| 454 | set ℓ_head := ℓ_node. set ℓ_tail := ℓ_node. | ||
| 455 | set hpos := 0. set tpos := 0. | ||
| 456 | set hist := [(ℓ_node, #())] : list (loc * val). | ||
| 457 | iAssert (hist_repr hist false) with "[$Hnode //]" as "Hrepr". | ||
| 458 | |||
| 459 | iMod (mono_list_own_alloc hist) as "[%γ_hist [Hhist● _]]". | ||
| 460 | iMod (mono_nat_own_alloc hpos) as "[%γ_hpos [Hhpos● _]]". | ||
| 461 | set γs := {| γ_hist := γ_hist; γ_hpos := γ_hpos |}. | ||
| 462 | |||
| 463 | iApply wp_fupd. iApply wp_allocN; [lia|done|]. | ||
| 464 | iIntros "!> %ℓ [Hℓ [Htok _]]". | ||
| 465 | iDestruct (array_cons with "Hℓ") as "[Hℓ0 Hℓ1]". | ||
| 466 | iDestruct (array_cons with "Hℓ1") as "[Hℓ1 _]". | ||
| 467 | rewrite Loc.add_0. | ||
| 468 | |||
| 469 | iMod (meta_set ⊤ ℓ γs nroot with "Htok") as "Hmeta"; first done. | ||
| 470 | |||
| 471 | iApply "HΦ". iModIntro. iFrame. by iExists tpos. | ||
| 472 | Qed. | ||
| 473 | |||
| 474 | Lemma try_bump_tail_spec hist0 tpos ℓ_old ℓ_new ℓ_q γs : | ||
| 475 | loc_at hist0 tpos = Some ℓ_old → | ||
| 476 | loc_at hist0 (S tpos) = Some ℓ_new → | ||
| 477 | γs.(γ_hist) ↪◯ML hist0 -∗ | ||
| 478 | <<{ ∀∀ vs mfin, queue_repr_1 γs ℓ_q vs mfin }>> | ||
| 479 | CAS #(ℓ_q +ₗ 1) #ℓ_old #ℓ_new @ ∅ | ||
| 480 | <<{ ∃∃ (b : bool), queue_repr_1 γs ℓ_q vs mfin | RET #b }>>. | ||
| 481 | Proof. | ||
| 482 | iIntros "%Hold %Hnew #Hhist◯ %Φ AU". | ||
| 483 | wp_bind (CmpXchg _ _ _)%E. | ||
| 484 | iMod "AU" as "(%vs & %fin & (%hist1 & %ℓ_head & %ℓ_tail & %hpos & %tpos' & @) & [_ Hclose])". | ||
| 485 | iAssert ⌜hist0 `prefix_of` hist1⌝%I as %Hhist01. | ||
| 486 | { by iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist◯") as "[_ ?]". } | ||
| 487 | |||
| 488 | destruct (decide (ℓ_tail = ℓ_old)) as [->|]. | ||
| 489 | - (* The tail has not been updated yet *) | ||
| 490 | wp_cmpxchg_suc. | ||
| 491 | |||
| 492 | iAssert (queue_repr_1 γs ℓ_q vs fin) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 493 | { iPureIntro. | ||
| 494 | repeat split; try done || lia. | ||
| 495 | exists (S tpos). repeat split; try done. | ||
| 496 | by eapply loc_at_prefix. } | ||
| 497 | iMod ("Hclose" with "[$Hq]") as "HΦ". | ||
| 498 | iModIntro. wp_pures. iApply "HΦ". | ||
| 499 | - (* The tail has been updated in the meantime *) | ||
| 500 | wp_cmpxchg_fail. | ||
| 501 | iAssert (queue_repr_1 γs ℓ_q vs fin) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 502 | { iExists tpos'. by iFrameNamed. } | ||
| 503 | iMod ("Hclose" with "[$Hq]") as "HΦ". | ||
| 504 | iModIntro. wp_pures. iApply "HΦ". | ||
| 505 | Qed. | ||
| 506 | |||
| 507 | (* Given a history that is represented, after updating the [next] | ||
| 508 | pointer of the current tail node to a new tail node, we obtain a | ||
| 509 | new (extended) history (with the new tail node appended). *) | ||
| 510 | Lemma hist_repr_snoc hist tpos (ℓ_tail ℓ_new : loc) data next : | ||
| 511 | length hist = S tpos → | ||
| 512 | loc_at hist tpos = Some ℓ_tail → | ||
| 513 | node_repr ℓ_new data (new_node_next_red next) -∗ | ||
| 514 | hist_repr hist false -∗ | ||
| 515 | (ℓ_tail +ₗ 1) ↦ node_next_hl (Normal None) ∗ | ||
| 516 | ((ℓ_tail +ₗ 1) ↦ node_next_hl (Normal (Some ℓ_new)) -∗ hist_repr (hist ++ [(ℓ_new, data)]) (bool_decide (next = FinalType))). | ||
| 517 | Proof. | ||
| 518 | unfold loc_at. | ||
| 519 | iIntros "%Hhistlen %Hloc @ Hhist". | ||
| 520 | rewrite list_lookup_fmap in Hloc. | ||
| 521 | destruct (hist !! tpos) as [[ℓ_tail' v_tail]|] eqn:Htpos; last done. | ||
| 522 | injection Hloc as ->. | ||
| 523 | apply list_elem_of_split_length in Htpos as (hist' & empty & -> & Htpos). | ||
| 524 | rewrite length_app length_cons -Htpos in Hhistlen. | ||
| 525 | assert (empty = []) as ->. { apply nil_length_inv. lia. } | ||
| 526 | clear Hhistlen Htpos. | ||
| 527 | |||
| 528 | iPoseProof (big_sepL_snoc with "Hhist") as "[Hinit Htail]". | ||
| 529 | iNamedSuffix "Htail" "_tail". | ||
| 530 | |||
| 531 | iSimplifyEq. | ||
| 532 | assert (loc_at (hist' ++ [(ℓ_tail, v_tail)]) (S (length hist')) = None) as ->. | ||
| 533 | { apply lookup_ge_None_2. rewrite length_fmap length_app length_cons /=. lia. } | ||
| 534 | iFrame "Hnnext_tail". iIntros "Hnnext_tail". | ||
| 535 | iSimplifyEq. rewrite /hist_repr. | ||
| 536 | iApply big_sepL_snoc. | ||
| 537 | iSplitR "Hndata Hnfinal Hnnext". | ||
| 538 | - iApply big_sepL_snoc. iSplitL "Hinit". | ||
| 539 | + iApply (big_sepL_mono with "Hinit"). | ||
| 540 | iIntros (k [ℓ v] Hk) "Hrepr". | ||
| 541 | unfold next_from. simpl. rewrite [bool_decide (S k = _)]bool_decide_false; last first. | ||
| 542 | { apply lookup_lt_Some in Hk. rewrite !length_app /=. lia. } | ||
| 543 | rewrite andb_false_r /=. | ||
| 544 | assert (loc_at ((hist' ++ [(ℓ_tail, v_tail)]) ++ [(ℓ_new, data)]) (S k) = loc_at (hist' ++ [(ℓ_tail, v_tail)]) (S k)) as ->. | ||
| 545 | { rewrite /loc_at !list_lookup_fmap lookup_app_l // length_app /=. | ||
| 546 | apply lookup_lt_Some in Hk. lia. } | ||
| 547 | done. | ||
| 548 | + iFrame. | ||
| 549 | rewrite /next_from /= [bool_decide (S (length hist') = _)]bool_decide_false; last first. | ||
| 550 | { rewrite !length_app /=. lia. } | ||
| 551 | rewrite andb_false_r /= /loc_at list_lookup_fmap lookup_app_r; last first. | ||
| 552 | { rewrite length_app /=. lia. } | ||
| 553 | rewrite length_app /= Nat.add_1_r Nat.sub_diag /=. | ||
| 554 | by iFrame. | ||
| 555 | - iFrame. | ||
| 556 | rewrite /next_from /= /loc_at length_app /= Nat.add_1_r list_lookup_fmap lookup_app_r. | ||
| 557 | * rewrite !length_app /= !Nat.add_1_r [bool_decide (S _ = S _)]bool_decide_true // | ||
| 558 | andb_true_r Nat.sub_succ -Arith_base.minus_Sn_m_stt // Nat.sub_diag /=. | ||
| 559 | destruct next; simpl; iFrame. | ||
| 560 | * rewrite length_app /= Nat.add_1_r. lia. | ||
| 561 | Qed. | ||
| 562 | |||
| 563 | Definition iffinal {A} (n : new_node_next) (a b : A) := | ||
| 564 | match n with | ||
| 565 | | FinalType => a | ||
| 566 | | NonFinalType => b | ||
| 567 | end. | ||
| 568 | |||
| 569 | Lemma set_tail_spec (ℓ_q ℓ_node : loc) data next : | ||
| 570 | node_repr ℓ_node data (new_node_next_red next) -∗ | ||
| 571 | <<{ ∀∀ vs, queue_repr ℓ_q vs None }>> | ||
| 572 | set_tail #ℓ_q #ℓ_node @ ∅ | ||
| 573 | <<{ queue_repr ℓ_q (vs ++ iffinal next [] [data]) (iffinal next (Some data) None) | RET #() }>>. | ||
| 574 | Proof. | ||
| 575 | iIntros "@ %Φ AU". iLöb as "IH". wp_rec. | ||
| 576 | |||
| 577 | wp_pures. wp_bind (! _)%E. | ||
| 578 | iMod "AU" as "(%vs & (%γs & #Hγs & %hist0 & %ℓ_head & %ℓ_tail & %hpos & %tpos & @) & [Hclose _])". | ||
| 579 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist0◯". | ||
| 580 | wp_load. iSimpl in "Hrepr". | ||
| 581 | rename Hℓtpos into Hℓtpos0. | ||
| 582 | iAssert (queue_repr_1 γs ℓ_q vs None) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 583 | { iExists tpos. by iFrameNamed. } | ||
| 584 | iMod ("Hclose" with "[$]") as "AU". clear Hvs Hℓhpos hpos vs ℓ_head. | ||
| 585 | iModIntro. wp_let. wp_pure. | ||
| 586 | |||
| 587 | wp_bind (! _)%E. | ||
| 588 | iMod "AU" as "(%vs & (%γs' & Hγs' & %hist2 & %ℓ_head & %ℓ_tail'' & %hpos & %tpos'' & @) & [Hclose _])". | ||
| 589 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 590 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist0◯") as %Hhist02. | ||
| 591 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist2◯". | ||
| 592 | destruct Hhist02 as [_ Hhist02]. rename Hℓtpos into Hℓtpos2. | ||
| 593 | assert (Hℓtpos0'' : loc_at hist2 tpos = Some ℓ_tail). | ||
| 594 | { by eapply loc_at_prefix. } clear Hℓtpos0. | ||
| 595 | wp_pures. | ||
| 596 | iDestruct (hist_repr_peek_2 with "[$Hrepr]") as "(%w & Hnrepr & Hrepr)"; first done. | ||
| 597 | iNamedSuffix "Hnrepr" "_tail". | ||
| 598 | wp_load. | ||
| 599 | iDestruct "Hnfinal_tail" as "#Hnfinal_tail2". | ||
| 600 | iPoseProof ("Hrepr" with "[$]") as "Hrepr". | ||
| 601 | iAssert (queue_repr_1 γs ℓ_q vs None) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 602 | { iExists tpos''. by iFrameNamed. } | ||
| 603 | iMod ("Hclose" with "[$]") as "AU". clear Hvs Hℓhpos hpos vs ℓ_head. | ||
| 604 | iModIntro. | ||
| 605 | |||
| 606 | destruct (next_from hist2 false tpos) as [[ℓ_next|]|] eqn:Htpos2; last done. | ||
| 607 | + (* it is not the last node *) | ||
| 608 | simpl. apply next_from_normal in Htpos2 as [Hnext HStpos2]. rewrite HStpos2 /=. | ||
| 609 | |||
| 610 | wp_pures. wp_bind (Snd (CmpXchg _ _ _))%E. | ||
| 611 | awp_apply (try_bump_tail_spec with "[//]"). | ||
| 612 | { apply Hℓtpos0''. } | ||
| 613 | { done. } | ||
| 614 | rewrite /atomic_acc /=. | ||
| 615 | iMod "AU" as "(%vs & (%γs' & Hγs' & Hrepr) & [Hclose _])". | ||
| 616 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 617 | iModIntro. iExists vs. iFrame "Hrepr". iSplit. | ||
| 618 | * iFrame. iIntros "Hrepr". by iApply ("Hclose" with "[$Hrepr]"). | ||
| 619 | * iIntros "%b Hrepr". | ||
| 620 | iMod ("Hclose" with "[$Hrepr //]") as "AU". | ||
| 621 | iModIntro. wp_pures. | ||
| 622 | by iApply ("IH" with "[$] [$] [$]"). | ||
| 623 | + (* it is the last (non-final) node *) | ||
| 624 | simpl. apply next_from_normal in Htpos2 as [Hnext HStpos2]. rewrite HStpos2 /=. | ||
| 625 | wp_pures. wp_bind (CmpXchg _ _ _)%E. | ||
| 626 | |||
| 627 | iMod "AU" as "(%vs & (%γs' & Hγs' & %hist3 & %ℓ_head & %ℓ_tail''' & %hpos & %tpos''' & @) & Hclose)". | ||
| 628 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 629 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist2◯") as %Hhist23. | ||
| 630 | destruct Hhist23 as [_ Hhist23]. rename Hℓtpos into Hℓtpos3. | ||
| 631 | |||
| 632 | destruct (loc_at hist3 (S tpos)) as [ℓ_tail''''|] eqn:Hrace. | ||
| 633 | * (* Another item has been enqueued in the meantime, so the CAS is poised to fail. *) | ||
| 634 | iDestruct "Hclose" as "[Hclose _]". | ||
| 635 | |||
| 636 | iDestruct (hist_repr_peek_3 with "[$Hrepr]") as "[Hℓ_tail1 Hrepr]". | ||
| 637 | { eapply prefix_lookup_Some; first apply Hℓtpos0''. by apply prefix_of_fmap. } | ||
| 638 | rewrite /next_from [bool_decide (S _ = _)]bool_decide_false; last first. | ||
| 639 | { apply lookup_lt_Some in Hrace. rewrite length_fmap in Hrace. lia. } | ||
| 640 | rewrite andb_false_r Hrace /=. | ||
| 641 | wp_cmpxchg_fail. | ||
| 642 | iSpecialize ("Hrepr" with "Hℓ_tail1"). | ||
| 643 | iAssert (queue_repr_1 γs ℓ_q vs None) with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr]" as "Hq". | ||
| 644 | { iExists tpos'''. by iFrameNamed. } | ||
| 645 | iMod ("Hclose" with "[$]") as "AU". iModIntro. | ||
| 646 | wp_pures. iApply ("IH" with "Hndata Hnnext Hnfinal AU"). | ||
| 647 | * (* This node is still the tail, so the CAS will succeed. *) | ||
| 648 | iDestruct "Hclose" as "[_ Hclose]". | ||
| 649 | |||
| 650 | iAssert ⌜length hist3 = S tpos⌝%I as %Hhist3. | ||
| 651 | { apply loc_at_length in Hrace as Hhist3. | ||
| 652 | iPureIntro. apply loc_at_prefix with (xs':=hist3) in Hℓtpos0''; last done. | ||
| 653 | apply lookup_lt_Some in Hℓtpos0''. rewrite length_fmap in Hℓtpos0''. lia. } | ||
| 654 | |||
| 655 | iDestruct (hist_repr_peek_2 with "[$Hrepr]") as "(%w' & Hℓ_tail3 & Hrepr)". | ||
| 656 | { eapply prefix_lookup_Some; first apply Hℓtpos0''. by apply prefix_of_fmap. } | ||
| 657 | rewrite /next_from [bool_decide (S _ = _)]bool_decide_true // andb_true_r Hrace /=. | ||
| 658 | iNamedSuffix "Hℓ_tail3" "_tail3". | ||
| 659 | iPoseProof ("Hrepr" with "[$]") as "Hrepr". clear w. | ||
| 660 | |||
| 661 | iPoseProof (hist_repr_snoc with "[$Hndata $Hnfinal $Hnnext] [$Hrepr]") as "[Htnode Henq]". | ||
| 662 | { apply Hhist3. } | ||
| 663 | { eapply prefix_lookup_Some. apply Hℓtpos0''. by apply prefix_of_fmap. } | ||
| 664 | wp_cmpxchg_suc. iSpecialize ("Henq" with "Htnode"). | ||
| 665 | |||
| 666 | pose mfin := iffinal next (Some data) None. | ||
| 667 | |||
| 668 | iMod (mono_list_auth_own_update_app [(ℓ_node, data)] with "Hhist●") as "[Hhist● _]". | ||
| 669 | iMod (hpos_weaken (hist3 ++ [(ℓ_node, data)]) mfin with "Hhpos●") as "Hhpos●". | ||
| 670 | iMod (hist_weaken (hist3 ++ [(ℓ_node, data)]) hpos mfin with "Hhist●") as "Hhist●". | ||
| 671 | replace (bool_decide (next = FinalType)) with (bool_decide (is_Some mfin)); last first. | ||
| 672 | { apply bool_decide_ext. destruct next; by cbn. } | ||
| 673 | |||
| 674 | iAssert (queue_repr_1 γs ℓ_q (vs ++ iffinal next [] [data]) mfin) with "[$Hhead $Htail $Hhist● $Hhpos● $Henq]" as "Hq". | ||
| 675 | { iExists tpos'''. iPureIntro. | ||
| 676 | repeat split; try done. | ||
| 677 | - eapply loc_at_prefix; first done. | ||
| 678 | by apply prefix_app_r. | ||
| 679 | - eapply loc_at_prefix; first done. | ||
| 680 | by apply prefix_app_r. | ||
| 681 | - rewrite drop_app fmap_app -Hvs. rewrite /= app_nil_r -app_assoc. | ||
| 682 | f_equal. | ||
| 683 | assert (Hhpos : hpos < length hist3). | ||
| 684 | { apply lookup_lt_Some in Hℓhpos. | ||
| 685 | by rewrite length_fmap in Hℓhpos. } | ||
| 686 | assert (S hpos - length hist3 = 0) as -> by lia. | ||
| 687 | simpl. by destruct next. } | ||
| 688 | iMod ("Hclose" with "[$]") as "HΦ". | ||
| 689 | iModIntro. wp_pures. done. | ||
| 690 | Qed. | ||
| 691 | |||
| 692 | Lemma enqueue_spec (ℓ_q : loc) (data : val) : | ||
| 693 | ⊢ <<{ ∀∀ vs, queue_repr ℓ_q vs None }>> | ||
| 694 | enqueue #ℓ_q data @ ∅ | ||
| 695 | <<{ queue_repr ℓ_q (vs ++ [data]) None | RET #() }>>. | ||
| 696 | Proof. | ||
| 697 | iIntros "%Φ AU". wp_lam. wp_pures. | ||
| 698 | wp_apply (new_node_spec with "[//]") as "%ℓ_node Hnode". | ||
| 699 | awp_apply (set_tail_spec with "[$Hnode]"). | ||
| 700 | rewrite /atomic_acc /=. iMod "AU" as "(%vs & Hrepr & Hclose)". | ||
| 701 | iModIntro. iFrame. | ||
| 702 | Qed. | ||
| 703 | |||
| 704 | Lemma finalize_spec (ℓ_q : loc) (data : val) : | ||
| 705 | ⊢ <<{ ∀∀ vs, queue_repr ℓ_q vs None }>> | ||
| 706 | finalize #ℓ_q data @ ∅ | ||
| 707 | <<{ queue_repr ℓ_q vs (Some data) | RET #() }>>. | ||
| 708 | Proof. | ||
| 709 | iIntros "%Φ AU". wp_lam. wp_pures. | ||
| 710 | wp_apply (new_node_spec with "[//]") as "%ℓ_node Hnode". | ||
| 711 | awp_apply (set_tail_spec with "[$Hnode]"). | ||
| 712 | rewrite /atomic_acc /=. iMod "AU" as "(%vs & Hrepr & Hclose)". | ||
| 713 | iModIntro. iFrame. by rewrite app_nil_r. | ||
| 714 | Qed. | ||
| 715 | |||
| 716 | Lemma try_dequeue_spec (ℓ_q : loc) : | ||
| 717 | ⊢ <<{ ∀∀ vs mfin, queue_repr ℓ_q vs mfin }>> | ||
| 718 | try_dequeue #ℓ_q @ ∅ | ||
| 719 | <<{ queue_repr ℓ_q (tail vs) mfin | ||
| 720 | | RET (if head vs is Some v then InjLV v else InjRV (val_opt_hl mfin)) }>>. | ||
| 721 | Proof. | ||
| 722 | iIntros "%Φ AU". iLöb as "IH". wp_rec. | ||
| 723 | |||
| 724 | wp_bind (! _)%E. | ||
| 725 | iMod "AU" as "(%vs0 & %fin0 & (%γs & #Hγs & %hist0 & %ℓ_head0 & %ℓ_tail0 & %hpos0 & %tpos0 & @) & [Hclose _])". | ||
| 726 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist0◯". | ||
| 727 | iDestruct (mono_nat_lb_own_get with "Hhpos●") as "#Hhpos0◯". | ||
| 728 | rename Hℓhpos into Hℓhpos0. | ||
| 729 | wp_load. | ||
| 730 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 731 | { iExists tpos0. by iFrameNamed. } | ||
| 732 | iModIntro. clear fin0 vs0 Hvs tpos0 Hℓtpos ℓ_tail0. | ||
| 733 | |||
| 734 | wp_pures. wp_bind (! _)%E. | ||
| 735 | iMod "AU" as "(%vs1 & %fin1 & (%γs' & Hγs' & %hist1 & %ℓ_head1 & %ℓ_tail1 & %hpos1 & %tpos1 & @) & Hclose)". | ||
| 736 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 737 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist1◯". | ||
| 738 | iDestruct (mono_nat_auth_lb_own_valid with "Hhpos● Hhpos0◯") as %Hhpos01. | ||
| 739 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist0◯") as %Hhist01. | ||
| 740 | destruct Hhpos01 as [_ Hhpos01]. destruct Hhist01 as [_ Hhist01]. | ||
| 741 | assert (Hℓhpos0' : loc_at hist1 hpos0 = Some ℓ_head0). | ||
| 742 | { by eapply loc_at_prefix. } clear Hℓhpos0. | ||
| 743 | iDestruct (hist_repr_peek_2 with "[$Hrepr]") as "(%u & @ & Hrepr)"; first done. | ||
| 744 | wp_load. | ||
| 745 | iSpecialize ("Hrepr" with "[$]"). | ||
| 746 | |||
| 747 | rewrite node_next_hl_next_from. | ||
| 748 | destruct (loc_at hist1 (S hpos0)) as [ℓ_headS0|] eqn:Hℓ_headS0. | ||
| 749 | - simpl. iDestruct "Hclose" as "[Hclose _]". | ||
| 750 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 751 | { iExists tpos1. by iFrameNamed. } | ||
| 752 | iModIntro. clear u fin1 vs1 Hvs tpos1 Hℓtpos ℓ_tail1. | ||
| 753 | |||
| 754 | wp_pures. wp_bind (! _)%E. | ||
| 755 | iMod "AU" as "(%vs2 & %fin2 & (%γs' & Hγs' & %hist2 & %ℓ_head2 & %ℓ_tail2 & %hpos2 & %tpos2 & @) & Hclose)". | ||
| 756 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 757 | iDestruct (mono_list_lb_own_get with "Hhist●") as "#Hhist2◯". | ||
| 758 | iDestruct (mono_nat_auth_lb_own_valid with "Hhpos● Hhpos0◯") as %Hhpos02. | ||
| 759 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist1◯") as %Hhist12. | ||
| 760 | destruct Hhpos02 as [_ Hhpos02]. destruct Hhist12 as [_ Hhist12]. | ||
| 761 | assert (Hℓ_headS0' : loc_at hist2 (S hpos0) = Some ℓ_headS0). | ||
| 762 | { by eapply loc_at_prefix. } clear Hℓ_headS0. | ||
| 763 | apply loc_at_val_at_Some in Hℓ_headS0' as Hval_headS0'. | ||
| 764 | destruct Hval_headS0' as [w Hval_headS0']. | ||
| 765 | iDestruct (hist_repr_peek_1 with "[$Hrepr]") as "(@ & Hrepr)"; try done. | ||
| 766 | iDestruct "Hndata" as "#Hndata_head". | ||
| 767 | wp_load. | ||
| 768 | |||
| 769 | destruct (next_from hist2 (bool_decide (is_Some fin2)) (S hpos0)) as [mℓ_next|] eqn:Hnext2. | ||
| 770 | + iDestruct "Hclose" as "[Hclose _]". | ||
| 771 | iEval (rewrite bool_decide_false //). | ||
| 772 | |||
| 773 | iDestruct "Hnfinal" as "#Hnfinal_head". | ||
| 774 | iEval (rewrite bool_decide_false //) in "Hnfinal_head". | ||
| 775 | |||
| 776 | iSpecialize ("Hrepr" with "[$]"). | ||
| 777 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 778 | { iExists tpos2. by iFrameNamed. } | ||
| 779 | iModIntro. | ||
| 780 | clear fin2 vs2 Hvs tpos2 Hℓtpos ℓ_tail2 Hnext2. | ||
| 781 | |||
| 782 | wp_pures. wp_load. wp_pures. | ||
| 783 | |||
| 784 | wp_bind (CmpXchg _ _ _)%E. | ||
| 785 | iMod "AU" as "(%vs3 & %fin3 & (%γs' & Hγs' & %hist3 & %ℓ_head3 & %ℓ_tail3 & %hpos3 & %tpos3 & @) & Hclose)". | ||
| 786 | iDestruct (meta_agree with "Hγs Hγs'") as "<- {Hγs'}". | ||
| 787 | iDestruct (mono_list_auth_lb_own_valid with "Hhist● Hhist2◯") as %Hhist23. | ||
| 788 | destruct Hhist23 as [_ Hhist23]. | ||
| 789 | assert (Hhist13 : hist1 `prefix_of` hist3). | ||
| 790 | { by trans hist2. } | ||
| 791 | |||
| 792 | destruct (decide (ℓ_head0 = ℓ_head3)) as [->|Hhead03]. | ||
| 793 | * iDestruct "Hclose" as "[_ Hclose]". | ||
| 794 | |||
| 795 | wp_cmpxchg_suc. | ||
| 796 | |||
| 797 | assert (Hℓhpos0'' : loc_at hist3 hpos0 = Some ℓ_head3). | ||
| 798 | { by eapply loc_at_prefix. } clear Hℓhpos0'. | ||
| 799 | iAssert ⌜hpos0 = hpos3⌝%I as %->. | ||
| 800 | { by iApply (loc_at_inj with "Hrepr"). } | ||
| 801 | |||
| 802 | destruct (decide (length (vs3 ++ option_list fin3) = 1)) as [Hvs3|Hvs3]. | ||
| 803 | -- destruct fin3 eqn:Hfin3. | ||
| 804 | ++ assert (Hℓ_headS0'' : loc_at hist3 (S hpos3) = Some ℓ_headS0). | ||
| 805 | { by eapply loc_at_prefix. } clear Hℓ_headS0'. | ||
| 806 | assert (Hval_headS0'' : val_at hist3 (S hpos3) = Some w). | ||
| 807 | { by eapply val_at_prefix. } clear Hval_headS0'. | ||
| 808 | |||
| 809 | simplify_eq/=. | ||
| 810 | destruct vs3; last first. | ||
| 811 | { rewrite length_app /= Nat.add_1_r in Hvs3. lia. } | ||
| 812 | |||
| 813 | assert (length hist3 = S (S hpos3)). | ||
| 814 | { apply (f_equal length), symmetry in Hvs. | ||
| 815 | rewrite /= length_fmap length_drop in Hvs. lia. } | ||
| 816 | |||
| 817 | iDestruct (hist_repr_peek_1 with "[$Hrepr]") as "(@ & Hrepr)"; try done. | ||
| 818 | iClear "Hnnext". | ||
| 819 | rewrite /next_from !andb_true_l [bool_decide (S _ = _)]bool_decide_true //=. | ||
| 820 | by iCombine "Hnfinal_head Hnfinal" gives "[_ %contra]". | ||
| 821 | ++ simplify_eq/=. rewrite app_nil_r in Hvs Hvs3. | ||
| 822 | |||
| 823 | iMod (mono_nat_own_update (S hpos3) with "Hhpos●") as "[Hhpos● _]". { lia. } | ||
| 824 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "HΦ". | ||
| 825 | { iExists tpos3. iFrameNamed. | ||
| 826 | iPureIntro. repeat split; try done. | ||
| 827 | - by eapply loc_at_prefix. | ||
| 828 | - by rewrite /= fmap_drop /= -Nat.add_1_r -drop_drop -fmap_drop -Hvs app_nil_r. } | ||
| 829 | |||
| 830 | iModIntro. wp_pures. | ||
| 831 | |||
| 832 | assert (Hval_headS0'' : val_at hist3 (S hpos3) = Some w). | ||
| 833 | { by eapply val_at_prefix. } clear Hval_headS0'. | ||
| 834 | unfold val_at in Hval_headS0''. | ||
| 835 | destruct vs3; try done. | ||
| 836 | rewrite -[S hpos3]Nat.add_0_r -lookup_drop -fmap_drop -Hvs /= in Hval_headS0''. | ||
| 837 | by simplify_eq/=. | ||
| 838 | -- assert (S (S hpos3) ≠ length hist3). | ||
| 839 | { rewrite Hvs length_fmap length_drop in Hvs3. lia. } | ||
| 840 | |||
| 841 | assert (queue_γs_dq hist3 hpos3 fin3 = DfracOwn 1) as ->. | ||
| 842 | { unfold queue_γs_dq. by repeat case_match. } | ||
| 843 | iMod (mono_nat_own_update (S hpos3) with "Hhpos●") as "[Hhpos● _]". { lia. } | ||
| 844 | iMod (hpos_weaken hist3 fin3 with "Hhpos●") as "Hhpos●". | ||
| 845 | iMod (hist_weaken hist3 (S hpos3) fin3 with "Hhist●") as "Hhist●". | ||
| 846 | |||
| 847 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "HΦ". | ||
| 848 | { iExists tpos3. iFrameNamed. | ||
| 849 | iPureIntro. repeat split; try done. | ||
| 850 | - by eapply loc_at_prefix. | ||
| 851 | - rewrite -Nat.add_1_r -drop_drop fmap_drop -Hvs. | ||
| 852 | destruct fin3; last by rewrite /= !app_nil_r. | ||
| 853 | by destruct vs3. } | ||
| 854 | |||
| 855 | iModIntro. wp_pures. | ||
| 856 | |||
| 857 | assert (Hval_headS0'' : val_at hist3 (S hpos3) = Some w). | ||
| 858 | { by eapply val_at_prefix. } clear Hval_headS0'. | ||
| 859 | unfold val_at in Hval_headS0''. | ||
| 860 | rewrite -[S hpos3]Nat.add_0_r -lookup_drop -fmap_drop -Hvs in Hval_headS0''. | ||
| 861 | |||
| 862 | destruct vs3; simplify_eq/=; last done. | ||
| 863 | by destruct fin3. | ||
| 864 | |||
| 865 | * iDestruct "Hclose" as "[Hclose _]". | ||
| 866 | |||
| 867 | wp_cmpxchg_fail. | ||
| 868 | |||
| 869 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "AU". | ||
| 870 | { iExists tpos3. by iFrameNamed. } | ||
| 871 | |||
| 872 | iModIntro. wp_pures. iApply ("IH" with "AU"). | ||
| 873 | |||
| 874 | + iDestruct "Hclose" as "[_ Hclose]". | ||
| 875 | |||
| 876 | destruct fin2 as [fin2|]; last done. simplify_eq/=. | ||
| 877 | assert (Hhist2 : length hist2 = S (S hpos0)). | ||
| 878 | { destruct (decide (length hist2 = S (S hpos0))); first done. | ||
| 879 | by rewrite /next_from bool_decide_false in Hnext2. } | ||
| 880 | assert (S hpos2 < length hist2). | ||
| 881 | { rewrite -(length_fmap snd). | ||
| 882 | apply lookup_lt_is_Some_1. | ||
| 883 | rewrite -[S hpos2]Nat.add_0_r -lookup_drop -fmap_drop -Hvs. | ||
| 884 | by destruct vs2, fin2. } | ||
| 885 | assert (hpos0 = hpos2) as -> by lia. | ||
| 886 | assert (Hvs2 : length vs2 = 0). | ||
| 887 | { apply eq_add_S. rewrite -Nat.add_1_r -(length_app _ [fin2]) Hvs length_fmap length_drop Hhist2. lia. } | ||
| 888 | destruct vs2; last done. clear Hvs2. | ||
| 889 | |||
| 890 | iSpecialize ("Hrepr" with "[$]"). | ||
| 891 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "HΦ". | ||
| 892 | { iExists tpos2. by iFrameNamed. } | ||
| 893 | |||
| 894 | iModIntro. clear tpos2 Hℓhpos Hℓtpos ℓ_tail2. | ||
| 895 | |||
| 896 | wp_pures. wp_load. | ||
| 897 | rewrite /val_at in Hval_headS0'. | ||
| 898 | rewrite -[S hpos2]Nat.add_0_r -lookup_drop -fmap_drop -Hvs in Hval_headS0'. | ||
| 899 | simplify_eq/=. wp_pures. iApply "HΦ". | ||
| 900 | - apply loc_at_length in Hℓ_headS0 as Hhistlen. | ||
| 901 | rewrite drop_ge /= in Hvs; last lia. destruct vs1, fin1; try done. | ||
| 902 | simplify_eq/=. | ||
| 903 | |||
| 904 | iDestruct "Hclose" as "[_ Hclose]". | ||
| 905 | iMod ("Hclose" with "[$Hhead $Htail $Hhist● $Hhpos● $Hrepr $Hγs]") as "HΦ". | ||
| 906 | { iExists tpos1. iFrameNamed. iPureIntro. | ||
| 907 | rewrite drop_ge //=. lia. } | ||
| 908 | |||
| 909 | iModIntro. wp_pures. iApply "HΦ". | ||
| 910 | Qed. | ||
| 911 | |||
| 912 | End fin_queue. | ||
| 913 | |||
| 914 | Definition fin_queue_hl := | ||
| 915 | {| fin_queue_spec.new := new; | ||
| 916 | fin_queue_spec.enqueue := enqueue; | ||
| 917 | fin_queue_spec.finalize := finalize; | ||
| 918 | fin_queue_spec.try_dequeue := try_dequeue; |}. | ||
| 919 | |||
| 920 | Definition fin_queue_iris `{!heapGS Σ, !fin_queueG Σ} : fin_queue_spec.fin_queue_iris Σ fin_queue_hl. | ||
| 921 | Proof. | ||
| 922 | refine (FinQueueIris _ _ | ||
| 923 | fin_queue_hl _ _ _ _ _ | ||
| 924 | queue_fin_obtain | ||
| 925 | queue_fin_agree | ||
| 926 | new_spec | ||
| 927 | enqueue_spec | ||
| 928 | finalize_spec | ||
| 929 | try_dequeue_spec). | ||
| 930 | Defined. | ||
| 931 | |||
| 932 | #[global] Typeclasses Opaque queue_repr queue_fin. | ||