├── README.md ├── hlcnaive2.tla ├── hlc.tla └── vc.tla /README.md: -------------------------------------------------------------------------------- 1 | ## Synopsis 2 | 3 | This folder shows Pluscal/TLA+ modeling of Hybrid Logical Clocks and Hybrid Vector Clocks. 4 | 5 | "hlc.tla" models Hybrid Logical Clocks in PlusCal. 6 | http://muratbuffalo.blogspot.com/2014/07/hybrid-logical-clocks.html 7 | 8 | "hlcnaive2.tla" shows a naive implementation of hybrid logical clocks. That naive implementation has a bug, where the logical clock can gradually but unboundedly gets ahead of the physical clock. That counterexample is hard to find and involves 30 steps in the algorithm. That is an example TLA+ modeling proves valuable. 9 | 10 | vc.tla models Hybrid Vector Clocks in PlusCal. 11 | http://muratbuffalo.blogspot.com/2015/10/analysis-of-bounds-on-hybrid-vector.html 12 | https://www.cse.buffalo.edu//~demirbas/publications/augmentedTime.pdf 13 | 14 | Also see https://github.com/muratdem/PlusCal-examples for more PlusCal examples. 15 | ## Installation 16 | 17 | To run these Pluscal/TLA+ models, you need the TLA+ toolkit, available at 18 | http://research.microsoft.com/en-us/um/people/lamport/tla/tools.html 19 | 20 | ## Contributors 21 | 22 | @muratdem 23 | 24 | ## License 25 | 26 | MIT license. 27 | -------------------------------------------------------------------------------- /hlcnaive2.tla: -------------------------------------------------------------------------------- 1 | -------------------- MODULE hlcnaive2 ------------------- 2 | EXTENDS Integers 3 | CONSTANT N, STOP, EPSILON 4 | ASSUME N \in Nat \ {0,1} 5 | Procs == 1..N 6 | 7 | SetMax(S) == CHOOSE i \in S : \A j \in S : i >= j 8 | 9 | (* Hybrid Logical Clocks naive algorithm 10 | --algorithm hlcnaive2 { 11 | \* shared/auxiliary variables; initially physical clocks are set to 0 12 | variable pt = [j \in Procs |-> 0], msg= [j \in Procs |-> 0]; 13 | 14 | fair process (j \in Procs) 15 | variable lc=0; 16 | { J0: while (pt[self] < STOP) 17 | { either 18 | \* local or receive event 19 | LRec:{\* phy clocks cannot diverge more than EPSILON thanks to NTP 20 | await(\A k \in Procs: pt[self] < pt[k]+ EPSILON); 21 | pt[self] := pt[self] +1; 22 | \* j also receives the msg if any 23 | lc:= SetMax({pt[self], lc+1, msg[self]+1}); 24 | } 25 | or \* send event 26 | Send:{ \* phy clocks cannot diverge more than EPSILON thanks to NTP 27 | await(\A k \in Procs: pt[self] < pt[k]+ EPSILON); 28 | pt[self] := pt[self] +1; 29 | lc := SetMax({lc+1, pt[self]}); 30 | \* write the message to k 31 | with (k \in Procs \ {self}) {msg[k] := lc} 32 | } 33 | }\* end while 34 | }\* end process 35 | }\* end alg 36 | *) 37 | \* BEGIN TRANSLATION 38 | VARIABLES pt, msg, pc, lc 39 | 40 | vars == << pt, msg, pc, lc >> 41 | 42 | ProcSet == (Procs) 43 | 44 | Init == (* Global variables *) 45 | /\ pt = [j \in Procs |-> 0] 46 | /\ msg = [j \in Procs |-> 0] 47 | (* Process j *) 48 | /\ lc = [self \in Procs |-> 0] 49 | /\ pc = [self \in ProcSet |-> "J0"] 50 | 51 | J0(self) == /\ pc[self] = "J0" 52 | /\ IF pt[self] < STOP 53 | THEN /\ \/ /\ pc' = [pc EXCEPT ![self] = "LRec"] 54 | \/ /\ pc' = [pc EXCEPT ![self] = "Send"] 55 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 56 | /\ UNCHANGED << pt, msg, lc >> 57 | 58 | LRec(self) == /\ pc[self] = "LRec" 59 | /\ (\A k \in Procs: pt[self] < pt[k]+ EPSILON) 60 | /\ pt' = [pt EXCEPT ![self] = pt[self] +1] 61 | /\ lc' = [lc EXCEPT ![self] = SetMax({pt'[self], lc[self]+1, msg[self]+1})] 62 | /\ pc' = [pc EXCEPT ![self] = "J0"] 63 | /\ msg' = msg 64 | 65 | Send(self) == /\ pc[self] = "Send" 66 | /\ (\A k \in Procs: pt[self] < pt[k]+ EPSILON) 67 | /\ pt' = [pt EXCEPT ![self] = pt[self] +1] 68 | /\ lc' = [lc EXCEPT ![self] = SetMax({lc[self]+1, pt'[self]})] 69 | /\ \E k \in Procs \ {self}: 70 | msg' = [msg EXCEPT ![k] = lc'[self]] 71 | /\ pc' = [pc EXCEPT ![self] = "J0"] 72 | 73 | j(self) == J0(self) \/ LRec(self) \/ Send(self) 74 | 75 | Next == (\E self \in Procs: j(self)) 76 | \/ (* Disjunct to prevent deadlock on termination *) 77 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 78 | 79 | Spec == /\ Init /\ [][Next]_vars 80 | /\ \A self \in Procs : WF_vars(j(self)) 81 | 82 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 83 | 84 | \* END TRANSLATION 85 | 86 | \* Boundedness 87 | TypeOK == (\A k \in Procs: lc[k] >= pt[k]) 88 | Sync == (\A k,l \in Procs: pt[k] <= pt[l]+EPSILON) 89 | Bounded == (\A k \in Procs: lc[k] < pt[k] + N*(EPSILON+1)) \* this is violated!!! in general unbounded! 90 | \* Stabilization 91 | 92 | ================================================== 93 | -------------------------------------------------------------------------------- /hlc.tla: -------------------------------------------------------------------------------- 1 | -------------------- MODULE hlc ------------------- 2 | EXTENDS Integers 3 | CONSTANT N, STOP, EPS 4 | ASSUME N \in Nat \ {0,1} 5 | Procs == 1..N 6 | 7 | SetMax(S) == CHOOSE i \in S : \A j \in S : i >= j 8 | 9 | (* Hybrid Logical Clocks algorithm 10 | --algorithm hlc { 11 | variable pt = [j \in Procs |-> 0], msg= [j \in Procs |-> <<0,0>>]; \* shared/aux vars 12 | 13 | fair process (j \in Procs) 14 | variable l=0, c=0; 15 | {J0:while (pt[self] < STOP) 16 | {either 17 | Recv:{ \* local or receive event 18 | await(\A k \in Procs: pt[self] < pt[k]+ EPS); \* NTP clock sync 19 | pt[self] := pt[self] +1; 20 | if (l>pt[self] /\ l=msg[self][1]) 21 | c:= SetMax({c, msg[self][2]})+1; 22 | else if (l>pt[self]) c:= c+1; 23 | else if (l=pt[self]) c:= c+1; 32 | else{ l:=pt[self]; c:=0;}; 33 | with (k \in Procs \ {self}) {msg[k] := <>}; 34 | } 35 | } 36 | } 37 | } 38 | *) 39 | \* BEGIN TRANSLATION 40 | VARIABLES pt, msg, pc, l, c 41 | 42 | vars == << pt, msg, pc, l, c >> 43 | 44 | ProcSet == (Procs) 45 | 46 | Init == (* Global variables *) 47 | /\ pt = [j \in Procs |-> 0] 48 | /\ msg = [j \in Procs |-> <<0,0>>] 49 | (* Process j *) 50 | /\ l = [self \in Procs |-> 0] 51 | /\ c = [self \in Procs |-> 0] 52 | /\ pc = [self \in ProcSet |-> "J0"] 53 | 54 | J0(self) == /\ pc[self] = "J0" 55 | /\ IF pt[self] < STOP 56 | THEN /\ \/ /\ pc' = [pc EXCEPT ![self] = "Recv"] 57 | \/ /\ pc' = [pc EXCEPT ![self] = "Send"] 58 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 59 | /\ UNCHANGED << pt, msg, l, c >> 60 | 61 | Recv(self) == /\ pc[self] = "Recv" 62 | /\ (\A k \in Procs: pt[self] < pt[k]+ EPS) 63 | /\ pt' = [pt EXCEPT ![self] = pt[self] +1] 64 | /\ IF l[self]>pt'[self] /\ l[self]=msg[self][1] 65 | THEN /\ c' = [c EXCEPT ![self] = SetMax({c[self], msg[self][2]})+1] 66 | ELSE /\ IF l[self]>pt'[self] 67 | THEN /\ c' = [c EXCEPT ![self] = c[self]+1] 68 | ELSE /\ IF l[self]=pt'[self] 79 | THEN /\ c' = [c EXCEPT ![self] = c[self]+1] 80 | /\ l' = l 81 | ELSE /\ l' = [l EXCEPT ![self] = pt'[self]] 82 | /\ c' = [c EXCEPT ![self] = 0] 83 | /\ \E k \in Procs \ {self}: 84 | msg' = [msg EXCEPT ![k] = <>] 85 | /\ pc' = [pc EXCEPT ![self] = "J0"] 86 | 87 | j(self) == J0(self) \/ Recv(self) \/ Send(self) 88 | 89 | Next == (\E self \in Procs: j(self)) 90 | \/ (* Disjunct to prevent deadlock on termination *) 91 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 92 | 93 | Spec == /\ Init /\ [][Next]_vars 94 | /\ \A self \in Procs : WF_vars(j(self)) 95 | 96 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 97 | 98 | \* END TRANSLATION 99 | 100 | \* Boundedness 101 | TypeOK == (\A k \in Procs: l[k] >= pt[k]) 102 | Sync == (\A k,m \in Procs: pt[k] <= pt[m]+EPS) 103 | Boundedl == (\A k \in Procs: l[k] < pt[k] + EPS) 104 | Boundedc == (\A k \in Procs: c[k] < N*(EPS+1)) 105 | 106 | \* Stabilization 107 | 108 | ================================================== 109 | -------------------------------------------------------------------------------- /vc.tla: -------------------------------------------------------------------------------- 1 | \* Hi-lock: (("\\\\\\*.+$" (0 (quote org-level-3) t))) 2 | \* Hi-lock: (("^.*\\(?:==\\).*$" (0 (quote org-level-1) t))) 3 | -------------------- MODULE vc ------------------- 4 | EXTENDS Integers 5 | CONSTANT N, STOP, EPSILON 6 | ASSUME N \in Nat \ {0,1} 7 | Procs == 1..N 8 | 9 | SetMax(S) == CHOOSE i \in S : \A k \in S : i >= k 10 | 11 | (* Vector Clocks, augmentedtime algorithm 12 | --algorithm vca { 13 | \* initially physical clock is set to 0 14 | variable pt = [j \in Procs |-> 0], msg= [j \in Procs |-> {}]; 15 | 16 | define{ 17 | VCPrune(v1,p) == {c \in v1: c.val>p-EPSILON /\ (\A d \in v1: d.node=c.node => d.val<=c.val)} 18 | VCget(v1,s) == CHOOSE c \in v1: c.node=s \* is there a way to return c.val? 19 | VCCget(v1,s) == IF (\E c \in v1: c.node=s) THEN (CHOOSE c \in v1: c.node=s) ELSE [node|->s, val|->0] 20 | VCless(v1,s) == {c \in v1: c.node#s} 21 | } 22 | 23 | fair process (j \in Procs) 24 | variable vc= {[node |-> self, val|->0]}; 25 | { J0: while (pt[self] < STOP) 26 | { either \* local or receive event 27 | J1:{ \* phy clocks cannot diverge more than EPSILON 28 | await(\A k \in Procs: pt[self] < pt[k]+EPSILON); 29 | pt[self] := pt[self] +1; 30 | vc := VCPrune(vc \union msg[self] \union {[node|->self, val|->SetMax({pt[self], VCget(vc,self).val+1, VCCget(msg[self],self).val+1})]}, pt[self]); 31 | } 32 | or \* send event 33 | J2:{ \* phy clocks cannot diverge more than EPSILON 34 | await(\A k \in Procs: pt[self] < pt[k]+EPSILON); 35 | pt[self] := pt[self] +1; 36 | vc:= VCless(vc,self) \union {[node|->self, val|->SetMax({VCget(vc,self).val+1, pt[self]})]}; 37 | \* write the message to k 38 | with (k \in Procs \ {self}) {msg[k] := vc} 39 | } 40 | } 41 | } 42 | } 43 | *) 44 | \* BEGIN TRANSLATION 45 | VARIABLES pt, msg, pc 46 | 47 | (* define statement *) 48 | VCPrune(v1,p) == {c \in v1: c.val>p-EPSILON /\ (\A d \in v1: d.node=c.node => d.val<=c.val)} 49 | VCget(v1,s) == CHOOSE c \in v1: c.node=s 50 | VCCget(v1,s) == IF (\E c \in v1: c.node=s) THEN (CHOOSE c \in v1: c.node=s) ELSE [node|->s, val|->0] 51 | VCless(v1,s) == {c \in v1: c.node#s} 52 | 53 | VARIABLE vc 54 | 55 | vars == << pt, msg, pc, vc >> 56 | 57 | ProcSet == (Procs) 58 | 59 | Init == (* Global variables *) 60 | /\ pt = [j \in Procs |-> 0] 61 | /\ msg = [j \in Procs |-> {}] 62 | (* Process j *) 63 | /\ vc = [self \in Procs |-> {[node |-> self, val|->0]}] 64 | /\ pc = [self \in ProcSet |-> "J0"] 65 | 66 | J0(self) == /\ pc[self] = "J0" 67 | /\ IF pt[self] < STOP 68 | THEN /\ \/ /\ pc' = [pc EXCEPT ![self] = "J1"] 69 | \/ /\ pc' = [pc EXCEPT ![self] = "J2"] 70 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 71 | /\ UNCHANGED << pt, msg, vc >> 72 | 73 | J1(self) == /\ pc[self] = "J1" 74 | /\ (\A k \in Procs: pt[self] < pt[k]+EPSILON) 75 | /\ pt' = [pt EXCEPT ![self] = pt[self] +1] 76 | /\ vc' = [vc EXCEPT ![self] = VCPrune(vc[self] \union msg[self] \union {[node|->self, val|->SetMax({pt'[self], VCget(vc[self],self).val+1, VCCget(msg[self],self).val+1})]}, pt'[self])] 77 | /\ pc' = [pc EXCEPT ![self] = "J0"] 78 | /\ msg' = msg 79 | 80 | J2(self) == /\ pc[self] = "J2" 81 | /\ (\A k \in Procs: pt[self] < pt[k]+EPSILON) 82 | /\ pt' = [pt EXCEPT ![self] = pt[self] +1] 83 | /\ vc' = [vc EXCEPT ![self] = VCless(vc[self],self) \union {[node|->self, val|->SetMax({VCget(vc[self],self).val+1, pt'[self]})]}] 84 | /\ \E k \in Procs \ {self}: 85 | msg' = [msg EXCEPT ![k] = vc'[self]] 86 | /\ pc' = [pc EXCEPT ![self] = "J0"] 87 | 88 | j(self) == J0(self) \/ J1(self) \/ J2(self) 89 | 90 | Next == (\E self \in Procs: j(self)) 91 | \/ (* Disjunct to prevent deadlock on termination *) 92 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 93 | 94 | Spec == /\ Init /\ [][Next]_vars 95 | /\ \A self \in Procs : WF_vars(j(self)) 96 | 97 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 98 | 99 | \* END TRANSLATION 100 | 101 | \* Boundedness 102 | VCCOK == (\A k \in Procs: VCget(vc[k],k).val >= pt[k] /\ VCget(vc[k],k).val <= pt[k] +EPSILON) 103 | TypeOK == (\A k \in Procs: (\A v \in vc[k]: v.val >= pt[k] -EPSILON)) 104 | Sync == (\A k,l \in Procs: pt[k] <= pt[l] +EPSILON) 105 | VCprop == (\A k,l \in Procs: VCget(vc[k],k).val >= VCCget(vc[l],k).val) 106 | \* Stabilization 107 | ================================================== 108 | Add tests for stabilization ??? 109 | 110 | \* Using a set of pairs to represent VC succinctly. 111 | --------------------------------------------------------------------------------