Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LockServ with SeqNum VST applied #2

Merged
merged 6 commits into from
Feb 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,34 @@ default: Makefile.coq
+$(MAKE) -C extraction/lockserv

LOCKSERV_MLFILES = extraction/lockserv/ocaml/LockServ.ml extraction/lockserv/ocaml/LockServ.mli
LOCKSERV_SEQNUM_MLFILES = extraction/lockserv-seqnum/ocaml/LockServSeqNum.ml extraction/lockserv-seqnum/ocaml/LockServSeqNum.mli

Makefile.coq: _CoqProject
coq_makefile -f _CoqProject -o Makefile.coq -no-install \
-extra '$(LOCKSERV_MLFILES)' \
'extraction/lockserv/coq/ExtractLockServ.v systems/LockServ.vo' \
'$$(COQC) $$(COQDEBUG) $$(COQFLAGS) extraction/lockserv/coq/ExtractLockServ.v' \
-extra '$(LOCKSERV_SEQNUM_MLFILES)' \
'extraction/lockserv-seqnum/coq/ExtractLockServSeqNum.v systems/LockServSeqNum.vo' \
'$$(COQC) $$(COQDEBUG) $$(COQFLAGS) extraction/lockserv-seqnum/coq/ExtractLockServSeqNum.v' \
-extra-phony 'distclean' 'clean' \
'rm -f $$(join $$(dir $$(VFILES)),$$(addprefix .,$$(notdir $$(patsubst %.v,%.aux,$$(VFILES)))))'

$(LOCKSERV_MLFILES): Makefile.coq
$(LOCKSERV_MLFILES) $(LOCKSERV_SEQNUM_MLFILES): Makefile.coq
$(MAKE) -f Makefile.coq $@

lockserv:
+$(MAKE) -C extraction/lockserv

lockserv-seqnum:
+$(MAKE) -C extraction/lockserv-seqnum

clean:
if [ -f Makefile.coq ]; then \
$(MAKE) -f Makefile.coq distclean; fi
rm -f Makefile.coq
$(MAKE) -C extraction/lockserv clean
$(MAKE) -C extraction/lockserv-seqnum clean

lint:
@echo "Possible use of hypothesis names:"
Expand All @@ -49,4 +57,4 @@ lint:
distclean: clean
rm -f _CoqProject

.PHONY: default clean lint $(LOCKSERV_MLFILES) lockserv
.PHONY: default clean lint $(LOCKSERV_MLFILES) $(LOCKSERV_SEQNUM_MLFILES) lockserv lockserv-seqnum
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Verdi LockServ

[![Build Status](https://api.travis-ci.org/DistributedComponents/verdi-lockserv.svg?branch=master)](https://travis-ci.org/DistributedComponents/verdi-lockserv)

An implementation of a lock server, verified in Coq using the Verdi framework.
An implementation of a simple asynchronous lock server, verified in Coq using the Verdi framework to achieve mutual exclusion.

Requirements
------------
Expand Down Expand Up @@ -36,7 +36,7 @@ Then, run `./configure` in the root directory. This will check for the appropri

By default, the script assumes that `Verdi` and `StructTact` are installed in Coq's `user-contrib` directory, but this can be overridden by setting the `Verdi_PATH` and `StructTact_PATH` environment variables.

Finally, run `make` in the root directory. This will compile the lock server definitions, check the proofs, and finally build an OCaml program from the extracted code called called `LockServMain` in the `extraction/lockserv` directory.
Finally, run `make` in the root directory. This will compile the lock server definitions, check the proofs of mutual exclusion, and finally build an OCaml program from the extracted code called called `LockServMain` in the `extraction/lockserv` directory.

Running LockServ on a cluster
-----------------------------
Expand Down Expand Up @@ -75,3 +75,10 @@ There is a simple client in the directory `extraction/lockserv/script` that can
>>> c.send_lock()
'Locked'
>>> c.send_unlock()

LockServ with Sequence Numbering
--------------------------------

As originally defined, the lock server does not tolerate duplicate messages, which means that `LockServMain` can potentially give unexpected results when the underlying UDP-based runtime system generates duplicates. However, the Verdi framework defines a sequence numbering verified system transformer that when applied allows systems to ignore duplicate messages, while still guaranteeing mutual exclusion.

The directory `extraction/lockserv-seqnum` contains the files needed to produce an OCaml program called `LockServSeqNumMain` which uses the sequence numbering transformer. After running `./configure` in the root directory, simply run `make` in this directory to compile the program. `LockServSeqNumMain` has the same command-line option as `LockServMain`, and the python client can be used to interface with nodes in both kinds of clusters.
2 changes: 1 addition & 1 deletion configure
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Configuration options for coqproject.sh
DEPS=(Verdi StructTact)
DIRS=(systems extraction/lockserv/coq)
DIRS=(systems extraction/lockserv/coq extraction/lockserv-seqnum/coq)
CANARIES=("Verdi.Verdi" "Build Verdi before building Verdi LockServ" "StructTact.StructTactics" "Build StructTact before building Verdi LockServ")
Verdi_DIRS=(core lib systems extraction)
source script/coqproject.sh
4 changes: 4 additions & 0 deletions extraction/lockserv-seqnum/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ocaml/LockServSeqNum.ml
ocaml/LockServSeqNum.mli
_build
LockServSeqNumMain.native
21 changes: 21 additions & 0 deletions extraction/lockserv-seqnum/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
OCAMLBUILD = ocamlbuild -use-ocamlfind -tag thread -package verdi-runtime -I ocaml -cflag -g

MLEXTRACTED = ocaml/LockServSeqNum.ml ocaml/LockServSeqNum.mli

MLFILES = ocaml/LockServSeqNumArrangement.ml ocaml/LockServSeqNumSerialization.ml \
ocaml/LockServSeqNumMain.ml ocaml/LockServSeqNumOpts.ml

default: LockServSeqNumMain.native

$(MLEXTRACTED):
+$(MAKE) -C ../.. extraction/lockserv-seqnum/$@

LockServSeqNumMain.native: $(MLEXTRACTED) $(MLFILES)
$(OCAMLBUILD) LockServSeqNumMain.native

clean:
$(OCAMLBUILD) -clean

.PHONY: default clean $(MLEXTRACTED)

.NOTPARALLEL: $(MLEXTRACTED)
12 changes: 12 additions & 0 deletions extraction/lockserv-seqnum/coq/ExtractLockServSeqNum.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Require Import Verdi.Verdi.

Require Import LockServSeqNum.

Require Import ExtrOcamlBasic.
Require Import ExtrOcamlNatInt.

Require Import Verdi.ExtrOcamlBasicExt.
Require Import Verdi.ExtrOcamlList.
Require Import Verdi.ExtrOcamlFin.

Extraction "extraction/lockserv-seqnum/ocaml/LockServSeqNum.ml" seq transformed_base_params transformed_multi_params.
68 changes: 68 additions & 0 deletions extraction/lockserv-seqnum/ocaml/LockServSeqNumArrangement.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module type IntValue = sig
val v : int
end

module type Params = sig
val debug : bool
val num_clients : int
end

module LockServSeqNumArrangement (P : Params) = struct
type name = LockServSeqNum.name0
type state = LockServSeqNum.seq_num_data
type input = LockServSeqNum.msg0
type output = LockServSeqNum.msg0
type msg = LockServSeqNum.seq_num_msg
type client_id = int
type res = (output list * state) * ((name * msg) list)
type task_handler = name -> state -> res
type timeout_setter = name -> state -> float

let systemName = "Lock Server with Sequence Numbering"

let serializeName = LockServSeqNumSerialization.serializeName

let deserializeName = LockServSeqNumSerialization.deserializeName

let init = fun n ->
let open LockServSeqNum in
Obj.magic ((transformed_multi_params P.num_clients).init_handlers (Obj.magic n))

let handleIO = fun n i s ->
let open LockServSeqNum in
Obj.magic ((transformed_multi_params P.num_clients).input_handlers (Obj.magic n) (Obj.magic i) (Obj.magic s))

let handleNet = fun dst src m s ->
let open LockServSeqNum in
Obj.magic ((transformed_multi_params P.num_clients).net_handlers (Obj.magic dst) (Obj.magic src) (Obj.magic m) (Obj.magic s))

let deserializeMsg = LockServSeqNumSerialization.deserializeMsg

let serializeMsg = LockServSeqNumSerialization.serializeMsg

let deserializeInput = LockServSeqNumSerialization.deserializeInput

let serializeOutput = LockServSeqNumSerialization.serializeOutput

let debug = P.debug

let debugInput = fun _ inp ->
Printf.printf "[%s] got input %s" (Util.timestamp ()) (LockServSeqNumSerialization.debugSerializeInput inp);
print_newline ()

let debugRecv = fun _ (nm, msg) ->
Printf.printf "[%s] receiving message %s from %s" (Util.timestamp ()) (LockServSeqNumSerialization.debugSerializeMsg msg) (serializeName nm);
print_newline ()

let debugSend = fun _ (nm, msg) ->
Printf.printf "[%s] sending message %s to %s" (Util.timestamp ()) (LockServSeqNumSerialization.debugSerializeMsg msg) (serializeName nm);
print_newline ()

let createClientId () =
let upper_bound = 1073741823 in
Random.int upper_bound

let serializeClientId = string_of_int

let timeoutTasks = []
end
34 changes: 34 additions & 0 deletions extraction/lockserv-seqnum/ocaml/LockServSeqNumMain.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
open LockServSeqNumOpts
open LockServSeqNumArrangement

let () =
let () =
try
parse Sys.argv
with
| Arg.Help msg ->
Printf.printf "%s: %s" Sys.argv.(0) msg;
exit 2
| Arg.Bad msg ->
Printf.eprintf "%s" msg;
exit 2
in
let () =
try
validate ()
with Arg.Bad msg ->
Printf.eprintf "%s: %s." Sys.argv.(0) msg;
prerr_newline ();
exit 2
in
let module Pms = struct
let debug = !debug
let num_clients = List.length !cluster - 1
end in
let module Arrangement = LockServSeqNumArrangement (Pms) in
let module Shim = UnorderedShim.Shim (Arrangement) in
let open Shim in
main { cluster = !cluster
; me = !me
; port = !port
}
53 changes: 53 additions & 0 deletions extraction/lockserv-seqnum/ocaml/LockServSeqNumOpts.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
open List
open Printf
open Str

let cluster_default : (LockServSeqNum.name0 * (string * int)) list = []
let me_default : LockServSeqNum.name0 = LockServSeqNum.Server
let port_default : int = 8351
let debug_default : bool = true

let cluster = ref cluster_default
let me = ref me_default
let port = ref port_default
let debug = ref debug_default

let node_spec arg nodes_ref doc =
let parse opt =
(* name,ip:port *)
if string_match (regexp "\\([^,]+\\),\\(.+\\):\\([0-9]+\\)") opt 0 then
match LockServSeqNumSerialization.deserializeName (matched_group 1 opt) with
| Some nm -> (nm, (matched_group 2 opt, int_of_string (matched_group 3 opt)))
| None -> raise (Arg.Bad (sprintf "wrong argument: '%s'; option '%s' expects a proper name" arg opt))
else
raise (Arg.Bad (sprintf "wrong argument: '%s'; option '%s' expects an entry in the form 'name,host:port'" arg opt))
in (arg, Arg.String (fun opt -> nodes_ref := !nodes_ref @ [parse opt]), doc)

let parse inp =
let opts =
[ node_spec "-node" cluster "{name,host:port} one node in the cluster"
; ("-me", Arg.String (fun opt ->
match LockServSeqNumSerialization.deserializeName opt with
| Some nm -> me := nm
| None -> raise (Arg.Bad (sprintf "wrong argument: '-me' expects a proper name"))), "{name} name for this node")
; ("-port", Arg.Set_int port, "{port} port for client commands")
; ("-debug", Arg.Set debug, "run in debug mode")
] in
Arg.parse_argv ?current:(Some (ref 0)) inp
opts
(fun x -> raise (Arg.Bad (sprintf "%s does not take position arguments" inp.(0))))
"Try -help for help or one of the following."

let rec assoc_unique = function
| [] -> true
| (k, _) :: l -> if mem_assoc k l then false else assoc_unique l

let validate () =
if length !cluster = 0 then
raise (Arg.Bad "Please specify at least one -node");
if not (mem_assoc !me !cluster) then
raise (Arg.Bad (sprintf "%s is not a member of this cluster" (LockServSeqNumSerialization.serializeName !me)));
if not (assoc_unique !cluster) then
raise (Arg.Bad "Please remove duplicate -node name entries");
if !port = snd (List.assoc !me !cluster) then
raise (Arg.Bad "Can't use same port for client commands and messages")
37 changes: 37 additions & 0 deletions extraction/lockserv-seqnum/ocaml/LockServSeqNumSerialization.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
let serializeName : LockServSeqNum.name0 -> string = function
| LockServSeqNum.Server -> "Server"
| LockServSeqNum.Client i -> Printf.sprintf "Client-%d" i

let deserializeName (s : string) : LockServSeqNum.name0 option =
match s with
| "Server" -> Some LockServSeqNum.Server
| _ ->
try Scanf.sscanf s "Client-%d" (fun x -> Some (LockServSeqNum.Client (Obj.magic x)))
with _ -> None

let deserializeMsg : string -> LockServSeqNum.seq_num_msg = fun s ->
Marshal.from_string s 0

let serializeMsg : LockServSeqNum.seq_num_msg -> string = fun m ->
Marshal.to_string m []

let deserializeInput inp c : LockServSeqNum.msg0 option =
match inp with
| "Unlock" -> Some LockServSeqNum.Unlock
| "Lock" -> Some (LockServSeqNum.Lock c)
| "Locked" -> Some (LockServSeqNum.Locked c)
| _ -> None

let serializeOutput : LockServSeqNum.msg0 -> int * string = function
| LockServSeqNum.Lock _ -> (0, "Lock") (* never happens *)
| LockServSeqNum.Unlock -> (0, "Unlock") (* never happens *)
| LockServSeqNum.Locked c -> (c, "Locked")

let debugSerializeInput : LockServSeqNum.msg0 -> string = function
| LockServSeqNum.Lock c -> Printf.sprintf "Lock %d" c
| LockServSeqNum.Unlock -> "Unlock"
| LockServSeqNum.Locked c -> Printf.sprintf "Locked %d" c

let debugSerializeMsg : LockServSeqNum.seq_num_msg -> string = function
| { LockServSeqNum.tmNum = n ; LockServSeqNum.tmMsg = m } ->
Printf.sprintf "%d: %s" n (debugSerializeInput (Obj.magic m))
2 changes: 2 additions & 0 deletions extraction/lockserv-seqnum/script/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
./LockServSeqNumMain.native -me $1 -port $2 -node Server,localhost:9000 -node Client-0,localhost:9001 -node Client-1,localhost:9002
30 changes: 30 additions & 0 deletions systems/LockServSeqNum.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Require Import Verdi.Verdi.

Require Import LockServ.
Require Verdi.SeqNum.
Require Import Verdi.SeqNumCorrect.

Section LockServSeqNum.
Variable num_Clients : nat.

Definition transformed_base_params :=
@SeqNum.base_params (LockServ_BaseParams num_Clients) (LockServ_MultiParams num_Clients).

Definition transformed_multi_params :=
@SeqNum.multi_params (LockServ_BaseParams num_Clients) (LockServ_MultiParams num_Clients).

Theorem transformed_correctness :
forall net tr,
step_dup_star (params := transformed_multi_params) step_async_init net tr ->
@mutual_exclusion num_Clients (nwState (revertNetwork net)).
Proof using.
intros.
pose proof @true_in_reachable_transform _ (LockServ_MultiParams num_Clients)
(fun net : network => mutual_exclusion (nwState net))
(@true_in_reachable_mutual_exclusion num_Clients).
unfold true_in_reachable in *.
apply H0.
unfold reachable.
eauto.
Qed.
End LockServSeqNum.