Skip to content
Snippets Groups Projects
multi_channel.v 12 KiB
Newer Older
(** This file contains the definition of the channels, encoded as a pair of
lock-protected buffers, and their primitive proof rules. Moreover:

- It defines the connective [c ↣ prot] for ownership of channel endpoints,
  which describes that channel endpoint [c] adheres to protocol [prot].
- It proves Actris's specifications of [send] and [recv] w.r.t. dependent
  separation protocols.

An encoding of the usual (binary) choice connectives [prot1 <{Q1}+{Q2}> prot2]
and [prot1 <{Q1}&{Q2}> prot2], inspired by session types, is also included in
this file.

In this file we define the three message-passing connectives:

- [new_chan] creates references to two empty buffers and a lock, and returns a
  pair of endpoints, where the order of the two references determines the
  polarity of the endpoints.
- [send] takes an endpoint and adds an element to the first buffer.
- [recv] performs a busy loop until there is something in the second buffer,
  which it pops and returns, locking during each peek.

It is additionaly shown that the channel ownership [c ↣ prot] is closed under
the subprotocol relation [⊑] *)
From iris.algebra Require Import gmap excl_auth gmap_view.
From iris.heap_lang Require Export primitive_laws notation.
From iris.heap_lang Require Export proofmode.
From iris.heap_lang.lib Require Import spin_lock.
From actris.utils Require Export llist.
From actris.channel Require Import multi_proto_model.
From actris.channel Require Export multi_proto.
Set Default Proof Using "Type".

(* TODO: Update new_chan definition to use pointers with offsets *)
(** * The definition of the message-passing connectives *)
Definition new_chan : val :=
  λ: "n",
    let: "l" := AllocN ("n"*"n") NONEV in
    let: "xxs" := lnil #() in
    (rec: "go1" "i" := if: "i" = "n" then #() else
       let: "xs" := lnil #() in
       (rec: "go2" "j" := if: "j" = "n" then #() else
          lcons ("l" + ("i"*"n"+"j"), "l" + ("j"*"n"+"i")) "xs";;
          "go2" ("j"+#1)) #0;;
       lcons "xs" "xxs";;
       "go1" ("i"+#1)) #0;; "xxs".

Definition wait : val :=
  rec: "go" "c" :=
    match: !"c" with
      NONE => #()
    | SOME <> => "go" "c"
    end.

Definition send : val :=
  λ: "c" "i" "v",
    let: "len" := Fst "c" in
    if: "i" < "len" then
      let: "l" := Fst (! ((Snd "c") + "i")) in
      "l" <- SOME "v";; wait "l"
    (* OBS: Hacky *)
    else (rec: "go" <> := "go" #())%V #().

Definition recv : val :=
  rec: "go" "c" "i" :=
    let: "len" := Fst "c" in
    if: "i" < "len" then
      let: "l" := Snd (! ((Snd "c") + "i")) in
      let: "v" := Xchg "l" NONEV in
      match: "v" with
        NONE => "go" "c" "i"
      | SOME "v" => "v"
      end
    (* OBS: Hacky *)
    else (rec: "go" <> := "go" #())%V #().

(** * Setup of Iris's cameras *)
Class proto_exclG Σ V :=
  protoG_exclG ::
    inG Σ (excl_authR (laterO (proto (leibnizO V) (iPropO Σ) (iPropO Σ)))).

Definition proto_exclΣ V := #[
  GFunctor (authRF (optionURF (exclRF (laterOF (protoOF (leibnizO V) idOF idOF)))))
].

Class chanG Σ := {
  chanG_proto_exclG :: proto_exclG Σ val;
  chanG_tokG :: inG Σ (exclR unitO);
  chanG_protoG :: protoG Σ val;
}.
Definition chanΣ : gFunctors := #[ proto_exclΣ val; protoΣ val; GFunctor (exclR unitO) ].
Global Instance subG_chanΣ {Σ} : subG chanΣ Σ  chanG Σ.
Proof. solve_inG. Qed.

(** * Definition of the pointsto connective *)
Notation iProto Σ := (iProto Σ val).
Notation iMsg Σ := (iMsg Σ val).

Definition tok `{!chanG Σ} (γ : gname) : iProp Σ := own γ (Excl ()).

Definition chan_inv `{!heapGS Σ, !chanG Σ} γ γE γt i j (l:loc) : iProp Σ :=
  (l  NONEV  tok γt) 
  ( v m, l  SOMEV v 
            iProto_own γ i (<Send j> m)%proto 
            ( p, iMsg_car m v (Next p)  own γE (E (Next p)))) 
  ( p, l  NONEV 
          iProto_own γ i p  own γE (E (Next p))).

Definition iProto_pointsto_def `{!heapGS Σ, !chanG Σ}
    (c : val) (i:nat) (p : iProto Σ) : iProp Σ :=
   γ γE1 γE2 γt1 γt2 (l:loc) ls,
     c = PairV #(length ls) #l  
    inv (nroot.@"ctx") (iProto_ctx γ) 
    l ↦∗ ls 
    ([list] j  v  ls, 
        (l1 l2 : loc),
         v = PairV #l1 #l2 
         inv (nroot.@"p") (chan_inv γ γE1 γt1 i j l1) 
         inv (nroot.@"p") (chan_inv γ γE2 γt2 j i l2)) 
    (* llist (λ l v, *)
    (*          ∃ (j:nat) (l1 l2 : loc), *)
    (*            ⌜l = (j,(l1,l2))⌝ ∗ ⌜v = PairV #l1 #l2⌝ ∗ *)
    (*            inv (nroot.@"p") (chan_inv γ γE1 γt1 i j l1) ∗ *)
    (*            inv (nroot.@"p") (chan_inv γ γE2 γt2 j i l2)) l *)
    (*       (zip (seq 0 (length ls)) ls) ∗ *)
    own γE1 (E (Next p))  own γE1 (E (Next p)) 
    iProto_own γ i p.
Definition iProto_pointsto_aux : seal (@iProto_pointsto_def). by eexists. Qed.
Definition iProto_pointsto := iProto_pointsto_aux.(unseal).
Definition iProto_pointsto_eq :
  @iProto_pointsto = @iProto_pointsto_def := iProto_pointsto_aux.(seal_eq).
Arguments iProto_pointsto {_ _ _} _ _ _%proto.
Global Instance: Params (@iProto_pointsto) 4 := {}.
Notation "c ↣{ i } p" := (iProto_pointsto c i p)
  (at level 20, format "c  ↣{  i  }  p").

Section channel.
  Context `{!heapGS Σ, !chanG Σ}.
  Implicit Types p : iProto Σ.
  Implicit Types TT : tele.

  Global Instance iProto_pointsto_ne c i : NonExpansive (iProto_pointsto c i).
  Proof. rewrite iProto_pointsto_eq. solve_proper. Qed.
  Global Instance iProto_pointsto_proper c i : Proper (() ==> ()) (iProto_pointsto c i).
  Proof. apply (ne_proper _). Qed.

  (* Lemma iProto_pointsto_le c p1 p2 : c ↣ p1 ⊢ ▷ (p1 ⊑ p2) -∗ c ↣ p2. *)
  (* Proof. *)
  (*   rewrite iProto_pointsto_eq. iDestruct 1 as (γ s l r lk ->) "[Hlk H]". *)
  (*   iIntros "Hle'". iExists γ, s, l, r, lk. iSplit; [done|]. iFrame "Hlk". *)
  (*   by iApply (iProto_own_le with "H"). *)
  (* Qed. *)

  (** ** Specifications of [send] and [recv] *)
  Lemma new_chan_spec (n:nat) (ps:gmap nat (iProto Σ)) :
    ( i, i < n  is_Some (ps !! i)) 
    {{{ iProto_consistent ps }}}
      new_chan #n
    {{{ cs ls, RET #cs;
        length ls = n  cs ↦∗ ls 
        [list] i  l  ls, l {i} (ps !!! i) }}}.
  Proof. Admitted.

  Lemma own_prot_excl γ i (p1 p2 : iProto Σ) :
    own γ (gmap_view_frag i (DfracOwn 1) (Excl' (Next p1))) -∗
    own γ (gmap_view_frag i (DfracOwn 1) (Excl' (Next p2))) -∗
    False.
  Proof. Admitted.

  Lemma send_spec c i j v p :
    {{{ c {i} <Send j> MSG v; p }}}
      send c #j v
    {{{ RET #(); c {i} p }}}.
  Proof.
    rewrite iProto_pointsto_eq. iIntros (Φ) "Hc HΦ". wp_lam; wp_pures.
    iDestruct "Hc" as
      (γ γE1 γE2 γt1 γt2 l ls ->) "(#IH & Hl & Hls & H● & H◯ & Hown)".
    wp_pures.
    case_bool_decide; last first.
    { wp_pures. iClear "IH H● H◯ Hown HΦ Hls Hl".
      iLöb as "IH". wp_lam. iApply "IH". }
    (* assert (is_Some ((zip (seq 0 (length ls)) ls) !! j)) as [l' HSome]. *)
    (* { apply lookup_lt_is_Some_2. lia. } *)
    (* wp_smart_apply (llookup_spec with "Hls"); [done|]. *)
    assert (is_Some (ls !! j)) as [l' HSome].
    { apply lookup_lt_is_Some_2. lia. }
    wp_pures.
    wp_smart_apply (wp_load_offset with "Hl").
    { done. } 
    iIntros "Hl". wp_pures.
    iDestruct (big_sepL_lookup_acc with "Hls") as "[Hj Hls]"; [done|].
    iDestruct "Hj" as (l1 l2 ->) "#[IHl1 IHl2]". 
    iDestruct ("Hls" with "[]") as "Hls".
    { iExists _, _. iFrame "#". done. }
    wp_pures.
    wp_bind (Store _ _).
    iInv "IHl1" as "HIp".
    iDestruct "HIp" as "[HIp|HIp]"; last first.
    { iDestruct "HIp" as "[HIp|HIp]".
      - iDestruct "HIp" as (? m) "(>Hl' & Hown' & HIp)".
        wp_store.
        rewrite /iProto_own.
        iDestruct (own_prot_excl with "Hown Hown'") as "H". done.
      - iDestruct "HIp" as (p') "(>Hl' & Hown' & HIp)".
        wp_store.
        rewrite /iProto_own.
        iDestruct (own_prot_excl with "Hown Hown'") as "H". done. }
    iDestruct "HIp" as "[>Hl' Htok]".
    wp_store.
    iMod (own_update_2 with "H● H◯") as "[H● H◯]".
    { apply excl_auth_update. }
    iModIntro.
    iSplitL "Hl' H● Hown". 
    { iRight. iLeft. iIntros "!>". iExists _, _. iFrame.
      iExists _. iFrame. rewrite iMsg_base_eq. simpl. done. }
    wp_pures.
    iLöb as "HL".
    wp_lam.
    wp_bind (Load _).
    iInv "IHl1" as "HIp".
    iDestruct "HIp" as "[HIp|HIp]".
    { iDestruct "HIp" as ">[Hl' Htok']".
      iDestruct (own_valid_2 with "Htok Htok'") as %[]. }
    iDestruct "HIp" as "[HIp|HIp]".
    - iDestruct "HIp" as (? m) "(>Hl' & Hown & HIp)".
      wp_load. iModIntro.
      iSplitL "Hl' Hown HIp".
      { iRight. iLeft. iExists _, _. iFrame. }
      wp_pures. iApply ("HL" with "HΦ Hl Hls Htok H◯").
    - iDestruct "HIp" as (p') "(>Hl' & Hown & H●)".
      wp_load.
      iModIntro.
      iSplitL "Hl' Htok".
      { iLeft. iFrame. }
      iDestruct (own_valid_2 with "H● H◯") as "#Hagree".
      iDestruct (excl_auth_agreeI with "Hagree") as "Hagree'".
      wp_pures.
      iMod (own_update_2 with "H● H◯") as "[H● H◯]".
      { apply excl_auth_update. }
      iModIntro.
      iApply "HΦ".
      iExists _, _, _, _, _, _, _.
      iSplit; [done|]. iFrame "#∗".
      iRewrite -"Hagree'". done.
  Qed.
  
  (* Lemma send_spec_tele {TT} c (tt : TT) *)
  (*       (v : TT → val) (P : TT → iProp Σ) (p : TT → iProto Σ) : *)
  (*   {{{ c ↣ (<!.. x > MSG v x {{ P x }}; p x) ∗ P tt }}} *)
  (*     send c (v tt) *)
  (*   {{{ RET #(); c ↣ (p tt) }}}. *)
  (* Proof. *)
  (*   iIntros (Φ) "[Hc HP] HΦ". *)
  (*   iDestruct (iProto_pointsto_le _ _ (<!> MSG v tt; p tt)%proto with "Hc [HP]") *)
  (*     as "Hc". *)
  (*   { iIntros "!>". *)
  (*     iApply iProto_le_trans. *)
  (*     iApply iProto_le_texist_intro_l. *)
  (*     by iFrame "HP". } *)
  (*   by iApply (send_spec with "Hc"). *)
  (* Qed. *)

  Lemma recv_spec {TT} c i j (v : TT  val) (P : TT  iProp Σ) (p : TT  iProto Σ) :
    {{{ c {i} <(Recv j) @.. x> MSG v x {{  P x }}; p x }}}
      recv c #j
    {{{ x, RET v x; c {i} p x  P x }}}.
  Proof.
    iIntros (Φ) "Hc HΦ". iLöb as "HL". wp_lam.
    rewrite iProto_pointsto_eq.
    iDestruct "Hc" as
      (γ γE1 γE2 γt1 γt2 l ls ->) "(#IH & Hl & Hls & H● & H◯ & Hown)".
    wp_pures.
    case_bool_decide; last first.
    { wp_pures. iClear "IH H● H◯ Hown HΦ Hls Hl".
      iLöb as "IH". wp_lam. iApply "IH". }
    wp_pures.
    assert (is_Some (ls !! j)) as [l' HSome].
    { apply lookup_lt_is_Some_2. lia. }
    wp_smart_apply (wp_load_offset with "Hl").
    { done. } 
    iIntros "Hl". wp_pures.
    iDestruct (big_sepL_lookup_acc with "Hls") as "[Hj Hls]"; [done|].
    iDestruct "Hj" as (l1 l2 ->) "#[IHl1 IHl2]". 
    iDestruct ("Hls" with "[]") as "Hls".
    { iExists _, _. iFrame "#". done. }    
    wp_pures.
    wp_bind (Xchg _ _).
    iInv "IHl2" as "HIp".
    iDestruct "HIp" as "[HIp|HIp]".
    { iDestruct "HIp" as ">[Hl' Htok]".
      wp_xchg.
      iModIntro.
      iSplitL "Hl' Htok".
      { iLeft. iFrame. }
      wp_pures. iApply ("HL" with "[H● H◯ Hown Hls Hl] HΦ").
      iExists _, _, _, _, _, _, _. iSplit; [done|]. iFrame "#∗". }
    iDestruct "HIp" as "[HIp|HIp]"; last first.
    { iDestruct "HIp" as (p') "[>Hl' [Hown' H◯']]".
      wp_xchg.
      iModIntro.
      iSplitL "Hl' Hown' H◯'".
      { iRight. iRight. iExists _. iFrame. }
      wp_pures. iApply ("HL" with "[H● H◯ Hown Hls Hl] HΦ").
      iExists _, _, _, _, _, _, _. iSplit; [done|]. iFrame "#∗". }
    iDestruct "HIp" as (w m) "(>Hl' & Hown' & HIp)".
    iDestruct "HIp" as (p') "[Hm Hp']".
    iInv "IH" as "Hctx".
    wp_xchg.
    iMod (iProto_step with "Hctx Hown' Hown Hm") as
      (p'') "(Hm & Hctx & Hown & Hown')".
    iModIntro.
    iSplitL "Hctx"; [done|].
    iModIntro.
    iSplitL "Hl' Hown Hp'".
    { iRight. iRight. iExists _. iFrame. }
    wp_pure _.
    rewrite iMsg_base_eq.
    iDestruct (iMsg_texist_exist with "Hm") as (x <-) "[Hp HP]".
    wp_pures.
    iMod (own_update_2 with "H● H◯") as "[H● H◯]".
    { apply excl_auth_update. }
    iModIntro. iApply "HΦ".
    iFrame.
    iExists _, _, _, _, _, _, _. iSplit; [done|].
    iRewrite "Hp". iFrame "#∗".
  Qed.

End channel.