From iris.base_logic.lib Require Export invariants.
From iris.heap_lang Require Import lang proofmode notation.

Definition prog : expr :=
  let: "l" := ref #0 in
  Fork ("l" <- #1);;
  !"l".

(**
  This program will race to either update [l] or read [l], meaning the
  resulting value could be either [0] or [1].
*)

Section proofs.
Context `{!heapGS Σ}.
Let N := nroot .@ "prog".

(**
  So what should the invariant be? The resource of interest is what the
  location [l] points to, and throughout the program, it points to
  either [0] or [1]. As such, we will create an invariant which captures
  that [l] points to either [0] or [1].
*)
Definition prog_inv (l : loc) : iProp Σ :=
  ∃ v, l ↦ v ∗ (⌜v = #0⌝ ∨ ⌜v = #1⌝).

(* exercise: wp_prog (2 stars) *)
(** Complete the proof of wp_prog. **)
Lemma wp_prog : {{{ True }}} prog {{{ v, RET v; ⌜v = #0⌝ ∨ ⌜v = #1⌝ }}}.
Proof.
  iIntros (Φ) "_ HΦ".
  rewrite /prog.
  wp_alloc l as "Hl".
  wp_pures.
  (** We now allocate our [prog_inv] invariant using [inv_alloc]. *)
  iMod (inv_alloc N _ (prog_inv l) with "[Hl]") as "#Hinv".
  (** We prove that the invariant is currently true. *)
  {
    iNext.
    iExists #0.
    iFrame.
    by iLeft.
  }
  (**
    With the invariant allocated and in the persistent context, we can
    use it to prove both threads.
  *)
  wp_apply wp_fork.
  - (**
      As [#l <- #1] is atomic and the mask on the WP is [⊤], we can open the invariant.
    *)
    iInv "Hinv" as "(%v & Hl & _)".
    (** We use the obtained points-to predicate to prove the WP. *)
    wp_store.
    (** We close the invariant... *)
    iSplitL.
    {
      iIntros "!> !>".
      iExists #1.
      iFrame.
      by iRight.
    }
    (** ... and finish the proof of the forked thread. *)
    done.
  - (* exercise *)
Admitted.

(* exercise: prog1_inv (2 stars) *)
Definition prog1 : expr :=
  let: "l" := ref #0 in
  Fork ("l" <- #2);;
  Fork ("l" <- #4);;
  "l" <- #6;;
  !"l".

(** Define an invariant that is true throughout prog1. **)
(* Definition prog1_inv (l : loc) : iProp Σ := ... *)

(* exercise: prog1_spec (3 stars) *)
(** Based on your prog1_inv, state and prove a specification for prog1. **)

End proofs.
