Timings for recovery_proof.v
- /home/gitlab-runner/builds/gGKko-aj/0/coq/coq/_bench/opam.OLD/ocaml-OLD/.opam-switch/build/coq-perennial.dev/./src/program_proof/wal/recovery_proof.v.timing
- /home/gitlab-runner/builds/gGKko-aj/0/coq/coq/_bench/opam.NEW/ocaml-NEW/.opam-switch/build/coq-perennial.dev/./src/program_proof/wal/recovery_proof.v.timing
From RecordUpdate Require Import RecordUpdate.
From Perennial.Helpers Require Import range_set.
From Perennial.program_proof Require Import disk_lib.
From Perennial.program_proof Require Import wal.invariant.
From Perennial.program_proof Require Import wal.circ_proof_crash.
From Perennial.goose_lang Require Import crash_modality.
From Perennial.goose_lang Require recovery_lifting.
From Perennial.program_proof Require Import wal.logger_proof.
From Perennial.program_proof Require Import wal.installer_proof.
Implicit Types (v:val) (z:Z).
Implicit Types (γ: wal_names).
Implicit Types (s: log_state.t) (memLog: slidingM.t) (txns: list (u64 * list update.t)).
Implicit Types (pos: u64) (txn_id: nat).
Context (P: log_state.t -> iProp Σ).
Let circN := walN .@ "circ".
Definition wal_init_ghost_state (γnew: wal_names) : iProp Σ :=
"installer_pos" ∷ ghost_var γnew.(installer_pos_name) 1 0%nat ∗
"installer_txn_id" ∷ ghost_var γnew.(installer_txn_id_name) 1 0%nat ∗
"installer_pos_mem" ∷ ghost_var γnew.(installer_pos_mem_name) 1 (U64 0) ∗
"installer_txn_id_mem" ∷ ghost_var γnew.(installer_txn_id_mem_name) 1 0%nat ∗
"logger_pos" ∷ ghost_var γnew.(logger_pos_name) 1 (U64 0) ∗
"logger_txn_id" ∷ ghost_var γnew.(logger_txn_id_name) 1 0%nat ∗
"installed_pos_mem" ∷ ghost_var γnew.(installed_pos_mem_name) 1 (U64 0) ∗
"installed_txn_id_mem" ∷ ghost_var γnew.(installed_txn_id_mem_name) 1 0%nat ∗
"diskEnd" ∷ ghost_var γnew.(diskEnd_name) 1 0%nat ∗
"diskEnd_txn_id" ∷ ghost_var γnew.(diskEnd_txn_id_name) 1 0%nat ∗
"diskEnd_mem" ∷ mono_nat_auth_own γnew.(diskEnd_mem_name) 1 0%nat ∗
"diskEnd_mem_txn_id" ∷ mono_nat_auth_own γnew.(diskEnd_mem_txn_id_name) 1 0%nat ∗
"being_installed_start_txn" ∷ mono_nat_auth_own γnew.(being_installed_start_txn_name) 1 0%nat ∗
"already_installed" ∷ ghost_var γnew.(already_installed_name) 1 ([] : list update.t) ∗
"stable_txn_ids" ∷ map_ctx γnew.(stable_txn_ids_name) 1 (∅ : gmap nat unit) ∗
"txns_ctx" ∷ txns_ctx γnew [] ∗
"start_avail" ∷ thread_own γnew.(start_avail_name) Available ∗
"start_avail_ctx" ∷ thread_own_ctx γnew.(start_avail_name) True ∗
"diskEnd_avail" ∷ thread_own γnew.(diskEnd_avail_name) Available ∗
"diskEnd_avail_ctx" ∷ thread_own_ctx γnew.(diskEnd_avail_name) True ∗
"cs" ∷ ghost_var γnew.(cs_name) 1 (inhabitant : circΣ.t) ∗
"txns" ∷ ghost_var γnew.(txns_name) 1 ([] : list (u64 * list update.t))
.
Definition is_wal_inner_crash (γold: wal_names) s' : iProp Σ := True.
Definition wal_ghost_exchange (γold γnew: wal_names) : iProp Σ := True.
(* This is produced by recovery as a post condition, can be used to get is_wal *)
Definition is_wal_inv_pre (l: loc) γ s (dinit : disk) : iProp Σ :=
is_wal_inner l γ s dinit ∗ (∃ cs, is_circular_state γ.(circ_name) cs ∗ circular_pred γ cs).
Existing Instance own_into_crash.
Definition log_crash_to σ diskEnd_txn_id :=
set log_state.durable_lb (λ _, diskEnd_txn_id)
(set log_state.txns (take (S diskEnd_txn_id)) σ).
Lemma crash_to_diskEnd γ cs σ diskEnd_txn_id installed_txn_id installer_txn_id :
is_durable_txn (Σ:=Σ) γ cs σ.(log_state.txns) diskEnd_txn_id σ.(log_state.durable_lb) -∗
is_durable γ cs σ.(log_state.txns) installed_txn_id installer_txn_id diskEnd_txn_id -∗
⌜relation.denote log_crash σ
(log_crash_to σ (σ.(log_state.durable_lb) `max` diskEnd_txn_id))%nat
tt⌝.
eexists _ (σ.(log_state.durable_lb) `max` diskEnd_txn_id)%nat;
simpl; monad_simpl.
apply is_txn_bound in Hend_txn.
Lemma concat_mono {A: Type} (l1 l2: list (list A)):
incl l1 l2 →
incl (concat l1) (concat l2).
Lemma take_incl {A} (l: list A) n:
incl (take n l) l.
rewrite -{2}(firstn_skipn n l) in_app_iff.
Lemma fmap_incl {A B} (f: A → B) (l l': list A):
incl l l' →
incl (fmap f l) (fmap f l').
rewrite -?elem_of_list_In.
intros (?&?&Hin')%elem_of_list_fmap.
rewrite ?elem_of_list_In.
Lemma log_crash_to_wf σ σ' x :
wal_wf σ →
relation.denote log_crash σ σ' x →
wal_wf σ'.
intros Hwf Htrans; monad_inv.
destruct Hwf as (Haddrs&Hmono&Hb1&hb2).
rewrite /log_state.updates; simpl.
eapply incl_Forall; eauto.
apply concat_mono, fmap_incl, take_incl.
rewrite -{1}(firstn_skipn (S crash_txn) (σ.(log_state.txns))).
rewrite fmap_app list_mono_app; naive_solver.
Lemma log_crash_to_post_crash σ σ' x :
relation.denote log_crash σ σ' x →
wal_post_crash σ'.
intros Htrans; monad_inv.
rewrite /wal_post_crash //=.
Lemma is_txn_implies_non_empty_txns γ cs txns installed_txn_id:
is_txn txns installed_txn_id (start cs) →
txns ≠ [].
apply elem_of_list_lookup_2 in Hlookup.
(* XXX: I think this suggests that we're going to have to require the initial state
to have a non empty list of txns. *)
Lemma is_installed_txn_implies_non_empty_txns γ cs txns installed_txn_id lb:
is_installed_txn (Σ:=Σ) γ cs txns installed_txn_id lb -∗
⌜ txns ≠ [] ⌝.
by eapply is_txn_implies_non_empty_txns.
Lemma circ_matches_txns_crash diskEnd cs txns installed_txn_id installer_pos installer_txn_id
diskEnd_mem diskEnd_mem_txn_id diskEnd_txn_id :
diskEnd = Z.to_nat (circΣ.diskEnd cs) →
circ_matches_txns cs txns
installed_txn_id installer_pos installer_txn_id
diskEnd_mem diskEnd_mem_txn_id diskEnd_txn_id →
circ_matches_txns cs (take (S diskEnd_txn_id) txns)
installed_txn_id installer_pos installer_txn_id
diskEnd diskEnd_txn_id diskEnd_txn_id.
rewrite /circ_matches_txns.
apply is_memLog_boundaries_take_txns; first by reflexivity.
replace (Z.to_nat (circΣ.diskEnd cs) - int.nat (start cs))%nat
with (length (upds cs)) by (rewrite /circΣ.diskEnd; word).
eapply (is_memLog_boundaries_move _ _ _ pmwrb_de) in Hcirc.
Lemma is_txn_from_take_is_txn n txns id pos:
is_txn (take n txns) id pos →
is_txn txns id pos.
destruct (decide (id < n)%nat).
by rewrite -> lookup_take by lia.
rewrite -> lookup_take_ge by lia.
Hint Unfold circ_matches_txns : word.
Lemma lookup_take_Some {A: Type} (l: list A) (n i: nat) a:
(take n l !! i = Some a) → (i < n)%nat.
rewrite lookup_take_ge in His_Some; auto; congruence.
Lemma is_txn_take txns txn_id pos :
is_txn txns txn_id pos →
is_txn (take (S txn_id) txns) txn_id pos.
rewrite -> lookup_take by lia; auto.
Lemma is_highest_txn_take txns txn_id pos :
is_highest_txn txns txn_id pos →
is_highest_txn (take (S txn_id) txns) txn_id pos.
rewrite /is_highest_txn /is_txn.
intros (Hlook&Hle); split.
rewrite -> lookup_take by lia; auto.
intros (x&Hlookup&Hpos); subst.
eapply lookup_take_Some in Hlookup; lia.
Lemma alloc_wal_init_ghost_state γ γcirc :
⊢ |==> ∃ γnew, "%Hbase_name" ∷ ⌜γnew.(base_disk_name) = γ.(base_disk_name)⌝ ∗
"%Hcirc_name" ∷ ⌜γnew.(circ_name) = γcirc⌝ ∗
"Hinit" ∷ wal_init_ghost_state γnew.
iMod (ghost_var_alloc 0%nat) as (installer_pos_name) "Hinstalled_pos".
iMod (ghost_var_alloc 0%nat) as (installer_txn_id_name) "Hinstalled_txn_id".
iMod (ghost_var_alloc (U64 0)) as (installer_pos_mem_name) "?".
iMod (ghost_var_alloc 0%nat) as (installer_txn_id_mem_name) "?".
iMod (ghost_var_alloc (U64 0)) as (logger_pos_name) "?".
iMod (ghost_var_alloc 0%nat) as (logger_txn_id_name) "?".
iMod (ghost_var_alloc (U64 0)) as (installed_pos_mem_name) "?".
iMod (ghost_var_alloc 0%nat) as (installed_txn_id_mem_name) "?".
iMod (mono_nat_own_alloc 0%nat) as (diskEnd_mem_name) "[? _]".
iMod (mono_nat_own_alloc 0%nat) as (diskEnd_mem_txn_id_name) "[? _]".
iMod (ghost_var_alloc 0%nat) as (diskEnd_name) "?".
iMod (ghost_var_alloc 0%nat) as (diskEnd_txn_id_name) "?".
iMod (ghost_var_alloc ([] : list update.t)) as (already_installed_name) "Halready_installed".
iMod (mono_nat_own_alloc 0%nat) as (being_installed_start_txn_name) "[Hbeing_start _]".
iMod (map_init (K:=nat) (V:=unit) ∅) as (γstable_txn_ids_name) "Hstable_txns".
iMod (alist_alloc (@nil (u64 * list update.t))) as (γtxns_ctx_name) "Htxns_ctx".
iMod (thread_own_alloc True with "[//]") as (start_avail_name) "(Hstart_avail_ctx&Hstart_avail)".
iMod (thread_own_alloc True with "[//]") as (diskEnd_avail_name) "(HdiskEnd_avail_ctx&HdiskEnd_avail)".
iMod (ghost_var_alloc (inhabitant : circΣ.t)) as (cs_name) "Hcs".
iMod (ghost_var_alloc ([] : list (u64 * list update.t))) as (txns_name) "Htxns".
iExists {| circ_name := γcirc;
cs_name := cs_name;
txns_ctx_name := γtxns_ctx_name;
txns_name := txns_name;
being_installed_start_txn_name := being_installed_start_txn_name;
already_installed_name := already_installed_name;
diskEnd_avail_name := diskEnd_avail_name;
start_avail_name := start_avail_name;
stable_txn_ids_name := γstable_txn_ids_name;
logger_pos_name := logger_pos_name;
logger_txn_id_name := logger_txn_id_name;
installer_pos_mem_name := installer_pos_mem_name;
installer_txn_id_mem_name := installer_txn_id_mem_name;
installer_pos_name := installer_pos_name;
installer_txn_id_name := installer_txn_id_name;
diskEnd_mem_name := diskEnd_mem_name;
diskEnd_mem_txn_id_name := diskEnd_mem_txn_id_name;
diskEnd_name := diskEnd_name;
diskEnd_txn_id_name := diskEnd_txn_id_name;
installed_pos_mem_name := installed_pos_mem_name;
installed_txn_id_mem_name := installed_txn_id_mem_name;
base_disk_name := γ.(base_disk_name) |}; simpl.
Definition log_state0 bs : log_state.t :=
{|
log_state.d := list_to_map (imap (λ (i : nat) (x : Block), (513 + i, x)) bs);
log_state.txns := [(U64 0, [])];
log_state.installed_lb := 0;
log_state.durable_lb := 0
|}.
Lemma alloc_wal_init_ghost_state' γcirc bs :
⊢ |==> ∃ γbasedisk γnew,
"#Hbase_disk" ∷ is_base_disk γnew (log_state0 bs).(log_state.d) ∗
"%Hbase_name" ∷ ⌜γnew.(base_disk_name) = γbasedisk⌝ ∗
"%Hcirc_name" ∷ ⌜γnew.(circ_name) = γcirc⌝ ∗
"Hinit" ∷ wal_init_ghost_state γnew.
iMod (own_alloc (to_agree (log_state0 bs).(log_state.d))) as (γbasedisk) "H".
iMod (alloc_wal_init_ghost_state (wal_names_dummy <| base_disk_name := γbasedisk |>) γcirc) as (γnew Heq1 Heq2) "?"; iNamed.
Lemma log_state0_wf bs : wal_wf (log_state0 bs).
change (log_state.updates (log_state0 bs)) with (@nil update.t).
rewrite /list_mono; intros.
apply lookup_lt_Some in Hx1.
apply lookup_lt_Some in Hx2.
Lemma log_state0_post_crash bs : wal_post_crash (log_state0 bs).
rewrite /wal_post_crash /=.
Definition logger_resources γ : iProp Σ :=
(* subset of logger, with the pre resources needed to eventually form [is_circular_appender], which depends on in-memory state *)
∃ diskEnd diskEnd_txn_id,
"HnotLogging" ∷ thread_own γ.(diskEnd_avail_name) Available ∗
"HownLoggerPos_logger" ∷ ghost_var γ.(logger_pos_name) (1/2) diskEnd ∗
"HownLoggerTxn_logger" ∷ ghost_var γ.(logger_txn_id_name) (1/2) diskEnd_txn_id ∗
"HownDiskEndMem_logger" ∷ mono_nat_auth_own γ.(diskEnd_mem_name) (1/2) (int.nat diskEnd) ∗
"HownDiskEndMemTxn_logger" ∷ mono_nat_auth_own γ.(diskEnd_mem_txn_id_name) (1/2) diskEnd_txn_id ∗
"HownDiskEnd_logger" ∷ ghost_var γ.(diskEnd_name) (1/2) (int.nat diskEnd) ∗
"HownDiskEndTxn_logger" ∷ ghost_var γ.(diskEnd_txn_id_name) (1/2) diskEnd_txn_id ∗
"Happender_pre" ∷ is_circular_appender_pre γ.(circ_name).
Definition wal_resources γ : iProp Σ :=
logger_resources γ ∗ installer_inv γ.
Definition background_inv l γ : iProp Σ :=
∃ (circ_l: loc),
l ↦[Walog :: "circ"] #circ_l ∗
logger_inv γ circ_l ∗
installer_inv γ.
Ltac iDestruct_2 H :=
(* TODO(tej): I'm sorry *)
let pat := eval cbv in ("[" +:+ H +:+ " " +:+ H +:+ "2]") in
iDestruct H as pat.
Definition memLog_linv_init_ghost_state γ : iProp Σ :=
(ghost_var γ.(txns_name) (1/2) [(U64 0, [])]
∗ ghost_var γ.(logger_pos_name) (1/2) (U64 0)
∗ ghost_var γ.(logger_txn_id_name) (1/2) 0%nat
∗ ghost_var γ.(installer_pos_mem_name) (1/2) (U64 0)
∗ ghost_var γ.(installer_txn_id_mem_name) (1/2) 0%nat
∗ mono_nat_auth_own γ.(diskEnd_mem_name) (1/2/2) 0%nat
∗ mono_nat_auth_own γ.(diskEnd_mem_txn_id_name) (1/2/2) 0%nat
∗ ghost_var γ.(installed_pos_mem_name) (1/2) (U64 0)
∗ ghost_var γ.(installed_txn_id_mem_name) (1/2) 0%nat
∗ map_ctx γ.(stable_txn_ids_name) (1/2) {[0%nat := ()]}).
Definition memLog_linv_init_pers_state γ : iProp Σ :=
(0%nat [[γ.(stable_txn_ids_name)]]↦ro () ∗
txn_pos γ 0 0 ∗
mono_nat_lb_own γ.(being_installed_start_txn_name) 0).
Lemma memLog_linv_pers_core_init γ :
memLog_linv_init_pers_state γ
-∗ memLog_linv_pers_core γ
{| slidingM.log := []; slidingM.start := 0; slidingM.mutable := 0 |} 0 0 0 0
[(U64 0, [])] 0 0 0 0.
rewrite /memLog_linv_pers_core /named /=.
change (slidingM.endPos _) with (U64 0).
rewrite /slidingM.memEnd /=.
rewrite /memLog_linv_txns /named /slidingM.logIndex /mwrb.logend /=.
replace (int.nat 0) with 0%nat by word.
apply elem_of_list_singleton in Hx.
replace (_ :: _) with (replicate 6 {| mwrb.txn := 1; mwrb.upd := 0 |});
last by reflexivity.
apply elem_of_replicate_inv in Hbndry.
intros i bndry1 bndry2 Hbndry1 Hbndry2.
apply elem_of_list_lookup_2 in Hbndry1.
apply elem_of_list_lookup_2 in Hbndry2.
apply elem_of_replicate_inv in Hbndry1.
apply elem_of_replicate_inv in Hbndry2.
split; first by reflexivity.
split; first by reflexivity.
split; first by apply has_updates_nil.
rewrite subslice_zero_length in Hin.
Lemma memLog_linv_nextDiskEnd_txn_id_init γ :
memLog_linv_init_pers_state γ -∗
map_ctx γ.(stable_txn_ids_name) (1/2) {[0%nat := ()]} -∗
memLog_linv_nextDiskEnd_txn_id γ 0 0.
iIntros "#(?&?&?) Hstable".
iExists ({[0%nat:=()]} : gmap nat unit).
apply lookup_singleton_ne.
Lemma memLog_linv_init γ :
memLog_linv_init_pers_state γ -∗
memLog_linv_init_ghost_state γ -∗
memLog_linv γ
{| slidingM.log := [];
slidingM.start := 0;
slidingM.mutable := 0; |} 0 0.
iDestruct "Hvars" as "(?&?&?&?&?&?&?&?&?&Hstable)".
iExists 0%nat, 0%nat, [(U64 0, [])], (U64 0), 0%nat, (U64 0), 0%nat.
rewrite /memLog_linv_core /named.
iDestruct (memLog_linv_pers_core_init with "Hpers") as "$".
iApply (memLog_linv_nextDiskEnd_txn_id_init with "Hpers Hstable").
Lemma wal_linv_durable_init γ :
(diskEnd_at_least γ.(circ_name) 0
∗ thread_own_ctx γ.(diskEnd_avail_name)
(diskEnd_is γ.(circ_name) (1 / 2) 0))
∗ (start_at_least γ.(circ_name) 0
∗ thread_own_ctx γ.(start_avail_name) (start_is γ.(circ_name) (1 / 2) 0)) -∗
memLog_linv_init_pers_state γ -∗
memLog_linv_init_ghost_state γ -∗
wal_linv_durable γ {| circΣ.upds := []; circΣ.start := U64 0; |}.
iIntros "[[#HdiskEnd_lb HdiskEnd_ctx] [#Hstart_lb Hstart_ctx]] #HmemLog_pers HmemLog_ghost".
rewrite /wal_linv_durable.
iExists {| diskEnd := U64 0;
locked_diskEnd_txn_id := 0;
memLog := _;
|}; simpl.
change (circΣ.diskEnd _) with 0.
rewrite /locked_wf /diskEnd_linv /diskStart_linv /=.
iApply (memLog_linv_init with "HmemLog_pers HmemLog_ghost").
Lemma disk_array_to_big_opM_ind arrEnd bs :
(arrEnd - length bs) d↦∗ bs -∗
[∗ map] a↦b ∈ list_to_map (imap (λ i b', (arrEnd - length bs + i, b')) bs), a d↦ b.
iInduction bs as [|b0] "IH"; first by eauto.
rewrite disk_array_cons /= Z.add_0_r.
iDestruct "Hbs" as "[Hb0 Hbs]".
replace (arrEnd - S (length bs) + 1) with (arrEnd - length bs) by lia.
iApply (big_sepM_insert_2 with "[Hb0]").
replace (imap ((λ (i : nat) (b' : Block), (arrEnd - S (length bs) + i, b')) ∘ S) bs)
with (imap (λ (i : nat) (b' : Block), (arrEnd - length bs + i, b')) bs).
Lemma disk_array_to_big_opM start bs :
start d↦∗ bs -∗
[∗ map] a↦b ∈ list_to_map (imap (λ i b', (start + i, b')) bs), a d↦ b.
replace start with ((start + length bs) - length bs) by lia.
apply disk_array_to_big_opM_ind.
Lemma dom_blocks_to_map_u64 {A} (f: Block → A) (bs: list Block) :
dom (list_to_map (imap (λ i x, (U64 (513 + i), f x)) bs) : gmap u64 _) =
rangeSet 513 (length bs).
rewrite dom_list_to_map_L.
change (λ n, fst ∘ _) with (λ (n:nat) (_:Block), U64 (513 + n)).
generalize dependent start.
induction bs; simpl; intros.
rewrite -> seqZ_cons by lia.
replace (Z.succ start) with (start + 1)%Z by lia.
replace (Z.pred (S (length bs))) with (Z.of_nat $ length bs) by lia.
replace (start + 0%nat) with start by lia.
apply imap_ext; intros; simpl.
Lemma dom_blocks_to_map_Z {A} (f: Block → A) (bs: list Block) :
dom (list_to_map (imap (λ i x, (513 + i, f x)) bs) : gmap Z _) =
list_to_set (seqZ 513 (length bs)).
rewrite dom_list_to_map_L.
change (λ n, fst ∘ _) with (λ (n:nat) (_:Block), (513 + n)).
generalize dependent start.
induction bs; simpl; intros.
rewrite -> seqZ_cons by lia.
replace (Z.succ start) with (start + 1) by lia.
replace (Z.pred (S (length bs))) with (Z.of_nat $ length bs) by lia.
replace (start + 0%nat) with start by lia.
apply imap_ext; intros; simpl.
Lemma is_wal_inner_durable_init (bs: list Block) :
dom dinit = list_to_set (seqZ 513 (length bs)) →
0 d↦∗ repeat block0 513 ∗
513 d↦∗ bs ==∗
let σ := log_state0 bs in
∃ γ, is_wal_inner_durable γ σ dinit ∗ wal_resources γ.
iIntros (Hdinit_dom) "[Hlog Hdata]".
iMod (circular_init with "Hlog") as (γcirc) "(Hcirc & Hcirc_appender & Hstart & Hend)".
iDestruct (start_is_to_at_least with "Hstart") as "#Hstart_lb".
iDestruct (diskEnd_is_to_at_least with "Hend") as "#Hend_lb".
iMod (alloc_wal_init_ghost_state' γcirc bs) as (γbasedisk γ) "Hinit".
iMod (map_alloc_ro 0%nat () with "stable_txn_ids") as "[stable_txn_ids #H0stable]".
change (<[0%nat:=()]> ∅) with ({[0%nat:=()]} : gmap nat unit).
iMod (ghost_var_update [(U64 0, [])] with "txns") as "txns".
iMod (ghost_var_update {| circΣ.upds := []; circΣ.start := U64 0 |} with "cs") as "cs".
iDestruct_2 "logger_pos".
iDestruct_2 "logger_txn_id".
iDestruct_2 "installer_pos".
iDestruct_2 "installer_txn_id".
iDestruct_2 "stable_txn_ids".
iDestruct_2 "diskEnd_mem".
iDestruct_2 "diskEnd_mem_txn_id".
iDestruct_2 "diskEnd_txn_id".
iDestruct_2 "installed_pos_mem".
iDestruct_2 "installed_txn_id_mem".
iDestruct_2 "installer_pos_mem".
iDestruct_2 "installer_txn_id_mem".
iDestruct_2 "already_installed".
iDestruct_2 "being_installed_start_txn".
iDestruct "diskEnd_mem" as "[diskEnd_mem11 diskEnd_mem12]".
iDestruct "diskEnd_mem_txn_id"
as "[diskEnd_mem_txn_id11 diskEnd_mem_txn_id12]".
iDestruct (mono_nat_lb_own_get with "being_installed_start_txn")
as "#being_installed_start_txn_lb".
iDestruct (mono_nat_lb_own_get with "diskEnd_mem_txn_id2")
as "#diskEnd_mem_txn_id_lb".
iMod (alloc_txn_pos (U64 0) [] with "txns_ctx") as "[txns_ctx _]".
iMod (thread_own_replace with "start_avail_ctx start_avail Hstart")
as "(start_avail_ctx&start_avail)".
iMod (thread_own_replace with "diskEnd_avail_ctx diskEnd_avail Hend")
as "(diskEnd_avail_ctx&diskEnd_avail)".
rewrite /is_wal_inner_durable.
cbn [log_state.txns log_state0].
iPoseProof (txns_ctx_txn_pos _ _ 0 0 with "txns_ctx") as "#Htxn_pos".
iAssert (memLog_linv_init_ghost_state _) with "[$]" as "HmemLog_ghost".
iAssert (is_installed_core_ghost _ _ _) with "[$]" as "Hinstalled_ghost".
iSplitL "Hcirc stable_txn_ids cs
HmemLog_ghost start_avail_ctx diskEnd_avail_ctx
Hinstalled_ghost Hdata
installer_pos2 installer_txn_id2
diskEnd_mem11 diskEnd_mem_txn_id11
diskEnd2 diskEnd_txn_id2
".
iSplitL "
logger_pos logger_txn_id
diskEnd_mem2 diskEnd_mem_txn_id2
diskEnd diskEnd_txn_id
".
replace (int.nat 0) with 0%nat by word.
iFrame "diskEnd_mem_txn_id_lb ∗".
iSplitL "installer_pos"; first by (iExists _; iFrame).
iSplitL "installer_pos_mem"; first by (iExists _; iFrame).
iSplitL "installer_txn_id_mem"; iExists _; iFrame.
iSplit; first by eauto using log_state0_wf.
iSplit; first by eauto using log_state0_post_crash.
rewrite /nextDiskEnd_inv.
iSplitL "stable_txn_ids".
rewrite big_sepM_singleton.
rewrite /is_txn in H1, H2.
rewrite -list_lookup_fmap /= in H1.
rewrite -list_lookup_fmap /= in H2.
apply lookup_lt_Some in H1.
apply lookup_lt_Some in H2.
iSplitL "HmemLog_ghost start_avail_ctx diskEnd_avail_ctx".
iApply (wal_linv_durable_init with "[start_avail_ctx diskEnd_avail_ctx] [] HmemLog_ghost").
iExists 0%nat, 0%nat, 0%nat.
iSplitL "Hinstalled_ghost Hdata".
iSplit; first by (rewrite /log_state0 /=; iPureIntro; lia).
iSplit; first by (rewrite subslice_zero_length; iApply txns_are_nil).
iApply (big_sepM_mono (λ a b, a d↦ b)%I).
apply elem_of_list_to_map_2 in Hb.
destruct (elem_of_lookup_imap_1 _ _ _ Hb) as (a'&b'&Ha'&Hlookup).
iApply (disk_array_to_big_opM with "Hdata").
split; last by (simpl; word).
do 4 (
apply elem_of_cons in Hbndry;
destruct Hbndry as [->|Hbndry];
first by (simpl; word)
).
intros i bndry1 bndry2 Hbndry1 Hbndry2.
assert (
bndry1 = {| mwrb.txn := 1%nat; mwrb.upd := 0%nat |} ∧
bndry2 = {| mwrb.txn := 1%nat; mwrb.upd := 0%nat |}
) as [-> ->].
do 4 (destruct i; first by (
simpl in Hbndry1; inversion Hbndry1; subst bndry1; clear Hbndry1;
simpl in Hbndry2; inversion Hbndry2; subst bndry2; clear Hbndry2;
intuition
)).
rewrite subslice_nil subslice_zero_length.
apply is_memLog_region_nil.
iSplit; first by rewrite /log_state0 //.
rewrite /log_state0 /circΣ.diskEnd /=.
split; first by apply Forall_nil_2.
split; first by constructor.
rewrite dom_blocks_to_map_Z //.
Lemma is_base_disk_crash γ γ' d :
γ'.(base_disk_name) = γ.(base_disk_name) →
is_base_disk γ d ⊢@{_} is_base_disk γ' d.
rewrite /is_base_disk => -> //.
Definition map_set_ctx {K} `{Countable K} `{!mapG Σ K ()}
(γ: gname) q (m: gmap K ()) : iProp Σ :=
map_ctx γ q m ∗
[∗ map] k↦_ ∈ m, ptsto_ro γ k ().
Lemma map_set_ctx_alloc1 {K} `{Countable K} `{!mapG Σ K ()} {γ: gname} (k:K) (s: gset K) :
map_set_ctx γ 1 (gset_to_gmap () s) -∗
|==> map_set_ctx γ 1 (gset_to_gmap () ({[k]} ∪ s)) ∗
ptsto_ro γ k ().
iDestruct 1 as "[Hctx Hro]".
destruct (gset_to_gmap () s !! k) eqn:Hlookup.
(* already there, just need to extract it *)
iDestruct (big_sepM_lookup _ _ k () with "Hro") as "#$"; first by auto.
replace ({[k]} ∪ s) with s; [ by iFrame | ].
apply lookup_gset_to_gmap_Some in Hlookup as [? _].
iMod (map_alloc_ro k () with "Hctx") as "[Hctx #Hk]"; first by auto.
rewrite gset_to_gmap_union_singleton.
rewrite big_sepM_insert //.
Lemma map_set_ctx_alloc {K} `{Countable K} `{!mapG Σ K ()} {γ: gname} (s' s: gset K) :
map_set_ctx γ 1 (gset_to_gmap () s) -∗
|==> map_set_ctx γ 1 (gset_to_gmap () (s' ∪ s)) ∗
[∗ set] k ∈ s', ptsto_ro γ k ().
iInduction s' as [|k s'] "IH" using set_ind_L.
rewrite left_id_L big_sepS_empty.
rewrite gset_to_gmap_union_singleton.
rewrite big_sepS_insert //.
iMod ("IH" with "Hctx") as "[Hctx $]".
iMod (map_set_ctx_alloc1 k with "Hctx") as "[Hctx #$]".
rewrite gset_to_gmap_union_singleton //.
Lemma map_alloc_ro_set {K} `{Countable K} `{!mapG Σ K ()} {γ: gname} (s: gset K) :
map_ctx γ 1 (∅ : gmap K ()) -∗
|==> map_ctx γ 1 (gset_to_gmap () s) ∗
[∗ set] k∈s, ptsto_ro γ k ().
iMod (map_set_ctx_alloc s ∅ with "[Hctx]") as "[Hctx $]".
rewrite gset_to_gmap_empty.
iDestruct "Hctx" as "[$ _]".
Lemma init_stable_txns γstable_txn_ids_name (installed_txn_id diskEnd_txn_id durable_lb : nat) :
map_ctx γstable_txn_ids_name 1 (∅ : gmap nat unit) -∗
|==> "Hstable_txns" ∷ map_ctx γstable_txn_ids_name 1 (gset_to_gmap () {[diskEnd_txn_id; installed_txn_id; (durable_lb `max` diskEnd_txn_id)%nat]}) ∗
"#HdiskEnd_stable" ∷ diskEnd_txn_id [[γstable_txn_ids_name]]↦ro () ∗
"#Hnew_durable_lb_stable" ∷
(durable_lb `max` diskEnd_txn_id)%nat [[γstable_txn_ids_name]]↦ro () ∗
"#Hinstalled_txn_id_stable" ∷ installed_txn_id [[γstable_txn_ids_name]]↦ro () ∗
"#Hstable_ro" ∷ ([∗ map] txn_id ↦ _ ∈ (gset_to_gmap () {[diskEnd_txn_id; installed_txn_id; (durable_lb `max` diskEnd_txn_id)%nat]}),
txn_id [[γstable_txn_ids_name]]↦ro tt).
iMod (map_alloc_ro_set with "Hstable_txns") as "[$ Hro]".
iDestruct (big_sepS_elem_of with "Hro") as "#$"; first by set_solver.
iDestruct (big_sepS_elem_of with "Hro") as "#$"; first by set_solver.
iDestruct (big_sepS_elem_of with "Hro") as "#$"; first by set_solver.
rewrite dom_gset_to_gmap.
Lemma is_installed_txn_crash γ γ' cs txns installed_txn_id installed_lb crash_txn :
(installed_txn_id ≤ crash_txn)%nat →
is_installed_txn γ cs txns installed_txn_id installed_lb -∗
installed_txn_id [[γ'.(stable_txn_ids_name)]]↦ro () -∗
is_installed_txn γ' cs (take (S crash_txn) txns) installed_txn_id installed_lb.
iIntros "#Hstart_stable'".
rewrite /is_installed_txn.
rewrite /is_txn in Hstart_txn |- *.
rewrite -> lookup_take by lia.
Lemma is_durable_txn_crash γ γ' cs txns diskEnd_txn_id durable_lb :
is_durable_txn γ cs txns diskEnd_txn_id durable_lb -∗
diskEnd_txn_id [[γ'.(stable_txn_ids_name)]]↦ro () -∗
(durable_lb `max` diskEnd_txn_id)%nat [[γ'.(stable_txn_ids_name)]]↦ro () -∗
let new_durable_lb := (durable_lb `max` diskEnd_txn_id)%nat in
is_durable_txn γ'
cs (take (S new_durable_lb) txns) diskEnd_txn_id new_durable_lb.
iIntros "#Hend_txn_stable' #Hdurable_lb_stable'".
rewrite /is_durable_txn take_length.
rewrite (Nat.max_l (_ `max` _) _); last by lia.
iExists diskEnd, diskEnd_txn_id; iFrame "%#".
pose proof (is_txn_bound _ _ _ Hend_txn).
rewrite subslice_to_end; last by (rewrite take_length; lia).
rewrite -subslice_take_drop.
destruct (decide (durable_lb ≤ diskEnd_txn_id)%nat).
rewrite Nat.max_r; last by lia.
rewrite subslice_zero_length.
rewrite Nat.max_l; last by lia.
rewrite -(subslice_app_contig _ (S diskEnd_txn_id)) in Hdurable_nils;
last by lia.
apply Forall_app in Hdurable_nils.
destruct (decide (durable_lb ≤ diskEnd_txn_id)%nat)
as [Hcmp|Hcmp].
rewrite Nat.max_r; last by lia.
apply is_txn_take; assumption.
rewrite Nat.max_l; last by lia.
apply (is_txn_from_take_is_txn (S diskEnd_txn_id)).
rewrite Nat.min_l; last by lia.
Lemma diskEnd_linv_post_crash γ' diskEnd Q :
int.Z (U64 diskEnd) = diskEnd →
diskEnd_is γ'.(circ_name) (1/2) diskEnd -∗
thread_own_ctx γ'.(diskEnd_avail_name) Q -∗
thread_own γ'.(diskEnd_avail_name) Available -∗
|==> diskEnd_linv γ' diskEnd ∗
thread_own γ'.(diskEnd_avail_name) Available.
iIntros (Hbound) "H Hctx Havail".
iDestruct (diskEnd_is_to_at_least with "[$]") as "#Hatleast".
iMod (thread_own_get with "Hctx Havail") as "(Hctx & _ & Hused)".
replace (int.Z (U64 diskEnd)) with diskEnd by auto.
iMod (thread_own_put (diskEnd_is γ'.(circ_name) (1/2) diskEnd) with
"Hctx Hused H") as "[$ $]".
Lemma diskStart_linv_post_crash γ' start Q :
start_is γ'.(circ_name) (1/2) start -∗
thread_own_ctx γ'.(start_avail_name) Q -∗
thread_own γ'.(start_avail_name) Available -∗
|==> diskStart_linv γ' start ∗
thread_own γ'.(start_avail_name) Available.
iDestruct (start_is_to_at_least with "[$]") as "#Hatleast".
iMod (thread_own_get with "Hctx Havail") as "(Hctx & _ & Hused)".
iMod (thread_own_put (start_is γ'.(circ_name) (1/2) start) with
"Hctx Hused H") as "[$ $]".
Lemma memLog_linv_nextDiskEnd_txn_id_post_crash γ diskEnd diskEnd_txn_id installed_txn_id durable_lb :
(installed_txn_id ≤ diskEnd_txn_id)%nat →
map_ctx γ.(stable_txn_ids_name) (1/2) (gset_to_gmap () {[diskEnd_txn_id; installed_txn_id; (durable_lb `max` diskEnd_txn_id)%nat]}) -∗
txn_pos γ (durable_lb `max` diskEnd_txn_id) diskEnd -∗
(durable_lb `max` diskEnd_txn_id)%nat [[γ.(stable_txn_ids_name)]]↦ro () -∗
memLog_linv_nextDiskEnd_txn_id γ diskEnd (durable_lb `max` diskEnd_txn_id)%nat.
iIntros (Hbound) "Hctx #Hpos #HdiskEnd_stable".
apply lookup_gset_to_gmap_None.
assert (txn_id ≠ diskEnd_txn_id) by lia.
assert (txn_id ≠ installed_txn_id) by lia.
assert (txn_id ≠ (durable_lb `max` diskEnd_txn_id)%nat) by lia.
Lemma is_durable_txn_get_txn_pos γ' cs txns diskEnd_txn_id durable_lb :
is_durable_txn γ' cs txns diskEnd_txn_id durable_lb -∗
txns_ctx γ' txns -∗
txn_pos γ' diskEnd_txn_id (U64 (circΣ.diskEnd cs)) ∗
txn_pos γ' (durable_lb `max` diskEnd_txn_id) (U64 (circΣ.diskEnd cs)).
iDestruct (txns_ctx_txn_pos with "Hctx") as "$"; eauto.
replace (U64 (circΣ.diskEnd cs)) with diskEnd by word; auto.
iDestruct (txns_ctx_txn_pos with "Hctx") as "$"; eauto.
replace (U64 (circΣ.diskEnd cs)) with diskEnd by word; auto.
Lemma txns_mono_lt_last σ diskEnd :
wal_wf σ →
is_txn σ.(log_state.txns) (length σ.(log_state.txns) - 1) diskEnd →
Forall (λ pos, int.Z pos ≤ int.Z diskEnd) σ.(log_state.txns).*1.
apply Forall_forall => pos Hin.
apply elem_of_list_lookup in Hin as [txn_id Hlookup].
assert (is_txn σ.(log_state.txns) txn_id pos).
rewrite -list_lookup_fmap //.
eapply (wal_wf_txns_mono_pos' (txn_id1:=txn_id)); eauto.
Lemma stable_sound_crash txns stable_txns crash_txn :
stable_sound txns stable_txns →
stable_sound (take crash_txn txns) stable_txns.
rewrite /stable_sound;
intros Hstable ??? Hgt Hlookup Htxn Htxn'.
specialize (Hstable _ _ pos Hgt Hlookup).
rewrite /is_txn in Htxn; fmap_Some in Htxn as txn.
rewrite /is_txn in Htxn'; fmap_Some in Htxn' as txn'.
pose proof (lookup_take_Some _ _ _ _ Htxn).
pose proof (lookup_take_Some _ _ _ _ Htxn').
rewrite lookup_take in Htxn; auto.
rewrite lookup_take in Htxn'; auto.
apply Hstable; rewrite /is_txn.
Lemma nextDiskEnd_inv_post_crash_stable_sound γ txns installed_txn_id diskEnd_txn_id durable_lb :
Forall (λ x, snd x = nil) (subslice (S diskEnd_txn_id) (S (durable_lb `max` diskEnd_txn_id)) txns) →
nextDiskEnd_inv γ txns -∗
installed_txn_id [[γ.(stable_txn_ids_name)]]↦ro () -∗
⌜let stable_txns' := gset_to_gmap tt {[diskEnd_txn_id; installed_txn_id; (durable_lb `max` diskEnd_txn_id)%nat]} in
stable_sound (take (S (durable_lb `max` diskEnd_txn_id))%nat txns) stable_txns'⌝.
iIntros "Hinstalled_stable".
iDestruct (map_ro_valid with "Hstablectx Hinstalled_stable") as %Hinstalled_stable.
apply (stable_sound_crash _ _ (S (durable_lb `max` diskEnd_txn_id))) in HafterNextDiskEnd.
iIntros (??? Hlt Hlookup Htxn Htxn').
apply lookup_gset_to_gmap_Some in Hlookup as [Hlookup _].
pose proof Htxn' as Htxn'_backup.
rewrite /is_txn in Htxn'.
fmap_Some in Htxn' as txn'.
assert (Htxn'_lt:=Htxn').
apply mk_is_Some, lookup_lt_is_Some_1 in Htxn'_lt.
rewrite take_length in Htxn'_lt.
assert (txn_id = diskEnd_txn_id ∨ txn_id = installed_txn_id ∨ txn_id = (durable_lb `max` diskEnd_txn_id)%nat) as [-> | [-> | ->]] by set_solver.
(* we're talking about diskEnd_txn_id *)
unshelve (epose proof (iffLR (Forall_forall _ _) Hnils _ _) as Hnil).
apply elem_of_list_lookup.
eexists (txn_id' - (S diskEnd_txn_id))%nat.
rewrite subslice_lookup; last by lia.
rewrite -(lookup_take _ (S (durable_lb `max` diskEnd_txn_id)));
last by lia.
rewrite -Nat.le_add_sub; last by lia.
(* this is installed_txn_id, which we get from its stability before *)
eapply HafterNextDiskEnd; eauto.
(* Called after wpc for recovery is completed, so l is the location of the wal *)
Lemma wal_crash_obligation_alt E Prec Pcrash l γ s :
↑walN ⊆ E →
is_wal_inv_pre l γ s dinit -∗
□ (∀ s s' (Hcrash: relation.denote log_crash s s' ()),
▷ P s -∗ |={E ∖ ↑N.@"wal"}=> ▷ Prec s s' ∗ ▷ Pcrash s s') -∗
▷ P s -∗
|={⊤}=> ∃ γ', is_wal P l γ dinit ∗
(<bdisc> (|C={E}=> ∃ s s',
⌜relation.denote log_crash s s' tt⌝ ∗
(* NOTE: need to add the ghost state that the logger will need *)
is_wal_inner_durable γ' s' dinit ∗
wal_resources γ' ∗ ▷ Prec s s')) ∗
□ (|C={E}=> inv (N.@"wal") (∃ s s',
⌜relation.denote log_crash s s' tt⌝ ∗
is_wal_inner_crash γ s ∗
wal_ghost_exchange γ γ' ∗
Pcrash s s')).
iIntros (Hin) "Hinv_pre #Hwand HP".
iDestruct "Hinv_pre" as "(Hinner&Hcirc)".
iDestruct "Hcirc" as (cs) "(Hcirc_state&Hcirc_pred)".
iMod (circ_buf_crash_obligation_alt circN (λ σ, circular_pred γ σ)%I (↑circN)
(λ σ, circular_pred γ σ)%I
(λ _, True)%I with "Hcirc_state [] [Hcirc_pred]") as
(γcirc') "(#His_circular&His_circular_cfupd&His_circular_crash)".
iMod (alloc_wal_init_ghost_state γ γcirc') as (γ') "H"; iNamed "H".
iDestruct (own_discrete_laterable with "His_circular_cfupd") as (Pcirc_tok) "(HPcirc_tok&#HPcirc_tok_wand)".
iMod (ncinv_cinv_alloc (N.@"wal") ⊤ E
((∃ σ, is_wal_inner l γ σ dinit ∗ P σ) ∗
wal_init_ghost_state γ' ∗ Pcirc_tok)
(∃ σ σ',
⌜relation.denote log_crash σ σ' tt⌝ ∗
is_wal_inner_crash γ σ ∗
wal_ghost_exchange γ γ' ∗
Pcrash σ σ')%I
(∃ s s',
⌜relation.denote log_crash s s' tt⌝ ∗
(is_wal_inner_durable γ' s' dinit) ∗ wal_resources γ' ∗ Prec s s')%I with
"[] [Hinner HP Hinit HPcirc_tok]") as "(Hncinv&Hcfupd&Hcinv)".
iIntros "(H1&>Hinit&Htok) #HC".
iMod ("HPcirc_tok_wand" with "[$]") as "H".
iSpecialize ("H" with "[$]").
iMod (fupd_mask_mono with "H") as (cs0') "(Hcirc&Hcirc_resources&>Hcirc_pred)"; first solve_ndisj.
iDestruct "H1" as (σ) "(His_wal_inner&HP)".
iDestruct "His_wal_inner" as "(>%Hwf&_&>?&>?&>?&>?)"; iNamed.
iDestruct (ghost_var_agree with "Howncs Hcirc_pred") as %<-.
iDestruct (is_circular_state_wf with "[$]") as %Hcirc_wf.
iMod (init_stable_txns γ'.(stable_txn_ids_name) installed_txn_id diskEnd_txn_id σ.(log_state.durable_lb) with "[$]") as "Hstable".
set (σ':= log_crash_to
σ (σ.(log_state.durable_lb) `max` diskEnd_txn_id)
).
iDestruct (crash_to_diskEnd with "circ.end Hdurable") as %Htrans.
iMod (ghost_var_update installer_pos with "installer_pos") as "[installer_pos1 installer_pos2]".
iMod (ghost_var_update installer_txn_id with "installer_txn_id") as "[installer_txn_id1 installer_txn_id2]".
iMod (ghost_var_update [] with "already_installed") as "[already_installed1 already_installed2]".
iMod (mono_nat_own_update installed_txn_id with "being_installed_start_txn") as "[[being_installed_start_txn1 being_installed_start_txn2] #being_installed_start_txn_id_mem_lb]"; first by lia.
iMod (txns_ctx_app (take
(S (σ.(log_state.durable_lb) `max` diskEnd_txn_id))
σ.(log_state.txns)
) with "txns_ctx") as "Htxns_ctx'".
iMod (ghost_var_update σ'.(log_state.txns) with "txns") as "[Htxns1 Htxns2]".
iDestruct (is_durable_txn_crash with "circ.end [$] [$]") as "#Hdurable_txn".
replace (U64 (circΣ.diskEnd cs0)) with diskEnd by word.
iPoseProof "circ.start" as "#circ.start2"; iNamed "circ.start2".
iDestruct "Hcirc_resources" as "(Hcirc_start&Hcirc_diskEnd & Happender)".
iDestruct (nextDiskEnd_inv_post_crash_stable_sound _ _ _ diskEnd_txn_id σ.(log_state.durable_lb)
with "HnextDiskEnd_inv Hstart_txn_stable") as %Hstable.
destruct (decide (σ.(log_state.durable_lb) ≤ diskEnd_txn_id)%nat).
rewrite Nat.max_r; last by lia.
rewrite subslice_zero_length.
rewrite Nat.max_l; last by lia.
rewrite -(subslice_app_contig _ (S diskEnd_txn_id)) in Hdurable_nils;
last by lia.
apply Forall_app in Hdurable_nils.
iDestruct (txns_ctx_txn_pos _ _ installed_txn_id with "Htxns_ctx") as "#Hinstalled_pos";
first by eauto.
iDestruct (txns_ctx_make_factory with "Htxns_ctx Htxns_ctx'") as "[Hold_txns Htxns_ctx']".
iDestruct (is_durable_txn_get_txn_pos with "Hdurable_txn Htxns_ctx'") as "[#HdiskEnd_pos #Hdurable_lb_pos]".
iDestruct (txn_pos_valid_general with "Htxns_ctx' HdiskEnd_pos") as %HdiskEnd_is_txn.
iDestruct (old_txn_get_pos with "[$] Hinstalled_pos") as "#Hinstalled_pos'"; first by lia.
iMod (diskEnd_linv_post_crash _ (int.Z diskEnd)
with "[Hcirc_diskEnd] diskEnd_avail_ctx diskEnd_avail")
as "(HdiskEnd_linv & diskEnd_avail)".
iExactEq "Hcirc_diskEnd".
iMod (diskStart_linv_post_crash
with "[Hcirc_start] start_avail_ctx start_avail")
as "(HdiskStart_linv & start_avail)".
iDestruct "Hstable_txns" as "[Hstable_txns1 Hstable_txns2]".
rewrite (Nat.max_l (_ `max` _) _); last by lia.
iDestruct (memLog_linv_nextDiskEnd_txn_id_post_crash with
"Hstable_txns1 Hdurable_lb_pos [$]")
as "HnextDiskEnd_linv"; first by lia.
iMod (ghost_var_update (cs0.(circΣ.start)) with "installer_pos_mem")
as "[installer_pos_mem1 installer_pos_mem2]".
iMod (ghost_var_update (cs0.(circΣ.start)) with "installed_pos_mem")
as "[installed_pos_mem1 installed_pos_mem2]".
iMod (ghost_var_update installed_txn_id with "installed_txn_id_mem")
as "[installed_txn_id_mem1 installed_txn_id_mem2]".
iMod (ghost_var_update installed_txn_id with "installer_txn_id_mem")
as "[installer_txn_id_mem1 installer_txn_id_mem2]".
iMod (ghost_var_update (U64 (circΣ.diskEnd cs0)) with "logger_pos")
as "[logger_pos1 logger_pos2]".
iMod (ghost_var_update diskEnd_txn_id with "logger_txn_id")
as "[logger_txn_id1 logger_txn_id2]".
iMod (ghost_var_update (int.nat (circΣ.diskEnd cs0)) with "diskEnd")
as "[diskEnd1 diskEnd2]".
iMod (ghost_var_update diskEnd_txn_id with "diskEnd_txn_id")
as "[diskEnd_txn_id1 diskEnd_txn_id2]".
iMod (ghost_var_update cs0 with "cs")
as "cs".
iMod (mono_nat_own_update (int.nat diskEnd) with "diskEnd_mem") as "[[[diskEnd_mem11 diskEnd_mem12] diskEnd_mem2] #diskEnd_mem_lb]"; first by lia.
iMod (mono_nat_own_update diskEnd_txn_id with "diskEnd_mem_txn_id") as "[[[diskEnd_mem_txn_id11 diskEnd_mem_txn_id12] diskEnd_mem_txn_id2] #diskEnd_mem_txn_lb]"; first by lia.
iMod ("Hwand" $! σ σ' with "[//] HP") as "(HPrec&HPcrash)".
iSplitR "start_avail diskEnd_avail
being_installed_start_txn2
installer_pos2 installer_txn_id2
logger_pos2 logger_txn_id2
installer_pos_mem2 installer_txn_id_mem2
already_installed2
installed_pos_mem2 installed_txn_id_mem2
diskEnd2 diskEnd_txn_id2
diskEnd_mem2 diskEnd_mem_txn_id2
Happender
".
eapply log_crash_to_wf; eauto.
by eapply log_crash_to_post_crash.
(* we proved this before with [memLog_linv_nextDiskEnd_txn_id_post_crash] *)
rewrite /wal_linv_durable.
iExists {| diskEnd := diskEnd;
locked_diskEnd_txn_id := diskEnd_txn_id;
memLog := {| slidingM.log := cs0.(circΣ.upds);
slidingM.start := cs0.(circΣ.start);
slidingM.mutable := U64 (circΣ.diskEnd cs0);
|}
|}.
replace (U64 (int.Z diskEnd)) with diskEnd by word.
iFrame "HdiskEnd_linv HdiskStart_linv".
rewrite /memLog_linv /memLog_linv_core.
iSplitL "installer_pos_mem1 installer_txn_id_mem1
logger_pos1 logger_txn_id1
diskEnd_mem11 diskEnd_mem_txn_id11
installed_pos_mem1 installed_txn_id_mem1
Htxns1 HnextDiskEnd_linv".
rewrite /locked_wf/=; split_and!; try word.
rewrite /circ_wf in Hcirc_wf.
rewrite /slidingM.wf/=; split_and!; try word.
iExists installed_txn_id, _, _, _, _, _, _.
rewrite /memLog_linv_pers_core /=.
replace (U64 (circΣ.diskEnd cs0)) with diskEnd in * by word.
rewrite HdiskEnd_val /circΣ.diskEnd.
iFrame "Hinstalled_pos'".
iFrame "HdiskEnd_stable".
replace (S (_ `max` _) `min` _)%nat with
(S (σ.(log_state.durable_lb) `max` diskEnd_txn_id))%nat;
last by lia.
replace (slidingM.endPos _) with diskEnd.
rewrite /slidingM.endPos /=.
iFrame (HdiskEnd_is_txn) "being_installed_start_txn_id_mem_lb".
destruct (decide
(σ.(log_state.durable_lb) ≤ diskEnd_txn_id)%nat
).
rewrite Nat.max_r; last by lia.
rewrite Nat.max_l; last by lia.
iApply "Hdurable_lb_pos".
replace (slidingM.memEnd _) with (int.Z diskEnd) by reflexivity.
apply circ_matches_txns_combine in Hcirc_matches.
rewrite /memLog_linv_txns /slidingM.logIndex /mwrb.logend /=
take_length Nat.sub_diag.
replace (int.nat diskEnd - _)%nat with (length cs0.(circΣ.upds)).
rewrite /slidingM.logIndex /=.
unfold circΣ.diskEnd in *.
rewrite Nat.min_l; last by lia.
rewrite /is_memLog_boundaries take_length Nat.min_l; last by lia.
apply elem_of_cons in Hbndry.
destruct Hbndry as [->|Hbndry]; first by (simpl; lia).
apply elem_of_cons in Hbndry.
destruct Hbndry as [->|Hbndry]; first by (simpl; lia).
apply elem_of_cons in Hbndry.
destruct Hbndry as [->|Hbndry]; first by (simpl; lia).
apply elem_of_cons in Hbndry.
destruct Hbndry as [->|Hbndry]; first by (simpl; lia).
apply elem_of_cons in Hbndry.
destruct Hbndry as [->|Hbndry]; first by (simpl; lia).
apply elem_of_cons in Hbndry.
destruct Hbndry as [->|Hbndry]; first by (simpl; lia).
intros i bndry1 bndry2 Hbndry1 Hbndry2.
do 3 (destruct i; first by (
simpl in Hbndry1; inversion Hbndry1; subst bndry1; clear Hbndry1;
simpl in Hbndry2; inversion Hbndry2; subst bndry2; clear Hbndry2;
simpl; (split; first by lia); (split; first by lia);
rewrite ?subslice_zero_length -?subslice_complete;
try (rewrite subslice_take_all; last by lia);
try apply is_memLog_region_nil;
try apply Hcirc_matches
)).
destruct i; last by inversion Hbndry2.
simpl in Hbndry1; inversion Hbndry1; subst bndry1; clear Hbndry1;
simpl in Hbndry2; inversion Hbndry2; subst bndry2; clear Hbndry2;
simpl; (split; first by lia); (split; first by lia);
rewrite ?subslice_zero_length -?subslice_complete;
apply is_memLog_region_nil.
simpl in Hbndry1; inversion Hbndry1; subst bndry1; clear Hbndry1;
simpl in Hbndry2; inversion Hbndry2; subst bndry2; clear Hbndry2.
rewrite subslice_take Nat.min_id.
destruct (decide (σ.(log_state.durable_lb) ≤ diskEnd_txn_id)).
rewrite Nat.max_r; last by lia.
rewrite !subslice_zero_length.
apply is_memLog_region_nil.
rewrite Nat.max_l; last by lia.
rewrite !subslice_zero_length.
apply is_memLog_region_nils.
rewrite -(subslice_app_contig _ (S diskEnd_txn_id))
in Hdurable_nils; last by lia.
apply Forall_app in Hdurable_nils.
replace (take _ _) with (σ'.(log_state.txns)) by reflexivity.
apply txns_mono_lt_last; eauto using log_crash_to_wf.
rewrite Nat.min_l; last by lia.
iExists installed_txn_id, installer_txn_id, diskEnd_txn_id; simpl.
assert (installed_txn_id <= diskEnd_txn_id) by word.
iSplitL "being_installed_start_txn1
already_installed1 Hold_txns Hdata".
rewrite /is_installed/is_installed_core.
iFrame "being_installed_start_txn1".
iFrame "already_installed1".
iDestruct (old_txns_are_get with "Hold_txns Hbeing_installed_txns") as "#Hbeing_installed_txns'".
rewrite subslice_length; len.
iExactEq "Hbeing_installed_txns'".
rewrite subslice_take_all //.
iApply (big_sepM_mono with "Hdata").
iIntros (k x Hlookup) "H".
rewrite /is_dblock_with_txns.
iDestruct "H" as (b txn_id' Hinstalled) "(?&?)".
iDestruct (is_base_disk_crash with "Hbasedisk") as "$".
iDestruct (is_installed_txn_crash γ γ' with "circ.start Hinstalled_txn_id_stable") as "$"; first by lia.
efeed pose proof (circ_matches_txns_crash (int.nat diskEnd)) as Hcirc_matches'; [ | by eauto | ].
iExists _, _, _; iFrame (Hlog_wf) "∗".
apply (is_memLog_boundaries_append_txns _ _ _ (
subslice
(S (diskEnd_txn_id))
(S (σ.(log_state.durable_lb) `max` diskEnd_txn_id))%nat
σ.(log_state.txns)
)) in Hcirc_matches'.
rewrite subslice_from_take subslice_app_contig in Hcirc_matches';
last by lia.
rewrite subslice_from_start in Hcirc_matches'.
rewrite /wal_resources /logger_resources.
iDestruct "diskEnd_mem_txn_lb" as "-#diskEnd_mem_txn_lb".
iDestruct (mono_nat_lb_own_le installer_txn_id with "diskEnd_mem_txn_lb") as
"diskEnd_mem_txn_lb"; first by word.
iFrame "start_avail diskEnd_avail".
repeat first [ iExists _ |
rewrite sep_exist_l |
rewrite sep_exist_r ].
replace (U64 (int.Z diskEnd)) with diskEnd by word.
rewrite /wal_init_ghost_state.
iMod ("Hcfupd" with "[$]") as (s0 s1) "(>%&>?&>?&?)".
Lemma txns_ctx_gname_eq γ γ' txns :
txns_ctx_name γ = txns_ctx_name γ' →
txns_ctx γ txns = txns_ctx γ' txns.
rewrite /txns_ctx/txn_val => -> //=.
Ltac show_crash1 := crash_case; eauto.
Ltac show_crash2 :=
try (crash_case);
iSplitL ""; first auto;
iSplitL ""; first auto;
iFrame; iExists _; iFrame; iExists _, _; iFrame "∗ #".
Global Instance txns_ctx_disc γ x:
Discretizable (txns_ctx γ x).
Global Instance is_wal_inner_durable_disc γ s:
Discretizable (is_wal_inner_durable γ s dinit).
Global Instance disk_inv_disc γ σ cs:
Discretizable (disk_inv γ σ cs dinit).
(* halt at σ0 ~~> σ1 ~recovery, crashes~> σ1 *)
Hint Unfold circΣ.diskEnd : word.
Lemma wal_post_crash_durable_lb:
∀ σ : log_state.t,
wal_post_crash σ
→ ∀ (cs : circΣ.t) (diskEnd : u64) (installed_txn_id diskEnd_txn_id : nat),
is_txn σ.(log_state.txns) diskEnd_txn_id diskEnd
→ (σ.(log_state.durable_lb) ≤ diskEnd_txn_id)%nat
→ diskEnd_txn_id = (length σ.(log_state.txns) - 1)%nat.
intros σ Hpostcrash cs diskEnd installed_txn_id diskEnd_txn_id Hend_txn Hdurable.
rewrite /wal_post_crash in Hpostcrash.
apply is_txn_bound in Hend_txn.
Lemma wal_post_crash_durable_lb_length_txns:
∀ σ : log_state.t,
wal_post_crash σ →
σ.(log_state.durable_lb) = (length σ.(log_state.txns) - 1)%nat.
rewrite /wal_post_crash in Hpostcrash.
Theorem wpc_mkLog_recover d γ σ :
{{{ is_wal_inner_durable γ σ dinit ∗ wal_resources γ }}}
mkLog (disk_val d) @ ⊤
{{{ l, RET #l;
"Hwal_inv_pre" ∷ is_wal_inv_pre l γ σ dinit ∗
"Hlogger" ∷ (∃ (circ_l: loc), "#Hcirc2" ∷ readonly (l ↦[Walog :: "circ"] #circ_l) ∗
logger_inv γ circ_l) ∗
"Hinstaller" ∷ installer_inv γ
}}}
{{{ is_wal_inner_durable γ σ dinit ∗ wal_resources γ }}}.
iIntros (Φ Φc) "(Hcs&Hwalres) HΦ".
try (crash_case); iFrame.
iDestruct "Hwalres" as "(Hlogger&Hinstaller)".
rewrite /logger_resources.
wpc_bind (recoverCircular _).
wpc_apply (wpc_recoverCircular with "[$]").
iIntros "(Hcirc&Happend)".
iSplitR "
Happend HnotLogging
HownLoggerPos_logger HownLoggerTxn_logger
HownDiskEnd_logger HownDiskEndTxn_logger
HownDiskEndMem_logger HownDiskEndMemTxn_logger
Hinstaller
".
rename diskEnd into oldDiskEnd.
iIntros "!>" (c diskStart diskEnd bufSlice upds).
iIntros "(Hupd_slice&Hcirc&Happender&%&%&%)".
iDestruct (is_circular_state_wf with "Hcirc") as %Hwf_circ.
set (memLog := {|
slidingM.log := upds;
slidingM.start := diskStart;
slidingM.mutable := int.Z diskStart + length upds |}).
wpc_frame "Hwal_linv Hinstalled HΦ Hcirc Happender HnotLogging
HownLoggerPos_logger HownLoggerTxn_logger
HownDiskEnd_logger HownDiskEndTxn_logger
HownDiskEndMem_logger HownDiskEndMemTxn_logger
Hdurable Hinstaller Howncs Htxns_ctx γtxns HnextDiskEnd_inv".
iDestruct "Happender" as (????) "(Haddrs&Hblocks&?)".
rewrite /is_wal_inner_durable.
iFrame "Htxns_ctx γtxns HnextDiskEnd_inv".
iSplitR "Haddrs Hblocks HnotLogging
HownLoggerPos_logger HownLoggerTxn_logger
HownDiskEnd_logger HownDiskEndTxn_logger
HownDiskEndMem_logger HownDiskEndMemTxn_logger
Hinstaller".
wp_apply (wp_new_free_lock); iIntros (ml) "Hlock".
iDestruct (updates_slice_cap_acc with "Hupd_slice") as "[Hupd_slice Hupds_cap]".
wp_apply (wp_mkSliding _ 1 with "[$]").
destruct Hwf_circ as (?&?).
iIntros (lslide) "Hsliding".
iDestruct (is_sliding_wf with "[$]") as %Hsliding_wf.
wp_apply wp_allocStruct; first val_ty.
iIntros (st) "Hwal_state".
wp_apply (wp_newCond' with "[$]").
iIntros (condLogger) "(Hlock&#cond_logger)".
wp_apply (wp_newCond' with "[$]").
iIntros (condInstall) "(Hlock&#cond_install)".
wp_apply (wp_newCond' with "[$]").
iIntros (condShut) "(Hlock&#cond_shut)".
wp_apply (util_proof.wp_DPrintf).
iApply struct_fields_split in "Hl".
iMod (readonly_alloc_1 with "memLock") as "#memLock".
iMod (readonly_alloc_1 with "d") as "#d".
iMod (readonly_alloc_1 with "circ") as "#circ".
iMod (readonly_alloc_1 with "st") as "#st".
iMod (readonly_alloc_1 with "condLogger") as "#condLogger".
iMod (readonly_alloc_1 with "condInstall") as "#condInstall".
iMod (readonly_alloc_1 with "condShut") as "#condShut".
iMod (alloc_lock walN _ _ (wal_linv st γ)
with "[$] [Hwal_state Hwal_linv Hsliding]") as "#lk".
rewrite /wal_linv_durable.
iDestruct "Hwal_linv" as (σls Heq1 Heq2 Hlocked_wf) "Hwal_linv".
rewrite /wal_linv_fields.
iExists {| memLogPtr := _; shutdown := _; nthread := _ |}.
iDestruct (struct_fields_split with "Hwal_state") as "Hwal_state".
iSplitL "Hcirc Hinstalled Hdurable Howncs Htxns_ctx γtxns HnextDiskEnd_inv".
iDestruct "Howncs" as "(Howncs1&Howncs2)".
iSplitR "Hcirc Howncs1"; last first.
iExists {| memLock := _; wal_d := _; circ := _; wal_st := _; condLogger := _;
condInstall := _; condShut := _ |}.
iSplitL "
Happender HnotLogging
HownLoggerPos_logger HownLoggerTxn_logger
HownDiskEnd_logger HownDiskEndTxn_logger
HownDiskEndMem_logger HownDiskEndMemTxn_logger
".
Definition wal_cfupd_cancel E γ' Prec : iProp Σ :=
(<bdisc> (|C={E}=> ∃ s s', ⌜relation.denote log_crash s s' tt⌝ ∗
is_wal_inner_durable γ' s' dinit ∗
wal_resources γ' ∗ ▷ Prec s s')).
Definition wal_cinv E γ γ' Pcrash : iProp Σ :=
□ (|C={E}=> inv (N.@"wal") (∃ s s',
⌜relation.denote log_crash s s' tt⌝ ∗
is_wal_inner_crash γ s ∗
wal_ghost_exchange γ γ' ∗
Pcrash s s')).
Lemma post_crash_crash_refl σ :
wal_post_crash σ →
relation.denote log_crash σ σ ().
exists σ (σ.(log_state.durable_lb)).
rewrite Heq firstn_all //.
Theorem wpc_MkLog_recover E d γ σ Prec Pcrash (Hpostcrash: wal_post_crash σ):
↑walN ⊆ E →
□ (∀ s s' (Hcrash: relation.denote log_crash s s' ()),
▷ P s -∗ |={E ∖ ↑N.@"wal"}=> ▷ Prec s s' ∗ ▷ Pcrash s s') -∗
{{{ is_wal_inner_durable γ σ dinit ∗ wal_resources γ ∗ ▷ P σ }}}
MkLog (disk_val d) @ ⊤
{{{ γ' l, RET #l;
is_wal P l γ dinit ∗
wal_cfupd_cancel E γ' Prec ∗
wal_cinv E γ γ' Pcrash
}}}
{{{ (∃ γ' σ0 σ', ⌜relation.denote log_crash σ0 σ' tt⌝ ∗
is_wal_inner_durable γ' σ' dinit ∗ wal_resources γ' ∗ ▷ Prec σ0 σ') }}}.
iIntros "!>" (Φ Φc) "(Hdurable&Hres&HP) HΦ".
iSpecialize ("Hwand" with "[] [$]").
by apply post_crash_crash_refl.
iMod (fupd_mask_mono with "Hwand") as "(Hrec&Hcrash)".
iFrame "Hdurable Hres Hrec".
by apply post_crash_crash_refl.
wpc_apply (wpc_mkLog_recover with "[$]").
iIntros "(Hdurable&Hres)".
iSpecialize ("Hwand" with "[] [$]").
by apply post_crash_crash_refl.
iMod (fupd_mask_mono with "Hwand") as "(Hrec&Hcrash)".
iFrame "Hdurable Hres Hrec".
by apply post_crash_crash_refl.
iMod (wal_crash_obligation_alt E with "Hwal_inv_pre Hwand HP") as (γ') "(#His_wal&Hcancel&#Hcinv)".
iApply (wpc_crash_frame_wand_bdisc with "[-Hcancel] Hcancel"); auto; try lia.
iApply (and_mono with "HΦ"); last done.
iDestruct "H2" as (s0 s1 Hcrash) "(Hinner&Hres&Hrec)".
rewrite /Walog__startBackgroundThreads.
wp_apply (wp_fork with "[Hlogger]").
iDestruct "Hlogger" as (?) "(Hro&Hlogger)".
by wp_apply (wp_Walog__logger with "[$]").
wp_apply (wp_fork with "[Hinstaller]").
by wp_apply (wp_Walog__installer with "[$]").
Local Instance ghost_var_into_crash {A} `{ghost_varG Σ A} (γ: gname) q (x: A):
IntoCrash (ghost_var γ q x) (λ _, ghost_var γ q x).
Local Instance mono_nat_auth_own_into_crash `{mono_natG Σ} (γ: gname) q (x: nat):
IntoCrash (mono_nat_auth_own γ q x) (λ _, mono_nat_auth_own γ q x).
Local Instance txns_are_into_crash γ n l:
IntoCrash (txns_are γ n l) (λ _, txns_are γ n l).
Instance is_installed_stable γ d txns installed_txn_id installer_txn_id diskEnd_txn_id :
IntoCrash (is_installed γ d txns installed_txn_id installer_txn_id diskEnd_txn_id)
(λ _, is_installed γ d txns installed_txn_id installer_txn_id diskEnd_txn_id).
iDestruct "Hbeing_installed_txns" as "-#Hbeing_installed_txns".
Instance disk_inv_stable γ s cs dinit:
IntoCrash (disk_inv γ s cs dinit) (λ _, disk_inv γ s cs dinit).
iDestruct (post_crash_nodep with "circ.start") as "-#circ.start'".
iDestruct (post_crash_nodep with "circ.end") as "-#circ.end'".
iDestruct (post_crash_nodep with "Hdurable") as "Hdurable".
iDestruct (post_crash_nodep with "Hbasedisk") as "-#Hbasedisk'".
Instance is_wal_inner_durable_stable γ s dinit:
IntoCrash (is_wal_inner_durable γ s dinit) (λ _, is_wal_inner_durable γ s dinit).
iDestruct (post_crash_nodep with "HnextDiskEnd_inv") as "HnextDiskEnd_inv".
iDestruct (post_crash_nodep with "Htxns_ctx") as "Htxns_ctx".
iDestruct (post_crash_nodep with "Hwal_linv") as "Hwal_linv".
(* this is not interesting on its own, but is just a test to make sure that the
wal spec is coherent *)
(* Just a simple example of using idempotence *)
Theorem wpr_MkLog d γ s dinit:
wal_post_crash s →
is_wal_inner_durable γ s dinit -∗
wal_resources γ -∗
wpr NotStuck ⊤
(MkLog (disk_val d))
(MkLog (disk_val d))
(λ _, True%I)
(λ _, True%I)
(λ _ _, True%I).
iIntros (?) "His_wal_inner_durable Hres".
iApply (idempotence_wpr NotStuck ⊤ _ _ (λ _, True)%I (λ _, True)%I (λ _ _, True)%I
(λ _, ∃ σ' γ' (Hpost: wal_post_crash σ'),
is_wal_inner_durable γ' σ' dinit ∗ wal_resources γ' ∗ ▷ True)%I
with "[His_wal_inner_durable Hres] []").
wpc_apply (wpc_MkLog_recover dinit (λ _, True)%I _ _ _ _ (λ _ _, True)%I (λ _ _, True)%I
with "[] [$His_wal_inner_durable $Hres]"); auto 10.
iDestruct 1 as (????) "(?&?&_)".
unshelve (iExists _, _, _; iFrame; eauto).
eapply log_crash_to_post_crash; eauto.
(* FIXME: we bundle local and global, but [idempotence_wpr] quantifies only over the local part...
and we want the terms below to pick up the new local part. Sadly the let-binding in the
statement of [idempotence_wpr] is lost so we have to introduce it again ourselves here. *)
set (hG' := HeapGS _ _ _).
iDestruct "H" as (σ'') "Hstart".
iDestruct "Hstart" as (??) "(H1&Hres&_)".
rewrite is_wal_inner_durable_stable.
iDestruct (post_crash_nodep with "Hres") as "Hres".
wpc_apply (wpc_MkLog_recover dinit (λ _, True)%I _ _ _ _ (λ _ _, True)%I (λ _ _, True)%I
with "[] [$H1 $Hres]"); auto 10.
iDestruct 1 as (????) "(?&?&_)".
unshelve (iExists _, _, _; iFrame; eauto).
eapply log_crash_to_post_crash; eauto.