Section LockServ.
Variable num_Clients : nat.
Definition Client_index := (fin num_Clients).
Inductive Name :=
| Client : Client_index -> Name
| Server : Name.
Definition list_Clients := map Client (all_fin num_Clients).
Definition Name_eq_dec : forall a b : Name, {a = b} + {a <> b}.
decide equality. apply fin_eq_dec.
Qed.
Inductive Msg :=
| Lock   : Msg
| Unlock : Msg
| Locked : Msg.
Definition Msg_eq_dec : forall a b : Msg, {a = b} + {a <> b}.
decide equality.
Qed.
Definition Input := Msg.
Definition Output := Msg.
Record Data := mkData { queue : list Client_index ; held : bool }.
Definition init_data (n : Name) : Data := mkData [] false.
Definition Handler (S : Type) := GenHandler (Name * Msg) S Output unit.
Definition ClientNetHandler (i : Client_index) (m : Msg) : Handler Data :=
match m with
| Locked => (put (mkData [] true)) >> write_output Locked
| _ => nop
end.
Definition ClientIOHandler (i : Client_index) (m : Msg) : Handler Data :=
match m with
| Lock => send (Server, Lock)
| Unlock => data <- get ;;
when (held data) (put (mkData [] false) >> send (Server, Unlock))
| _ => nop
end.
Definition ServerNetHandler (src : Name) (m : Msg) : Handler Data :=
st <- get ;;
let q := queue st in
match m with
| Lock =>
match src with
| Server => nop
| Client c =>
when (null q) (send (src, Locked)) >> put (mkData (q++[c]) (held st))
end
| Unlock => match q with
| _ :: x :: xs => put (mkData (x :: xs) (held st)) >> send (Client x, Locked)
| _ => put (mkData [] (held st))
end
| _ => nop
end.
Definition ServerIOHandler (m : Msg) : Handler Data := nop.
Definition NetHandler (nm src : Name) (m : Msg) : Handler Data :=
match nm with
| Client c => ClientNetHandler c m
| Server   => ServerNetHandler src m
end.
Definition InputHandler (nm : Name) (m : Msg) : Handler Data :=
match nm with
| Client c => ClientIOHandler c m
| Server   => ServerIOHandler m
end.
Ltac handler_unfold :=
repeat (monad_unfold; unfold NetHandler,
InputHandler,
ServerNetHandler,
ClientNetHandler,
ClientIOHandler,
ServerIOHandler in *).
Definition Nodes := Server :: list_Clients.
Proof using.
intros.
unfold Nodes, list_Clients.
simpl.
destruct n.
- right.
apply in_map.
apply all_fin_all.
- left.
reflexivity.
Qed.
Proof using.
unfold Nodes, list_Clients.
apply NoDup_cons.
- in_crush. discriminate.
- apply NoDup_map_injective.
+ intros. congruence.
+ apply all_fin_NoDup.
Qed.
Global Instance LockServ_BaseParams : BaseParams :=
{
data   := Data ;
input  := Input ;
output := Output
}.
Global Instance LockServ_MultiParams : MultiParams LockServ_BaseParams :=
{
name := Name ;
msg  := Msg ;
msg_eq_dec := Msg_eq_dec ;
name_eq_dec := Name_eq_dec ;
nodes := Nodes ;
all_names_nodes := In_n_Nodes ;
no_dup_nodes := nodup ;
init_handlers := init_data ;
net_handlers := fun dst src msg s =>
runGenHandler_ignore s (NetHandler dst src msg) ;
input_handlers := fun nm msg s =>
runGenHandler_ignore s (InputHandler nm msg)
}.
(* This is the fundamental safety property of the system: No two different clients can (think they) hold the lock at once. *)
Definition mutual_exclusion (sigma : name -> data) : Prop :=
forall m n,
held (sigma (Client m)) = true ->
held (sigma (Client n)) = true ->
m = n.
(* The system enforces mutual exclusion at the server. Whenever a client believs it holds the lock, that client is at the head of the server's queue. *)
Definition locks_correct (sigma : name -> data) : Prop :=
forall n,
held (sigma (Client n)) = true ->
exists t,
queue (sigma Server) = n :: t.
Proof using.
unfold locks_correct, mutual_exclusion.
intros.
repeat find_apply_hyp_hyp.
break_exists.
find_rewrite. find_inversion.
auto.
Qed.
Definition valid_unlock q h c p :=
pSrc p = Client c /\
(exists t, q = c :: t) /\
h = false.
Definition locks_correct_unlock (sigma : name -> data) (p : packet) : Prop :=
pBody p = Unlock ->
exists c, valid_unlock (queue (sigma Server)) (held (sigma (Client c))) c p.
Definition valid_locked q h c p :=
pDst p = Client c /\
(exists t, q = c :: t) /\
h = false.
Definition locks_correct_locked (sigma : name -> data) (p : packet) : Prop :=
pBody p = Locked ->
exists c, valid_locked (queue (sigma Server)) (held (sigma (Client c))) c p.
Definition LockServ_network_invariant (sigma : name -> data) (p : packet) : Prop :=
locks_correct_unlock sigma p /\
locks_correct_locked sigma p.
Definition LockServ_network_network_invariant (p q : packet) : Prop :=
(pBody p = Unlock -> pBody q = Unlock -> False) /\
(pBody p = Locked -> pBody q = Unlock -> False) /\
(pBody p = Unlock -> pBody q = Locked -> False) /\
(pBody p = Locked -> pBody q = Locked -> False).
Proof using.
unfold LockServ_network_network_invariant.
intuition.
Qed.
Proof using.
unfold locks_correct. simpl. discriminate.
Qed.
Proof using.
handler_unfold.
intros.
repeat break_match; repeat tuple_inversion;
subst; simpl in *; subst; simpl in *.
- left. eexists. intuition.
- left. eexists. intuition.
- left. eexists. intuition.
- auto.
- auto.
Qed.
Proof using.
unfold locks_correct.
intuition.
destruct (Name_eq_dec (Client x) (Client n)).
- find_inversion. exfalso.
rewrite_update.
congruence.
- rewrite_update.
auto.
Qed.
Ltac set_up_input_handlers :=
intros;
find_apply_lem_hyp InputHandler_cases;
intuition idtac; try break_exists; intuition idtac; subst;
repeat find_rewrite;
simpl in *; intuition idtac; repeat find_inversion;
try now rewrite update_nop_ext.
Proof using.
set_up_input_handlers;
auto using locks_correct_update_false.
Qed.
Proof using.
handler_unfold.
intros.
repeat break_match; repeat tuple_inversion; subst; auto.
Qed.
Proof using.
handler_unfold.
intros.
repeat break_match; repeat tuple_inversion; subst.
- find_apply_lem_hyp null_sound. find_rewrite. simpl.
intuition. left. eexists. intuition.
- simpl. find_apply_lem_hyp null_false_neq_nil.
intuition. left. eexists. intuition.
- simpl. auto.
- simpl. destruct st; simpl in *; subst; auto.
- simpl in *. intuition.
- simpl in *. intuition eauto.
- simpl. intuition.
Qed.
Definition at_head_of_queue sigma c := (exists t, queue (sigma Server) = c :: t).
Proof using.
unfold at_head_of_queue.
firstorder.
Qed.
Proof using.
unfold locks_correct.
intros.
destruct (Name_eq_dec (Client c) (Client n)); rewrite_update; try find_inversion; auto.
Qed.

Lemma locks_correct_locked_at_head :
forall sigma p c,
pDst p = Client c ->
pBody p = Locked ->
locks_correct_locked sigma p ->
at_head_of_queue sigma c.