├── .gitignore ├── 2pc.tla ├── ABProtocol.tla ├── Bakery.tla ├── BoundedBuffer.tla ├── BoundedBuffer2.tla ├── BoundedChannel.tla ├── Dekker.tla ├── Dekker2.tla ├── DieHard.tla ├── EuclidSedgewickpstyle.tla ├── FairProcess.cfg ├── FairProcess.tla ├── FastMutex.tla ├── Hello.tla ├── Increment.tla ├── LICENSE ├── MessageBatchDrainTest.tla ├── MessageBatchDrainTest2.tla ├── MessageBatchDrainTest3.tla ├── OneBitClock.cfg ├── OneBitClock.tla ├── OneBitProtocol.tla ├── PCalBoundedChannel.tla ├── RAFT.tla ├── README.md ├── TwoPhaseCommit.cfg ├── TwoPhaseCommit.tla ├── add.tla ├── eu.tla ├── euclid.tla ├── test.cfg └── test.tla /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.iws 3 | *.ipr 4 | *.iml 5 | .project 6 | .classpath 7 | .factorypath 8 | .settings/ 9 | .idea/ 10 | .DS_Store 11 | classes/ 12 | build.properties 13 | dist/ 14 | atlassian* 15 | keystore/ 16 | tmp/ 17 | bla*.java 18 | doc/manual/*.css 19 | doc/manual/build/* 20 | doc/manual/*.tmp 21 | doc/tutorial/build/* 22 | doc/tutorial/*.css 23 | conf/MANIFEST.MF 24 | target/ 25 | lib/ 26 | *.toolbox 27 | *.old 28 | *.cfg 29 | 30 | 31 | -------------------------------------------------------------------------------- /2pc.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE 2pc -------------------------------- 2 | 3 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 4 | 5 | (* PlusCal options (-termination) *) 6 | 7 | 8 | (* 9 | 10 | --algorithm 2pc { 11 | variables participants={"A", "B", "C"}, 12 | states=[i\in participants |-> "start"]; \* states can be "start", "prepared" / "failed", "committed" / "aborted" 13 | 14 | process(name="P") { 15 | p0: print "hello"; 16 | } 17 | 18 | } 19 | 20 | *) 21 | \* BEGIN TRANSLATION 22 | VARIABLES participants, states, pc 23 | 24 | vars == << participants, states, pc >> 25 | 26 | Init == (* Global variables *) 27 | /\ participants = {"a", "b", "c"} 28 | /\ states = [i\in participants |-> "start"] 29 | /\ pc = "Lbl_1" 30 | 31 | Lbl_1 == /\ pc = "Lbl_1" 32 | /\ PrintT("start") 33 | /\ pc' = "Done" 34 | /\ UNCHANGED << participants, states >> 35 | 36 | Next == Lbl_1 37 | \/ (* Disjunct to prevent deadlock on termination *) 38 | (pc = "Done" /\ UNCHANGED vars) 39 | 40 | Spec == Init /\ [][Next]_vars 41 | 42 | Termination == <>(pc = "Done") 43 | 44 | \* END TRANSLATION 45 | 46 | 47 | ============================================================================= 48 | \* Modification History 49 | \* Last modified Wed Mar 25 08:41:58 CET 2015 by bela 50 | \* Created Wed Mar 25 08:25:16 CET 2015 by bela 51 | -------------------------------------------------------------------------------- /ABProtocol.tla: -------------------------------------------------------------------------------- 1 | ----------------------------- MODULE ABProtocol ----------------------------- 2 | EXTENDS Naturals, Sequences, TLC 3 | (* the input array of messages to the algorithm *) 4 | CONSTANT Msg 5 | 6 | (* PlusCal options (-termination) *) 7 | 8 | (* define remove operation*) 9 | Remove(i,seq) == [ j \in 1..(Len(seq)-1) |-> IF j < i THEN seq[j] ELSE seq[j+1]] 10 | 11 | (* 12 | --algorithm ABProtocol 13 | { variables input = <<>>; output = <<>>; msgC = <<>>; ackC = <<>>; 14 | 15 | macro Send(m, chan) { chan := Append(chan,m) } 16 | macro Rcv(v, chan) { await chan # <<>>; 17 | v := Head(chan); 18 | chan := Tail(chan)} 19 | 20 | process (Sender = "S") 21 | variables next = 1; sbit = 0; ack; 22 | { s: while (TRUE) { 23 | either with (m \in Msg) { input := Append(input, m)} \* grab message from user to send 24 | or { await next <= Len(input) ; \* wait until message is available, then send next message 25 | Send(<>, msgC)} 26 | or { Rcv(ack, ackC); \* receive acknowledgement and set up for next message 27 | if (ack = sbit) { next := next+1; 28 | sbit := (sbit+1)%2 }}}} 29 | 30 | process (Receiver = "R") 31 | variables rbit = 1; msg; 32 | { r: while (TRUE) { 33 | either Send(rbit,ackC) \* send current acknowledgement 34 | or { Rcv(msg, msgC); \* receive next message (unconditional) and deliver if has correct abit 35 | if (msg[2] # rbit) { rbit := (rbit+1)%2 ; 36 | output := Append(output, msg[1])}}}} 37 | 38 | process (LoseMsg = "L") 39 | { l: while (TRUE) { 40 | either with (i \in 1..Len(msgC)) { msgC := Remove(i, msgC)} \* drop a randomly chosen message 41 | or with (i \in 1..Len(ackC)) {ackC := Remove(i, ackC) }}} \* drop a randomly chosen ack 42 | 43 | } (* end ABProtocol *) 44 | 45 | *) 46 | \* BEGIN TRANSLATION 47 | CONSTANT defaultInitValue 48 | VARIABLES input, output, msgC, ackC, next, sbit, ack, rbit, msg 49 | 50 | vars == << input, output, msgC, ackC, next, sbit, ack, rbit, msg >> 51 | 52 | ProcSet == {"S"} \cup {"R"} \cup {"L"} 53 | 54 | Init == (* Global variables *) 55 | /\ input = <<>> 56 | /\ output = <<>> 57 | /\ msgC = <<>> 58 | /\ ackC = <<>> 59 | (* Process Sender *) 60 | /\ next = 1 61 | /\ sbit = 0 62 | /\ ack = defaultInitValue 63 | (* Process Receiver *) 64 | /\ rbit = 1 65 | /\ msg = defaultInitValue 66 | 67 | Sender == /\ \/ /\ \E m \in Msg: 68 | input' = Append(input, m) 69 | /\ UNCHANGED <> 70 | \/ /\ next <= Len(input) 71 | /\ msgC' = Append(msgC,(<>)) 72 | /\ UNCHANGED <> 73 | \/ /\ ackC # <<>> 74 | /\ ack' = Head(ackC) 75 | /\ ackC' = Tail(ackC) 76 | /\ IF ack' = sbit 77 | THEN /\ next' = next+1 78 | /\ sbit' = (sbit+1)%2 79 | ELSE /\ TRUE 80 | /\ UNCHANGED << next, sbit >> 81 | /\ UNCHANGED <> 82 | /\ UNCHANGED << output, rbit, msg >> 83 | 84 | Receiver == /\ \/ /\ ackC' = Append(ackC,rbit) 85 | /\ UNCHANGED <> 86 | \/ /\ msgC # <<>> 87 | /\ msg' = Head(msgC) 88 | /\ msgC' = Tail(msgC) 89 | /\ IF msg'[2] # rbit 90 | THEN /\ rbit' = (rbit+1)%2 91 | /\ output' = Append(output, msg'[1]) 92 | ELSE /\ TRUE 93 | /\ UNCHANGED << output, rbit >> 94 | /\ ackC' = ackC 95 | /\ UNCHANGED << input, next, sbit, ack >> 96 | 97 | LoseMsg == /\ \/ /\ \E i \in 1..Len(msgC): 98 | msgC' = Remove(i, msgC) 99 | /\ ackC' = ackC 100 | \/ /\ \E i \in 1..Len(ackC): 101 | ackC' = Remove(i, ackC) 102 | /\ msgC' = msgC 103 | /\ UNCHANGED << input, output, next, sbit, ack, rbit, msg >> 104 | 105 | Next == Sender \/ Receiver \/ LoseMsg 106 | 107 | Spec == /\ Init /\ [][Next]_vars 108 | /\ WF_vars(Sender) 109 | /\ WF_vars(Receiver) 110 | /\ WF_vars(LoseMsg) 111 | 112 | \* END TRANSLATION 113 | ============================================================================= 114 | \* Modification History 115 | \* Last modified Tue Feb 24 14:47:59 EST 2015 by nrla 116 | \* Created Wed Feb 11 01:11:40 EST 2015 by nrla 117 | -------------------------------------------------------------------------------- /Bakery.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE Bakery ------------------------------- 2 | 3 | EXTENDS Integers 4 | CONSTANT N 5 | ASSUME N \in Nat 6 | 7 | Procs == 1..N 8 | a \prec b == \/ a[1] < b[1] 9 | \/ (a[1] = b[1]) /\ (a[2] < b[2]) 10 | 11 | (* 12 | --algorithm AtomicBakery { 13 | variable num = [i \in Procs |-> 0] ; 14 | 15 | process (p \in Procs) 16 | variables unchecked, max ; 17 | { 18 | ncs: while (TRUE) { 19 | e1: unchecked := Procs \ {self}; 20 | max := 0; 21 | e2: while (unchecked # {}) { 22 | with (i \in unchecked) { 23 | unchecked := unchecked \ {i}; 24 | if (num[i] > max) { max := num[i] } 25 | } 26 | }; 27 | e3: with (i \in {j \in Nat : j > max}) { 28 | num[self] := i; 29 | }; 30 | unchecked := Procs \ {self} ; 31 | wait: while (unchecked # {}) { 32 | with (i \in unchecked) { 33 | await \/ num[i] = 0 34 | \/ <> \prec <>; 35 | unchecked := unchecked \ {i}; 36 | } 37 | }; 38 | cs: skip ; \* the critical section; 39 | exit: num[self] := 0 40 | } 41 | } 42 | } 43 | *) 44 | \* BEGIN TRANSLATION 45 | CONSTANT defaultInitValue 46 | VARIABLES num, pc, unchecked, max 47 | 48 | vars == << num, pc, unchecked, max >> 49 | 50 | ProcSet == (Procs) 51 | 52 | Init == (* Global variables *) 53 | /\ num = [i \in Procs |-> 0] 54 | (* Process p *) 55 | /\ unchecked = [self \in Procs |-> defaultInitValue] 56 | /\ max = [self \in Procs |-> defaultInitValue] 57 | /\ pc = [self \in ProcSet |-> "ncs"] 58 | 59 | ncs(self) == /\ pc[self] = "ncs" 60 | /\ pc' = [pc EXCEPT ![self] = "e1"] 61 | /\ UNCHANGED << num, unchecked, max >> 62 | 63 | e1(self) == /\ pc[self] = "e1" 64 | /\ unchecked' = [unchecked EXCEPT ![self] = Procs \ {self}] 65 | /\ max' = [max EXCEPT ![self] = 0] 66 | /\ pc' = [pc EXCEPT ![self] = "e2"] 67 | /\ num' = num 68 | 69 | e2(self) == /\ pc[self] = "e2" 70 | /\ IF unchecked[self] # {} 71 | THEN /\ \E i \in unchecked[self]: 72 | /\ unchecked' = [unchecked EXCEPT ![self] = unchecked[self] \ {i}] 73 | /\ IF num[i] > max[self] 74 | THEN /\ max' = [max EXCEPT ![self] = num[i]] 75 | ELSE /\ TRUE 76 | /\ max' = max 77 | /\ pc' = [pc EXCEPT ![self] = "e2"] 78 | ELSE /\ pc' = [pc EXCEPT ![self] = "e3"] 79 | /\ UNCHANGED << unchecked, max >> 80 | /\ num' = num 81 | 82 | e3(self) == /\ pc[self] = "e3" 83 | /\ \E i \in {j \in Nat : j > max[self]}: 84 | num' = [num EXCEPT ![self] = i] 85 | /\ unchecked' = [unchecked EXCEPT ![self] = Procs \ {self}] 86 | /\ pc' = [pc EXCEPT ![self] = "wait"] 87 | /\ max' = max 88 | 89 | wait(self) == /\ pc[self] = "wait" 90 | /\ IF unchecked[self] # {} 91 | THEN /\ \E i \in unchecked[self]: 92 | /\ \/ num[i] = 0 93 | \/ <> \prec <> 94 | /\ unchecked' = [unchecked EXCEPT ![self] = unchecked[self] \ {i}] 95 | /\ pc' = [pc EXCEPT ![self] = "wait"] 96 | ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] 97 | /\ UNCHANGED unchecked 98 | /\ UNCHANGED << num, max >> 99 | 100 | cs(self) == /\ pc[self] = "cs" 101 | /\ TRUE 102 | /\ pc' = [pc EXCEPT ![self] = "exit"] 103 | /\ UNCHANGED << num, unchecked, max >> 104 | 105 | exit(self) == /\ pc[self] = "exit" 106 | /\ num' = [num EXCEPT ![self] = 0] 107 | /\ pc' = [pc EXCEPT ![self] = "ncs"] 108 | /\ UNCHANGED << unchecked, max >> 109 | 110 | p(self) == ncs(self) \/ e1(self) \/ e2(self) \/ e3(self) \/ wait(self) 111 | \/ cs(self) \/ exit(self) 112 | 113 | Next == (\E self \in Procs: p(self)) 114 | 115 | Spec == Init /\ [][Next]_vars 116 | 117 | \* END TRANSLATION 118 | MutualExclusion == \A i,j \in Procs : (i # j) => ~ (/\ pc[i] = "cs" /\ pc[j] = "cs") 119 | 120 | ============================================================================= 121 | \* Modification History 122 | \* Last modified Tue Mar 24 09:57:41 CET 2015 by bela 123 | \* Created Tue Mar 24 09:38:07 CET 2015 by bela 124 | -------------------------------------------------------------------------------- /BoundedBuffer.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE BoundedBuffer -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | CONSTANTS PROD, \* number of producers 5 | CONS, \* number of consumers 6 | BUF_SIZE \* size of the buffer 7 | 8 | ASSUME /\ PROD > 0 9 | /\ CONS > 0 10 | /\ BUF_SIZE > 0 11 | 12 | put(buf,el) == Append(buf,el) 13 | get(buf) == Tail(buf) 14 | 15 | (* 16 | 17 | --algorithm BoundedBuffer { 18 | variables \* running_threads={}, \* producers or consumers ready to work 19 | \* wait_set={}, \* producers or consumers waiting on a full/empty buffer 20 | buf = <<>>, \* buffer 21 | accesses=0; \* total number of gets and puts (used for termination) 22 | 23 | 24 | process(p \in 1..PROD) { 25 | p0: 26 | while(accesses < 100) { 27 | either { 28 | p1: 29 | when Len(buf) < BUF_SIZE; 30 | accesses := accesses +1; 31 | buf := Append(buf, accesses); 32 | } 33 | or { 34 | p2: skip; 35 | } 36 | }; 37 | } 38 | 39 | process(c \in 5..CONS+5) { 40 | c0: 41 | while(accesses < 100) { 42 | either { 43 | c1: 44 | when Len(buf) > 0; 45 | buf := Tail(buf); 46 | accesses := accesses +1; 47 | } 48 | or { 49 | c2: skip; 50 | } 51 | }; 52 | } 53 | 54 | 55 | } 56 | 57 | *) 58 | 59 | \* BEGIN TRANSLATION 60 | VARIABLES buf, accesses, pc 61 | 62 | vars == << buf, accesses, pc >> 63 | 64 | ProcSet == (1..PROD) \cup (5..CONS+5) 65 | 66 | Init == (* Global variables *) 67 | /\ buf = <<>> 68 | /\ accesses = 0 69 | /\ pc = [self \in ProcSet |-> CASE self \in 1..PROD -> "p0" 70 | [] self \in 5..CONS+5 -> "c0"] 71 | 72 | p0(self) == /\ pc[self] = "p0" 73 | /\ IF accesses < 100 74 | THEN /\ \/ /\ pc' = [pc EXCEPT ![self] = "p1"] 75 | \/ /\ pc' = [pc EXCEPT ![self] = "p2"] 76 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 77 | /\ UNCHANGED << buf, accesses >> 78 | 79 | p1(self) == /\ pc[self] = "p1" 80 | /\ Len(buf) < BUF_SIZE 81 | /\ accesses' = accesses +1 82 | /\ buf' = Append(buf, accesses') 83 | /\ pc' = [pc EXCEPT ![self] = "p0"] 84 | 85 | p2(self) == /\ pc[self] = "p2" 86 | /\ TRUE 87 | /\ pc' = [pc EXCEPT ![self] = "p0"] 88 | /\ UNCHANGED << buf, accesses >> 89 | 90 | p(self) == p0(self) \/ p1(self) \/ p2(self) 91 | 92 | c0(self) == /\ pc[self] = "c0" 93 | /\ IF accesses < 100 94 | THEN /\ \/ /\ pc' = [pc EXCEPT ![self] = "c1"] 95 | \/ /\ pc' = [pc EXCEPT ![self] = "c2"] 96 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 97 | /\ UNCHANGED << buf, accesses >> 98 | 99 | c1(self) == /\ pc[self] = "c1" 100 | /\ Len(buf) > 0 101 | /\ buf' = Tail(buf) 102 | /\ accesses' = accesses +1 103 | /\ pc' = [pc EXCEPT ![self] = "c0"] 104 | 105 | c2(self) == /\ pc[self] = "c2" 106 | /\ TRUE 107 | /\ pc' = [pc EXCEPT ![self] = "c0"] 108 | /\ UNCHANGED << buf, accesses >> 109 | 110 | c(self) == c0(self) \/ c1(self) \/ c2(self) 111 | 112 | Next == (\E self \in 1..PROD: p(self)) 113 | \/ (\E self \in 5..CONS+5: c(self)) 114 | \/ (* Disjunct to prevent deadlock on termination *) 115 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 116 | 117 | Spec == Init /\ [][Next]_vars 118 | 119 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 120 | 121 | \* END TRANSLATION 122 | 123 | ============================================================================= 124 | -------------------------------------------------------------------------------- /BoundedBuffer2.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE BoundedBuffer2 -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | CONSTANTS producers, \* set of producers 5 | consumers, \* set of consumers 6 | BUF_SIZE \* size of the buffer 7 | 8 | ASSUME /\ producers # {} 9 | /\ consumers # {} 10 | /\ producers \intersect consumers = {} 11 | /\ BUF_SIZE > 0 12 | 13 | 14 | participants == producers \union consumers 15 | 16 | 17 | 18 | (* 19 | 20 | --algorithm BoundedBuffer2 { 21 | variables running_threads=participants, 22 | wait_set={}, \* producers or consumers waiting on a full/empty buffer 23 | buf = <<>>, \* buffer 24 | accesses=0; \* total number of gets and puts (used for termination) 25 | 26 | \* define {running_threads == participants \ wait_set} 27 | 28 | macro wait(p) { 29 | wait_set := wait_set \union {p}; 30 | running_threads := running_threads \ {p}; 31 | } 32 | 33 | macro notify() { 34 | if(wait_set # {}) { 35 | with(m \in wait_set) { 36 | wait_set := wait_set \ {m}; 37 | running_threads := running_threads \union {m}; 38 | } 39 | } 40 | } 41 | 42 | macro notifyAll() { 43 | if(wait_set # {}) { 44 | running_threads := running_threads \union wait_set; 45 | wait_set := wait_set \ wait_set; 46 | } 47 | } 48 | 49 | macro put(p) { 50 | if(Len(buf) < BUF_SIZE) { 51 | buf := Append(buf, accesses); 52 | accesses := accesses +1; 53 | notifyAll(); 54 | } 55 | else 56 | wait(p); 57 | } 58 | 59 | macro get(p) { 60 | if(Len(buf) > 0) { 61 | buf := Tail(buf); 62 | notifyAll(); 63 | } 64 | else { 65 | wait(p); 66 | } 67 | } 68 | 69 | 70 | 71 | { 72 | while(TRUE) { 73 | with(p \in running_threads) { 74 | if(p \in producers) { 75 | put(p); 76 | } 77 | else { 78 | get(p); 79 | } 80 | } 81 | } 82 | } 83 | 84 | 85 | } 86 | 87 | *) 88 | 89 | \* BEGIN TRANSLATION 90 | VARIABLES running_threads, wait_set, buf, accesses 91 | 92 | vars == << running_threads, wait_set, buf, accesses >> 93 | 94 | Init == (* Global variables *) 95 | /\ running_threads = participants 96 | /\ wait_set = {} 97 | /\ buf = <<>> 98 | /\ accesses = 0 99 | 100 | Next == \E p \in running_threads: 101 | IF p \in producers 102 | THEN /\ IF Len(buf) < BUF_SIZE 103 | THEN /\ buf' = Append(buf, accesses) 104 | /\ accesses' = accesses +1 105 | /\ IF wait_set # {} 106 | THEN /\ running_threads' = (running_threads \union wait_set) 107 | /\ wait_set' = wait_set \ wait_set 108 | ELSE /\ TRUE 109 | /\ UNCHANGED << running_threads, 110 | wait_set >> 111 | ELSE /\ wait_set' = (wait_set \union {p}) 112 | /\ running_threads' = running_threads \ {p} 113 | /\ UNCHANGED << buf, accesses >> 114 | ELSE /\ IF Len(buf) > 0 115 | THEN /\ buf' = Tail(buf) 116 | /\ IF wait_set # {} 117 | THEN /\ running_threads' = (running_threads \union wait_set) 118 | /\ wait_set' = wait_set \ wait_set 119 | ELSE /\ TRUE 120 | /\ UNCHANGED << running_threads, 121 | wait_set >> 122 | ELSE /\ wait_set' = (wait_set \union {p}) 123 | /\ running_threads' = running_threads \ {p} 124 | /\ buf' = buf 125 | /\ UNCHANGED accesses 126 | 127 | Spec == Init /\ [][Next]_vars 128 | 129 | \* END TRANSLATION 130 | 131 | ============================================================================= 132 | -------------------------------------------------------------------------------- /BoundedChannel.tla: -------------------------------------------------------------------------------- 1 | --------------------------- MODULE BoundedChannel --------------------------- 2 | 3 | EXTENDS Integers, Sequences 4 | 5 | CONSTANTS Msg, \* set of messages 6 | N \* max size of bounded channel 7 | 8 | ASSUME /\ N \in Nat \ {0} 9 | /\ Msg # {} 10 | 11 | VARIABLE ch \* the channel 12 | 13 | 14 | TypeOK == ch \in Seq(Msg) 15 | 16 | Init == ch = <<>> 17 | 18 | 19 | Send == /\ Len(ch) < N 20 | /\ \E m \in Msg: ch' = Append(ch, m) 21 | 22 | Recv == /\ Len(ch) > 0 23 | /\ ch' = Tail(ch) 24 | 25 | Next == Send \/ Recv 26 | 27 | Spec == Init /\ [][Next]_<> 28 | 29 | ============================================================================= 30 | \* Modification History 31 | \* Last modified Tue Mar 24 13:58:37 CET 2015 by bela 32 | \* Created Tue Mar 24 13:48:11 CET 2015 by bela 33 | -------------------------------------------------------------------------------- /Dekker.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE Dekker -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | 5 | (* 6 | \* http://en.wikipedia.org/wiki/Dekker%27s_algorithm. We only model 2 processes 7 | --algorithm Dekker { 8 | variables entrance_intents=[i \in 0..1 |-> FALSE], \* intent to enter critical section 9 | turn=0; \* who's turn is it (first: p0) 10 | 11 | process (proc \in 0..1) 12 | variables other=1-self; \* the other process; works only with 2 processes 13 | { 14 | p0: 15 | entrance_intents[self] := TRUE; 16 | p1: 17 | await ~entrance_intents[other] /\ turn = self; 18 | cs: \* critical section 19 | print <>; 20 | turn := other; 21 | entrance_intents[self] := FALSE; 22 | } 23 | 24 | 25 | } 26 | 27 | *) 28 | 29 | \* BEGIN TRANSLATION 30 | VARIABLES entrance_intents, turn, pc, other 31 | 32 | vars == << entrance_intents, turn, pc, other >> 33 | 34 | ProcSet == (0..1) 35 | 36 | Init == (* Global variables *) 37 | /\ entrance_intents = [i \in 0..1 |-> FALSE] 38 | /\ turn = 0 39 | (* Process proc *) 40 | /\ other = [self \in 0..1 |-> 1-self] 41 | /\ pc = [self \in ProcSet |-> "p0"] 42 | 43 | p0(self) == /\ pc[self] = "p0" 44 | /\ entrance_intents' = [entrance_intents EXCEPT ![self] = TRUE] 45 | /\ pc' = [pc EXCEPT ![self] = "p1"] 46 | /\ UNCHANGED << turn, other >> 47 | 48 | p1(self) == /\ pc[self] = "p1" 49 | /\ ~entrance_intents[other[self]] /\ turn = self 50 | /\ pc' = [pc EXCEPT ![self] = "cs"] 51 | /\ UNCHANGED << entrance_intents, turn, other >> 52 | 53 | cs(self) == /\ pc[self] = "cs" 54 | /\ PrintT(<>) 55 | /\ turn' = other[self] 56 | /\ entrance_intents' = [entrance_intents EXCEPT ![self] = FALSE] 57 | /\ pc' = [pc EXCEPT ![self] = "Done"] 58 | /\ other' = other 59 | 60 | proc(self) == p0(self) \/ p1(self) \/ cs(self) 61 | 62 | Next == (\E self \in 0..1: proc(self)) 63 | \/ (* Disjunct to prevent deadlock on termination *) 64 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 65 | 66 | Spec == Init /\ [][Next]_vars 67 | 68 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 69 | 70 | \* END TRANSLATION 71 | 72 | ============================================================================= 73 | -------------------------------------------------------------------------------- /Dekker2.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE Dekker2 -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | 5 | (* 6 | \* http://en.wikipedia.org/wiki/Dekker%27s_algorithm. We only model 2 processes 7 | --algorithm Dekker2 { 8 | variables entrance_intents=[i \in 0..1 |-> FALSE], \* intent to enter critical section 9 | turn=0; \* who's turn is it (first: p0) 10 | 11 | fair process (proc \in 0..1) 12 | variables other=1-self; \* the other process; works only with 2 processes 13 | { 14 | start: 15 | while(TRUE) { 16 | 17 | p0: 18 | entrance_intents[self] := TRUE; 19 | 20 | p1: 21 | while(entrance_intents[other]) { 22 | if(turn # self) { 23 | entrance_intents[self] := FALSE; 24 | bw: 25 | \* while(turn # self) {skip;}; 26 | await turn = self; 27 | entrance_intents[self] := TRUE; 28 | } 29 | }; 30 | 31 | cs: \* critical section 32 | print <<"critical section", self>>; 33 | turn := other; 34 | entrance_intents[self] := FALSE; 35 | } 36 | } 37 | 38 | 39 | } 40 | 41 | *) 42 | 43 | \* BEGIN TRANSLATION 44 | VARIABLES entrance_intents, turn, pc, other 45 | 46 | vars == << entrance_intents, turn, pc, other >> 47 | 48 | ProcSet == (0..1) 49 | 50 | Init == (* Global variables *) 51 | /\ entrance_intents = [i \in 0..1 |-> FALSE] 52 | /\ turn = 0 53 | (* Process proc *) 54 | /\ other = [self \in 0..1 |-> 1-self] 55 | /\ pc = [self \in ProcSet |-> "start"] 56 | 57 | start(self) == /\ pc[self] = "start" 58 | /\ pc' = [pc EXCEPT ![self] = "p0"] 59 | /\ UNCHANGED << entrance_intents, turn, other >> 60 | 61 | p0(self) == /\ pc[self] = "p0" 62 | /\ entrance_intents' = [entrance_intents EXCEPT ![self] = TRUE] 63 | /\ pc' = [pc EXCEPT ![self] = "p1"] 64 | /\ UNCHANGED << turn, other >> 65 | 66 | p1(self) == /\ pc[self] = "p1" 67 | /\ IF entrance_intents[other[self]] 68 | THEN /\ IF turn # self 69 | THEN /\ entrance_intents' = [entrance_intents EXCEPT ![self] = FALSE] 70 | /\ pc' = [pc EXCEPT ![self] = "bw"] 71 | ELSE /\ pc' = [pc EXCEPT ![self] = "p1"] 72 | /\ UNCHANGED entrance_intents 73 | ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] 74 | /\ UNCHANGED entrance_intents 75 | /\ UNCHANGED << turn, other >> 76 | 77 | bw(self) == /\ pc[self] = "bw" 78 | /\ turn = self 79 | /\ entrance_intents' = [entrance_intents EXCEPT ![self] = TRUE] 80 | /\ pc' = [pc EXCEPT ![self] = "p1"] 81 | /\ UNCHANGED << turn, other >> 82 | 83 | cs(self) == /\ pc[self] = "cs" 84 | /\ PrintT(<<"critical section", self>>) 85 | /\ turn' = other[self] 86 | /\ entrance_intents' = [entrance_intents EXCEPT ![self] = FALSE] 87 | /\ pc' = [pc EXCEPT ![self] = "start"] 88 | /\ other' = other 89 | 90 | proc(self) == start(self) \/ p0(self) \/ p1(self) \/ bw(self) \/ cs(self) 91 | 92 | Next == (\E self \in 0..1: proc(self)) 93 | 94 | Spec == /\ Init /\ [][Next]_vars 95 | /\ \A self \in 0..1 : WF_vars(proc(self)) 96 | 97 | \* END TRANSLATION 98 | 99 | ============================================================================= 100 | -------------------------------------------------------------------------------- /DieHard.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE test -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | CONSTANTS Goal, Jugs, Capacity 5 | 6 | ASSUME /\ Goal \in Nat 7 | /\ Capacity \in [Jugs -> Nat \ {0}] 8 | 9 | Min(m,n) == IF m < n THEN m ELSE n 10 | 11 | Max(m,n) == IF m > n THEN m ELSE n 12 | 13 | Foo == [x \in Nat, y \in Nat |-> x*y] 14 | 15 | (* 16 | 17 | --algorithm DieHard { 18 | variables injug = [j \in Jugs |-> 0]; 19 | { 20 | while(TRUE) { 21 | either with(j \in Jugs) { \* Empty a jug 22 | injug[j] := 0; 23 | } 24 | or with(j \in Jugs) { \* Fill a jug 25 | injug[j] := Capacity[j]; 26 | } 27 | \* Move water from j -> k 28 | or with(j \in Jugs, k \in Jugs \ {j}, 29 | pour = Min(injug[j], Capacity[k] - injug[k])) { 30 | injug[j] := injug[j] - pour || injug[k] := injug[k] + pour; 31 | } 32 | }; 33 | print [j \in Jugs |-> injug[j]]; 34 | } 35 | } 36 | 37 | *) 38 | http://www.archimedes-lab.org/How_to_Solve/images/Water_gas_puzzle.gif 39 | \* BEGIN TRANSLATION 40 | 41 | \* END TRANSLATION 42 | 43 | ============================================================================= 44 | \* Modification History 45 | \* Last modified Thu Mar 19 12:34:13 CET 2015 by bela 46 | \* Created Thu Mar 12 17:37:39 CET 2015 by bela 47 | -------------------------------------------------------------------------------- /EuclidSedgewickpstyle.tla: -------------------------------------------------------------------------------- 1 | ----------------------- MODULE EuclidSedgewickpstyle ----------------------- 2 | (* for functions print, .., and assert *) 3 | EXTENDS Naturals, TLC 4 | (* parameterize the algorithm by K, make concrete before checking model *) 5 | CONSTANT K 6 | 7 | (* PlusCal options (-termination) *) 8 | 9 | (* helpers *) 10 | Divides(i,j) == \E k \in 0..j : j = i * k 11 | IsGCD(i,j,k) == Divides(i,j) /\ Divides(i,k) /\ \A r \in 0..j \cup 0..k : Divides(r,j) /\ Divides(r,k) => Divides(r,i) 12 | 13 | (* 14 | --algorithm EuclidSedgewick 15 | variables m \in 1..K, n \in 1..K, u=m, v=n 16 | begin 17 | print <>; 18 | while (u # 0) do 19 | if (u < v) then u := v || v := u end if; 20 | u := u - v 21 | end while; 22 | (* correctness condition *) 23 | assert IsGCD(v,m,n) 24 | end algorithm 25 | *) 26 | 27 | \* BEGIN TRANSLATION 28 | VARIABLES m, n, u, v, pc 29 | 30 | vars == << m, n, u, v, pc >> 31 | 32 | Init == (* Global variables *) 33 | /\ m \in 1..K 34 | /\ n \in 1..K 35 | /\ u = m 36 | /\ v = n 37 | /\ pc = "Lbl_1" 38 | 39 | Lbl_1 == /\ pc = "Lbl_1" 40 | /\ PrintT(<>) 41 | /\ pc' = "Lbl_2" 42 | /\ UNCHANGED << m, n, u, v >> 43 | 44 | Lbl_2 == /\ pc = "Lbl_2" 45 | /\ IF (u # 0) 46 | THEN /\ IF (u < v) 47 | THEN /\ /\ u' = v 48 | /\ v' = u 49 | ELSE /\ TRUE 50 | /\ UNCHANGED << u, v >> 51 | /\ pc' = "Lbl_3" 52 | ELSE /\ Assert(IsGCD(v,m,n), 53 | "Failure of assertion at line 23, column 5.") 54 | /\ pc' = "Done" 55 | /\ UNCHANGED << u, v >> 56 | /\ UNCHANGED << m, n >> 57 | 58 | Lbl_3 == /\ pc = "Lbl_3" 59 | /\ u' = u - v 60 | /\ pc' = "Lbl_2" 61 | /\ UNCHANGED << m, n, v >> 62 | 63 | Next == Lbl_1 \/ Lbl_2 \/ Lbl_3 64 | \/ (* Disjunct to prevent deadlock on termination *) 65 | (pc = "Done" /\ UNCHANGED vars) 66 | 67 | Spec == /\ Init /\ [][Next]_vars 68 | /\ WF_vars(Next) 69 | 70 | Termination == <>(pc = "Done") 71 | 72 | \* END TRANSLATION 73 | 74 | ============================================================================= 75 | \* Modification History 76 | \* Last modified Tue Feb 17 10:31:21 EST 2015 by nrla 77 | \* Created Wed Feb 11 18:29:13 EST 2015 by nrla 78 | -------------------------------------------------------------------------------- /FairProcess.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | -------------------------------------------------------------------------------- /FairProcess.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE FairProcess ---------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | CONSTANT ids 4 | 5 | (* 6 | 7 | --algorithm FairProcess { 8 | variable x=0; 9 | 10 | process(id=1) { 11 | p0: 12 | while(TRUE) { 13 | with(m \in ids) { 14 | x := m; 15 | print x; 16 | } 17 | } 18 | } 19 | } 20 | 21 | *) 22 | \* BEGIN TRANSLATION 23 | VARIABLE x 24 | 25 | vars == << x >> 26 | 27 | ProcSet == {1} 28 | 29 | Init == (* Global variables *) 30 | /\ x = 0 31 | 32 | id == \E m \in ids: 33 | /\ x' = m 34 | /\ PrintT(x') 35 | 36 | Next == id 37 | 38 | Spec == Init /\ [][Next]_vars 39 | 40 | \* END TRANSLATION 41 | 42 | ============================================================================= 43 | \* Modification History 44 | \* Last modified Fri Mar 27 11:41:20 CET 2015 by bela 45 | \* Created Fri Mar 27 11:29:52 CET 2015 by bela 46 | -------------------------------------------------------------------------------- /FastMutex.tla: -------------------------------------------------------------------------------- 1 | ----------------------------- MODULE FastMutex ----------------------------- 2 | EXTENDS Naturals, TLC 3 | CONSTANT N 4 | 5 | (* PlusCal options (-termination) *) 6 | 7 | (* 8 | --algorithm FastMutex { 9 | (* shared memory *) 10 | variables x = 0, y = 0, b = [i \in 1..N |-> FALSE]; 11 | fair process (Proc \in 1..N) 12 | variable j; 13 | { ncs:- skip; (* the non-critical section *) 14 | start: b[self] := TRUE; 15 | l1: x := self; 16 | l2: if (y # 0) { l3: b[self] := FALSE; (* change to TRUE to create deadlock *) 17 | l4: await y = 0; 18 | goto start }; 19 | l5: y := self; 20 | l6: if (x # self) { l7: b[self] := FALSE; 21 | j := 1; 22 | l8: while (j <= N) { await ~b[j]; 23 | j := j + 1 }; 24 | l9: if (y # self) { l10: await y = 0; 25 | goto start }}; 26 | cs:- skip; (* the critical section *) 27 | l11: y := 0; 28 | l12: b[self] := FALSE; 29 | goto ncs }} 30 | } 31 | } 32 | *) 33 | \* BEGIN TRANSLATION 34 | CONSTANT defaultInitValue 35 | VARIABLES x, y, b, pc, j 36 | 37 | vars == << x, y, b, pc, j >> 38 | 39 | ProcSet == (1..N) 40 | 41 | Init == (* Global variables *) 42 | /\ x = 0 43 | /\ y = 0 44 | /\ b = [i \in 1..N |-> FALSE] 45 | (* Process Proc *) 46 | /\ j = [self \in 1..N |-> defaultInitValue] 47 | /\ pc = [self \in ProcSet |-> "ncs"] 48 | 49 | ncs(self) == /\ pc[self] = "ncs" 50 | /\ TRUE 51 | /\ pc' = [pc EXCEPT ![self] = "start"] 52 | /\ UNCHANGED << x, y, b, j >> 53 | 54 | start(self) == /\ pc[self] = "start" 55 | /\ b' = [b EXCEPT ![self] = TRUE] 56 | /\ pc' = [pc EXCEPT ![self] = "l1"] 57 | /\ UNCHANGED << x, y, j >> 58 | 59 | l1(self) == /\ pc[self] = "l1" 60 | /\ x' = self 61 | /\ pc' = [pc EXCEPT ![self] = "l2"] 62 | /\ UNCHANGED << y, b, j >> 63 | 64 | l2(self) == /\ pc[self] = "l2" 65 | /\ IF y # 0 66 | THEN /\ pc' = [pc EXCEPT ![self] = "l3"] 67 | ELSE /\ pc' = [pc EXCEPT ![self] = "l5"] 68 | /\ UNCHANGED << x, y, b, j >> 69 | 70 | l3(self) == /\ pc[self] = "l3" 71 | /\ b' = [b EXCEPT ![self] = FALSE] 72 | /\ pc' = [pc EXCEPT ![self] = "l4"] 73 | /\ UNCHANGED << x, y, j >> 74 | 75 | l4(self) == /\ pc[self] = "l4" 76 | /\ y = 0 77 | /\ pc' = [pc EXCEPT ![self] = "start"] 78 | /\ UNCHANGED << x, y, b, j >> 79 | 80 | l5(self) == /\ pc[self] = "l5" 81 | /\ y' = self 82 | /\ pc' = [pc EXCEPT ![self] = "l6"] 83 | /\ UNCHANGED << x, b, j >> 84 | 85 | l6(self) == /\ pc[self] = "l6" 86 | /\ IF x # self 87 | THEN /\ pc' = [pc EXCEPT ![self] = "l7"] 88 | ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] 89 | /\ UNCHANGED << x, y, b, j >> 90 | 91 | l7(self) == /\ pc[self] = "l7" 92 | /\ b' = [b EXCEPT ![self] = FALSE] 93 | /\ j' = [j EXCEPT ![self] = 1] 94 | /\ pc' = [pc EXCEPT ![self] = "l8"] 95 | /\ UNCHANGED << x, y >> 96 | 97 | l8(self) == /\ pc[self] = "l8" 98 | /\ IF j[self] <= N 99 | THEN /\ ~b[j[self]] 100 | /\ j' = [j EXCEPT ![self] = j[self] + 1] 101 | /\ pc' = [pc EXCEPT ![self] = "l8"] 102 | ELSE /\ pc' = [pc EXCEPT ![self] = "l9"] 103 | /\ j' = j 104 | /\ UNCHANGED << x, y, b >> 105 | 106 | l9(self) == /\ pc[self] = "l9" 107 | /\ IF y # self 108 | THEN /\ pc' = [pc EXCEPT ![self] = "l10"] 109 | ELSE /\ pc' = [pc EXCEPT ![self] = "cs"] 110 | /\ UNCHANGED << x, y, b, j >> 111 | 112 | l10(self) == /\ pc[self] = "l10" 113 | /\ y = 0 114 | /\ pc' = [pc EXCEPT ![self] = "start"] 115 | /\ UNCHANGED << x, y, b, j >> 116 | 117 | cs(self) == /\ pc[self] = "cs" 118 | /\ TRUE 119 | /\ pc' = [pc EXCEPT ![self] = "l11"] 120 | /\ UNCHANGED << x, y, b, j >> 121 | 122 | l11(self) == /\ pc[self] = "l11" 123 | /\ y' = 0 124 | /\ pc' = [pc EXCEPT ![self] = "l12"] 125 | /\ UNCHANGED << x, b, j >> 126 | 127 | l12(self) == /\ pc[self] = "l12" 128 | /\ b' = [b EXCEPT ![self] = FALSE] 129 | /\ pc' = [pc EXCEPT ![self] = "ncs"] 130 | /\ UNCHANGED << x, y, j >> 131 | 132 | Proc(self) == ncs(self) \/ start(self) \/ l1(self) \/ l2(self) \/ l3(self) 133 | \/ l4(self) \/ l5(self) \/ l6(self) \/ l7(self) 134 | \/ l8(self) \/ l9(self) \/ l10(self) \/ cs(self) 135 | \/ l11(self) \/ l12(self) 136 | 137 | Next == (\E self \in 1..N: Proc(self)) 138 | \/ (* Disjunct to prevent deadlock on termination *) 139 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 140 | 141 | Spec == /\ Init /\ [][Next]_vars 142 | /\ \A self \in 1..N : WF_vars((pc[self] \notin {"ncs", "cs"}) /\ Proc(self)) 143 | 144 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 145 | 146 | \* END TRANSLATION 147 | 148 | Mutex == \A i,k \in 1..N : (i # k) => \neg ((pc[i] = "cs") /\ (pc[k] = "cs")) 149 | Liveness == \E i \in 1..N : pc[i] \notin {"ncs", "cs", "l11", "l12"} ~> \E k \in 1..N : pc[k] = "cs" 150 | ============================================================================= 151 | \* Modification History 152 | \* Last modified Tue Feb 24 15:44:10 EST 2015 by nrla 153 | \* Created Wed Feb 11 19:49:41 EST 2015 by nrla 154 | -------------------------------------------------------------------------------- /Hello.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE Hello ------------------------------- 2 | 3 | EXTENDS Naturals, TLC, FiniteSets, Sequences 4 | 5 | 6 | (* 7 | --algorithm HelloWorld { 8 | variables x = 1..10; 9 | { 10 | print <>; 11 | } 12 | } 13 | } 14 | *) 15 | \* BEGIN TRANSLATION 16 | VARIABLES x, pc 17 | 18 | vars == << x, pc >> 19 | 20 | Init == (* Global variables *) 21 | /\ x = 1..10 22 | /\ pc = "Lbl_1" 23 | 24 | Lbl_1 == /\ pc = "Lbl_1" 25 | /\ PrintT(<>) 26 | /\ pc' = "Done" 27 | /\ x' = x 28 | 29 | Next == Lbl_1 30 | \/ (* Disjunct to prevent deadlock on termination *) 31 | (pc = "Done" /\ UNCHANGED vars) 32 | 33 | Spec == Init /\ [][Next]_vars 34 | 35 | Termination == <>(pc = "Done") 36 | 37 | \* END TRANSLATION 38 | ============================================================================= 39 | \* Modification History 40 | \* Last modified Tue Jan 10 14:46:32 CET 2017 by bela 41 | \* Last modified Tue Jan 10 13:26:27 CET 2017 by bela 42 | \* Last modified Fri Feb 13 10:00:32 EST 2015 by nrla 43 | \* Created Wed Feb 11 18:05:23 EST 2015 by nrla 44 | -------------------------------------------------------------------------------- /Increment.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE Increment ------------------------------- 2 | EXTENDS Naturals, TLC 3 | CONSTANT N 4 | ASSUME N \in Nat \{0} 5 | 6 | Procs == 1..N 7 | 8 | 9 | \* N processes all increment a shared variable x by (1) reading it, assigning the read value +1 to local variable 10 | \* y and (2) assigning y back to x. 11 | \* Unless the read and write of x is done atomically, the Correctness property below will fail. 12 | \* To make this code incorrect, use a second label l2 in front of the assignment from y to x. 13 | (* 14 | --algorithm Incr { 15 | variables x=0; 16 | 17 | process (p \in Procs) 18 | variables y=0; 19 | { 20 | \* both assignments need to be done atomically; l1 ensures that 21 | l1: 22 | y :=x+1; 23 | x := y; \* Incorrect: l2: x := y; 24 | print <>; 25 | } 26 | } 27 | *) 28 | 29 | \* BEGIN TRANSLATION 30 | VARIABLES x, pc, y 31 | 32 | vars == << x, pc, y >> 33 | 34 | ProcSet == (Procs) 35 | 36 | Init == (* Global variables *) 37 | /\ x = 0 38 | (* Process p *) 39 | /\ y = [self \in Procs |-> 0] 40 | /\ pc = [self \in ProcSet |-> "l1"] 41 | 42 | l1(self) == /\ pc[self] = "l1" 43 | /\ y' = [y EXCEPT ![self] = x+1] 44 | /\ x' = y'[self] 45 | /\ PrintT(<>) 46 | /\ pc' = [pc EXCEPT ![self] = "Done"] 47 | 48 | p(self) == l1(self) 49 | 50 | Next == (\E self \in Procs: p(self)) 51 | \/ (* Disjunct to prevent deadlock on termination *) 52 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 53 | 54 | Spec == Init /\ [][Next]_vars 55 | 56 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 57 | 58 | \* END TRANSLATION 59 | 60 | AllDone == \A self \in Procs: pc[self] = "Done" 61 | Correctness == [](AllDone => x = N) 62 | 63 | ============================================================================= 64 | \* Modification History 65 | \* Last modified Tue Jan 10 09:23:07 CET 2017 by bela 66 | \* Last modified Fri Feb 13 10:00:32 EST 2015 by nrla 67 | \* Created Wed Feb 11 18:05:23 EST 2015 by nrla 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /MessageBatchDrainTest.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE MessageBatchDrainTest ------------------------------- 2 | EXTENDS Naturals, TLC 3 | CONSTANT N \* pick 3 to trigger the error! 4 | ASSUME N \in Nat \{0} 5 | 6 | Procs == 1..N 7 | 8 | (* 9 | Algorithm for having an MPSC queue where producers (after adding to the queue) either become the single consumer, 10 | or terminate. The queue is modelled by 'size', and 'counter' decides which producer gets to act as consumer 11 | next (after adding an element to the queue). The Java algorithm is in MessageBatchDrainTest (JGroups): 12 | https://github.com/belaban/JGroups/blob/master/tests/junit-functional/org/jgroups/tests/MessageBatchDrainTest.java 13 | Contrary to DrainTest, producers can add _multiple_ elements and the 14 | consumer removes _all_ of the elements in the queue. Note that 'counter' can temporarily 15 | become negative, e.g. for threads T1, T2, T3: 16 | - T1 adds 10 -> size: 10, counter: 10 17 | - T2 adds 5 -> size: 15 18 | - T3 adds 5 -> size: 20 19 | - T1 removes all elements: removed: 0, size: 0, counter: -10 20 | - T2 increments counter by 5: counter is -5 21 | - T3 increments counter by 5: counter is 0 22 | 23 | This algorithm is INCORRECT as counter going from (say) -4 to 0 will allow the next process to enter the 'drain' label (tmp == 0) while the existing 24 | process is still in 'drain': this violates the OnlyOneDrain invariant (at the bottom) !!! 25 | 26 | See MessageBatchDrainTest3.tla for the correct solution 27 | *) 28 | 29 | (* 30 | --algorithm Add { 31 | variables size=0, counter=0; 32 | 33 | process (p \in Procs) 34 | variables tmp=0, removed=0; 35 | { 36 | incr_size: size := size + self; \* Add 'self' elements to the queue: p1 adds 1, p2 2 etc... 37 | 38 | incr_counter: tmp := counter || counter := counter+self; 39 | 40 | tmp_check: 41 | if(tmp = 0) { \* start draining the queue 42 | drain: 43 | removed := size || size := 0; \* Remove _all_ elements from the queue 44 | 45 | decr_counter: 46 | counter := counter-removed; 47 | if(counter # 0) 48 | goto drain; 49 | } 50 | } 51 | } 52 | 53 | *) 54 | \* BEGIN TRANSLATION 55 | VARIABLES size, counter, pc, tmp, removed 56 | 57 | vars == << size, counter, pc, tmp, removed >> 58 | 59 | ProcSet == (Procs) 60 | 61 | Init == (* Global variables *) 62 | /\ size = 0 63 | /\ counter = 0 64 | (* Process p *) 65 | /\ tmp = [self \in Procs |-> 0] 66 | /\ removed = [self \in Procs |-> 0] 67 | /\ pc = [self \in ProcSet |-> "incr_size"] 68 | 69 | incr_size(self) == /\ pc[self] = "incr_size" 70 | /\ size' = size + self 71 | /\ pc' = [pc EXCEPT ![self] = "incr_counter"] 72 | /\ UNCHANGED << counter, tmp, removed >> 73 | 74 | incr_counter(self) == /\ pc[self] = "incr_counter" 75 | /\ /\ counter' = counter+self 76 | /\ tmp' = [tmp EXCEPT ![self] = counter] 77 | /\ pc' = [pc EXCEPT ![self] = "tmp_check"] 78 | /\ UNCHANGED << size, removed >> 79 | 80 | tmp_check(self) == /\ pc[self] = "tmp_check" 81 | /\ IF tmp[self] = 0 82 | THEN /\ pc' = [pc EXCEPT ![self] = "drain"] 83 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 84 | /\ UNCHANGED << size, counter, tmp, removed >> 85 | 86 | drain(self) == /\ pc[self] = "drain" 87 | /\ /\ removed' = [removed EXCEPT ![self] = size] 88 | /\ size' = 0 89 | /\ pc' = [pc EXCEPT ![self] = "decr_counter"] 90 | /\ UNCHANGED << counter, tmp >> 91 | 92 | decr_counter(self) == /\ pc[self] = "decr_counter" 93 | /\ counter' = counter-removed[self] 94 | /\ IF counter' # 0 95 | THEN /\ pc' = [pc EXCEPT ![self] = "drain"] 96 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 97 | /\ UNCHANGED << size, tmp, removed >> 98 | 99 | p(self) == incr_size(self) \/ incr_counter(self) \/ tmp_check(self) 100 | \/ drain(self) \/ decr_counter(self) 101 | 102 | Next == (\E self \in Procs: p(self)) 103 | \/ (* Disjunct to prevent deadlock on termination *) 104 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 105 | 106 | Spec == Init /\ [][Next]_vars 107 | 108 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 109 | 110 | \* END TRANSLATION 111 | 112 | \* Only one process can be in state decr_counter at any time (add to Model -> Model Overview -> Invariants) 113 | OnlyOneDrain == \A i,k \in Procs: (i # k) => ~(/\ pc[i] = "drain" /\ pc[k] = "drain") 114 | 115 | \* All the processes are done (state = "Done") 116 | AllDone == \A self \in Procs: pc[self] = "Done" 117 | 118 | \* We cannot have elements left in the queue but no threads to process them 119 | \* Add Correctness to the model's properties (Model -> Model Overview -> Properties) 120 | Correctness == [](AllDone => size = 0 /\ counter = 0) 121 | 122 | 123 | ============================================================================= 124 | \* Modification History 125 | \* Last modified Wed Jan 11 15:11:19 CET 2017 by bela 126 | \* Last modified Fri Feb 13 10:00:32 EST 2015 by nrla 127 | \* Created Wed Feb 11 18:05:23 EST 2015 by nrla 128 | -------------------------------------------------------------------------------- /MessageBatchDrainTest2.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE MessageBatchDrainTest2 ------------------------------- 2 | EXTENDS Naturals, TLC 3 | 4 | CONSTANT N, \* number of processes (e.g. 2) 5 | MAX_QUEUE_SIZE, \* max size of (bounded) queue (e.g. 10) 6 | ADD \* number of elements to add (e.g. 6): make it such that N * ADD > MAX_QUEUE_SIZE to get dropped elements on addition 7 | 8 | ASSUME N \in Nat \{0} 9 | 10 | Procs == 1..N 11 | 12 | Max(x,y) == IF x > y THEN x ELSE y 13 | Min(x,y) == IF x < y THEN x ELSE y 14 | 15 | (* 16 | Algorithm for having an MPSC queue where producers (after adding to the queue) either become the single consumer, or terminate. 17 | 18 | The queue is bounded and is represented by 'size', and 'counter' decides which producer gets to act as consumer 19 | next (after adding an element to the queue). 20 | 21 | Note that if size is 7, MAX_QUEUE_SIZE 10 and we want to add 6 messages, then we can only add 3, dropping 3 elements, making size 10. 22 | 23 | The Java algorithm is in MessageBatchDrainTest2 (JGroups): 24 | https://github.com/belaban/JGroups/blob/master/tests/junit-functional/org/jgroups/tests/MessageBatchDrainTest2.java 25 | 26 | Contrary to DrainTest, producers can add _multiple_ elements and the consumer removes _all_ of the elements in the queue. 27 | 28 | Note that 'counter' can temporarily become negative, e.g. for threads T1, T2, T3: 29 | - T1 adds 10 -> size: 10, counter: 10 30 | - T2 adds 5 -> size: 15 31 | - T3 adds 5 -> size: 20 32 | - T1 removes all elements: removed: 0, size: 0, counter: -10 33 | - T2 increments counter by 5: counter is -5 34 | - T3 increments counter by 5: counter is 0 35 | 36 | 37 | This algorithm is INCORRECT as counter going from (say) -4 to 0 will allow the next process to enter the 'drain' label (tmp == 0) while the existing 38 | process is still in 'drain': this violates the OnlyOneDrain invariant (at the bottom) !!! 39 | 40 | MessageBatchDrainTest3.tla contains the correct version 41 | *) 42 | 43 | (* 44 | --algorithm Add { 45 | variables size=0, counter=0; 46 | 47 | 48 | process (p \in Procs) 49 | variables tmp=0, added=0, removed=0, new_size=0, old_size=0; 50 | { 51 | add: 52 | old_size := size; 53 | new_size := Min(size+ADD, MAX_QUEUE_SIZE); 54 | added := new_size - size || size := new_size; \* Add ADD elements to the queue 55 | print <<"old_size= ", old_size, "new_size= ",new_size, "size= ", size, "added= ", added>>; 56 | 57 | if(added > 0) { 58 | 59 | incr_counter: 60 | tmp := counter || counter := counter+added; 61 | 62 | tmp_check: 63 | if(tmp = 0) { \* start draining the queue 64 | drain: 65 | removed := size || size := 0; \* Remove _all_ elements from the queue 66 | 67 | decr_counter: 68 | counter := counter-removed; 69 | if(counter # 0) 70 | goto drain; 71 | } 72 | }; 73 | } 74 | } 75 | 76 | *) 77 | \* BEGIN TRANSLATION 78 | VARIABLES size, counter, pc, tmp, added, removed, new_size, old_size 79 | 80 | vars == << size, counter, pc, tmp, added, removed, new_size, old_size >> 81 | 82 | ProcSet == (Procs) 83 | 84 | Init == (* Global variables *) 85 | /\ size = 0 86 | /\ counter = 0 87 | (* Process p *) 88 | /\ tmp = [self \in Procs |-> 0] 89 | /\ added = [self \in Procs |-> 0] 90 | /\ removed = [self \in Procs |-> 0] 91 | /\ new_size = [self \in Procs |-> 0] 92 | /\ old_size = [self \in Procs |-> 0] 93 | /\ pc = [self \in ProcSet |-> "add"] 94 | 95 | add(self) == /\ pc[self] = "add" 96 | /\ old_size' = [old_size EXCEPT ![self] = size] 97 | /\ new_size' = [new_size EXCEPT ![self] = Min(size+ADD, MAX_QUEUE_SIZE)] 98 | /\ /\ added' = [added EXCEPT ![self] = new_size'[self] - size] 99 | /\ size' = new_size'[self] 100 | /\ PrintT(<<"old_size= ", old_size'[self], "new_size= ",new_size'[self], "size= ", size', "added= ", added'[self]>>) 101 | /\ IF added'[self] > 0 102 | THEN /\ pc' = [pc EXCEPT ![self] = "incr_counter"] 103 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 104 | /\ UNCHANGED << counter, tmp, removed >> 105 | 106 | incr_counter(self) == /\ pc[self] = "incr_counter" 107 | /\ /\ counter' = counter+added[self] 108 | /\ tmp' = [tmp EXCEPT ![self] = counter] 109 | /\ pc' = [pc EXCEPT ![self] = "tmp_check"] 110 | /\ UNCHANGED << size, added, removed, new_size, old_size >> 111 | 112 | tmp_check(self) == /\ pc[self] = "tmp_check" 113 | /\ IF tmp[self] = 0 114 | THEN /\ pc' = [pc EXCEPT ![self] = "drain"] 115 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 116 | /\ UNCHANGED << size, counter, tmp, added, removed, 117 | new_size, old_size >> 118 | 119 | drain(self) == /\ pc[self] = "drain" 120 | /\ /\ removed' = [removed EXCEPT ![self] = size] 121 | /\ size' = 0 122 | /\ pc' = [pc EXCEPT ![self] = "decr_counter"] 123 | /\ UNCHANGED << counter, tmp, added, new_size, old_size >> 124 | 125 | decr_counter(self) == /\ pc[self] = "decr_counter" 126 | /\ counter' = counter-removed[self] 127 | /\ IF counter' # 0 128 | THEN /\ pc' = [pc EXCEPT ![self] = "drain"] 129 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 130 | /\ UNCHANGED << size, tmp, added, removed, new_size, 131 | old_size >> 132 | 133 | p(self) == add(self) \/ incr_counter(self) \/ tmp_check(self) 134 | \/ drain(self) \/ decr_counter(self) 135 | 136 | Next == (\E self \in Procs: p(self)) 137 | \/ (* Disjunct to prevent deadlock on termination *) 138 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 139 | 140 | Spec == Init /\ [][Next]_vars 141 | 142 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 143 | 144 | \* END TRANSLATION 145 | 146 | \* Only one process can be in state decr_counter at any time (add to Model -> Model Overview -> Invariants) 147 | OnlyOneDrain == \A i,k \in Procs: (i # k) => ~(/\ pc[i] = "drain" /\ pc[k] = "drain") 148 | 149 | \* All the processes are done (state = "Done") 150 | AllDone == \A self \in Procs: pc[self] = "Done" 151 | 152 | \* We cannot have elements left in the queue but no threads to process them 153 | \* Add Correctness to the model's properties (Model -> Model Overview -> Properties) 154 | Correctness == [](AllDone => size = 0 /\ counter = 0) 155 | 156 | 157 | ============================================================================= 158 | \* Modification History 159 | \* Last modified Wed Jan 11 15:05:54 CET 2017 by bela 160 | \* Last modified Fri Feb 13 10:00:32 EST 2015 by nrla 161 | \* Created Wed Feb 11 18:05:23 EST 2015 by nrla 162 | -------------------------------------------------------------------------------- /MessageBatchDrainTest3.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE MessageBatchDrainTest3 ------------------------------- 2 | EXTENDS Naturals, TLC 3 | 4 | CONSTANT N, \* number of processes (pick at least 3) 5 | MAX_QUEUE_SIZE, \* max size of (bounded) queue (e.g. 10) 6 | ADD \* number of elements to add (e.g. 6): make it such that N * ADD > MAX_QUEUE_SIZE to get dropped elements on addition 7 | 8 | ASSUME N \in Nat \{0} 9 | 10 | Procs == 1..N 11 | 12 | Max(x,y) == IF x > y THEN x ELSE y 13 | Min(x,y) == IF x < y THEN x ELSE y 14 | 15 | (* 16 | Algorithm for having an MPSC queue where producers (after adding to the queue) either become the single consumer, or terminate. 17 | 18 | The queue is bounded and is represented by 'size', and 'counter' decides which producer gets to act as consumer 19 | next (after adding an element to the queue). 20 | 21 | Note that if size is 7, MAX_QUEUE_SIZE 10 and we want to add 6 messages, then we can only add 3, dropping 3 elements, making size 10. 22 | 23 | The Java algorithm is in MessageBatchDrainTest2 (JGroups): 24 | https://github.com/belaban/JGroups/blob/master/tests/junit-functional/org/jgroups/tests/MessageBatchDrainTest2.java 25 | 26 | Contrary to DrainTest, producers can add _multiple_ elements and the consumer removes _all_ of the elements in the queue. 27 | 28 | Note that 'counter' can temporarily become negative, e.g. for threads T1, T2, T3: 29 | - T1 adds 10 -> size: 10, counter: 10 30 | - T2 adds 5 -> size: 15 31 | - T3 adds 5 -> size: 20 32 | - T1 removes all elements: removed: 0, size: 0, counter: -10 33 | - T2 increments counter by 5: counter is -5 34 | - T3 increments counter by 5: counter is 0 35 | 36 | 37 | Contrary to MessageBatchDrainTest2, this should be correct as counter is only incremented and decremented by 1: 38 | this represents 1 addition (no matter how many elements were added), and not the number of messages added or removed. 39 | Therefore, counter should never become negative. 40 | *) 41 | 42 | (* 43 | --algorithm Add { 44 | variables size=0, counter=0; 45 | 46 | 47 | process (p \in Procs) 48 | variables tmp=0, added=0, removed=0, new_size=0, old_size=0; 49 | { 50 | add: 51 | old_size := size; 52 | new_size := Min(size+ADD, MAX_QUEUE_SIZE); 53 | added := new_size - size || size := new_size; \* Add ADD elements to the queue 54 | \* print <<"old_size= ", old_size, "new_size= ",new_size, "size= ", size, "added= ", added>>; 55 | 56 | if(added > 0) { 57 | 58 | incr_counter: 59 | tmp := counter || counter := counter+1; 60 | 61 | tmp_check: 62 | if(tmp = 0) { \* start draining the queue 63 | drain: 64 | removed := size || size := 0; \* Remove _all_ elements from the queue 65 | \* Deliver removed messages to the application 66 | 67 | decr_counter: 68 | counter := counter-1; 69 | assert ~counter < 0; 70 | if(counter # 0) 71 | goto drain; 72 | } 73 | }; 74 | } 75 | } 76 | 77 | *) 78 | \* BEGIN TRANSLATION 79 | VARIABLES size, counter, pc, tmp, added, removed, new_size, old_size 80 | 81 | vars == << size, counter, pc, tmp, added, removed, new_size, old_size >> 82 | 83 | ProcSet == (Procs) 84 | 85 | Init == (* Global variables *) 86 | /\ size = 0 87 | /\ counter = 0 88 | (* Process p *) 89 | /\ tmp = [self \in Procs |-> 0] 90 | /\ added = [self \in Procs |-> 0] 91 | /\ removed = [self \in Procs |-> 0] 92 | /\ new_size = [self \in Procs |-> 0] 93 | /\ old_size = [self \in Procs |-> 0] 94 | /\ pc = [self \in ProcSet |-> "add"] 95 | 96 | add(self) == /\ pc[self] = "add" 97 | /\ old_size' = [old_size EXCEPT ![self] = size] 98 | /\ new_size' = [new_size EXCEPT ![self] = Min(size+ADD, MAX_QUEUE_SIZE)] 99 | /\ /\ added' = [added EXCEPT ![self] = new_size'[self] - size] 100 | /\ size' = new_size'[self] 101 | /\ IF added'[self] > 0 102 | THEN /\ pc' = [pc EXCEPT ![self] = "incr_counter"] 103 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 104 | /\ UNCHANGED << counter, tmp, removed >> 105 | 106 | incr_counter(self) == /\ pc[self] = "incr_counter" 107 | /\ /\ counter' = counter+1 108 | /\ tmp' = [tmp EXCEPT ![self] = counter] 109 | /\ pc' = [pc EXCEPT ![self] = "tmp_check"] 110 | /\ UNCHANGED << size, added, removed, new_size, old_size >> 111 | 112 | tmp_check(self) == /\ pc[self] = "tmp_check" 113 | /\ IF tmp[self] = 0 114 | THEN /\ pc' = [pc EXCEPT ![self] = "drain"] 115 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 116 | /\ UNCHANGED << size, counter, tmp, added, removed, 117 | new_size, old_size >> 118 | 119 | drain(self) == /\ pc[self] = "drain" 120 | /\ /\ removed' = [removed EXCEPT ![self] = size] 121 | /\ size' = 0 122 | /\ pc' = [pc EXCEPT ![self] = "decr_counter"] 123 | /\ UNCHANGED << counter, tmp, added, new_size, old_size >> 124 | 125 | decr_counter(self) == /\ pc[self] = "decr_counter" 126 | /\ counter' = counter-1 127 | /\ Assert(~counter' < 0, 128 | "Failure of assertion at line 68, column 28.") 129 | /\ IF counter' # 0 130 | THEN /\ pc' = [pc EXCEPT ![self] = "drain"] 131 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 132 | /\ UNCHANGED << size, tmp, added, removed, new_size, 133 | old_size >> 134 | 135 | p(self) == add(self) \/ incr_counter(self) \/ tmp_check(self) 136 | \/ drain(self) \/ decr_counter(self) 137 | 138 | Next == (\E self \in Procs: p(self)) 139 | \/ (* Disjunct to prevent deadlock on termination *) 140 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 141 | 142 | Spec == Init /\ [][Next]_vars 143 | 144 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 145 | 146 | \* END TRANSLATION 147 | 148 | \* Only one process can be in state decr_counter at any time (add to Model -> Model Overview -> Invariants) 149 | OnlyOneDrain == \A i,k \in Procs: (i # k) => ~(/\ pc[i] = "drain" /\ pc[k] = "drain") 150 | 151 | \* All the processes are done (state = "Done") 152 | AllDone == \A self \in Procs: pc[self] = "Done" 153 | 154 | \* We cannot have elements left in the queue but no threads to process them 155 | \* Add Correctness to the model's properties (Model -> Model Overview -> Properties) 156 | Correctness == [](AllDone => size = 0 /\ counter = 0) 157 | 158 | 159 | ============================================================================= 160 | \* Modification History 161 | \* Last modified Thu Jan 12 11:03:39 CET 2017 by bela 162 | \* Last modified Fri Feb 13 10:00:32 EST 2015 by nrla 163 | \* Created Wed Feb 11 18:05:23 EST 2015 by nrla 164 | -------------------------------------------------------------------------------- /OneBitClock.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | -------------------------------------------------------------------------------- /OneBitClock.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE OneBitClock ---------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | Put(s) == Append(s, "widget") 5 | Get(s) == Tail(s) 6 | 7 | 8 | (* 9 | 10 | --fair algorithm OneBitClock { 11 | variable b = 0 , box = << >> ; 12 | 13 | process(Producer = 0) { 14 | p1: 15 | while (TRUE) { 16 | await b = 0 ; 17 | box := Put(box) ; 18 | b := 1; 19 | print <<0,b,box>>; 20 | } 21 | } 22 | 23 | process (Consumer = 1) { 24 | c1: 25 | while (TRUE) { 26 | await b = 1 ; 27 | box := Get(box) ; 28 | b := 0; 29 | print <<1,b,box>>; 30 | } 31 | } 32 | 33 | } 34 | 35 | *) 36 | \* BEGIN TRANSLATION 37 | VARIABLES b, box 38 | 39 | vars == << b, box >> 40 | 41 | ProcSet == {0} \cup {1} 42 | 43 | Init == (* Global variables *) 44 | /\ b = 0 45 | /\ box = << >> 46 | 47 | Producer == /\ b = 0 48 | /\ box' = Put(box) 49 | /\ b' = 1 50 | /\ PrintT(<<0,b',box'>>) 51 | 52 | Consumer == /\ b = 1 53 | /\ box' = Get(box) 54 | /\ b' = 0 55 | /\ PrintT(<<1,b',box'>>) 56 | 57 | Next == Producer \/ Consumer 58 | 59 | Spec == /\ Init /\ [][Next]_vars 60 | /\ WF_vars(Next) 61 | 62 | \* END TRANSLATION 63 | 64 | ============================================================================= 65 | \* Modification History 66 | \* Last modified Fri Mar 27 09:24:54 CET 2015 by bela 67 | \* Created Thu Mar 26 18:33:13 CET 2015 by bela 68 | -------------------------------------------------------------------------------- /OneBitProtocol.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE OneBitProtocol -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | 5 | 6 | (* 7 | 8 | --algorithm OneBitProtocol { 9 | variable x \in [{0,1} -> BOOLEAN] ; 10 | 11 | fair process (P \in {0,1}) { 12 | r: while (TRUE) { 13 | either { 14 | with (v \in BOOLEAN) { x[self] := v }; 15 | goto r 16 | } 17 | or skip; 18 | e1: x[self] := TRUE; 19 | e2: if (~x[1-self]) { 20 | cs: print <>; 21 | } 22 | else { 23 | if(self = 0) goto e2; 24 | else { 25 | e3: x[1] := FALSE; 26 | e4: await ~x[0] 27 | goto e1; 28 | } 29 | }; 30 | } 31 | } 32 | } 33 | 34 | 35 | *) 36 | 37 | 38 | 39 | \* BEGIN TRANSLATION 40 | VARIABLES x, pc 41 | 42 | vars == << x, pc >> 43 | 44 | ProcSet == ({0,1}) 45 | 46 | Init == (* Global variables *) 47 | /\ x \in [{0,1} -> BOOLEAN] 48 | /\ pc = [self \in ProcSet |-> "r"] 49 | 50 | r(self) == /\ pc[self] = "r" 51 | /\ \/ /\ \E v \in BOOLEAN: 52 | x' = [x EXCEPT ![self] = v] 53 | /\ pc' = [pc EXCEPT ![self] = "r"] 54 | \/ /\ TRUE 55 | /\ pc' = [pc EXCEPT ![self] = "e1"] 56 | /\ x' = x 57 | 58 | e1(self) == /\ pc[self] = "e1" 59 | /\ x' = [x EXCEPT ![self] = TRUE] 60 | /\ pc' = [pc EXCEPT ![self] = "e2"] 61 | 62 | e2(self) == /\ pc[self] = "e2" 63 | /\ IF ~x[1-self] 64 | THEN /\ pc' = [pc EXCEPT ![self] = "cs"] 65 | ELSE /\ IF self = 0 66 | THEN /\ pc' = [pc EXCEPT ![self] = "e2"] 67 | ELSE /\ pc' = [pc EXCEPT ![self] = "e3"] 68 | /\ x' = x 69 | 70 | cs(self) == /\ pc[self] = "cs" 71 | /\ PrintT(<>) 72 | /\ pc' = [pc EXCEPT ![self] = "r"] 73 | /\ x' = x 74 | 75 | e3(self) == /\ pc[self] = "e3" 76 | /\ x' = [x EXCEPT ![1] = FALSE] 77 | /\ pc' = [pc EXCEPT ![self] = "e4"] 78 | 79 | e4(self) == /\ pc[self] = "e4" 80 | /\ IF x[0] 81 | THEN /\ TRUE 82 | /\ pc' = [pc EXCEPT ![self] = "e4"] 83 | ELSE /\ pc' = [pc EXCEPT ![self] = "e1"] 84 | /\ x' = x 85 | 86 | P(self) == r(self) \/ e1(self) \/ e2(self) \/ cs(self) \/ e3(self) 87 | \/ e4(self) 88 | 89 | Next == (\E self \in {0,1}: P(self)) 90 | 91 | Spec == /\ Init /\ [][Next]_vars 92 | /\ \A self \in {0,1} : WF_vars(P(self)) 93 | 94 | \* END TRANSLATION 95 | 96 | TypeOK == pc \in [{0,1} -> {"r", "e1", "e2", "cs"}] 97 | /\ x \in [{0,1} -> BOOLEAN] 98 | InCS(i) == pc[i] = "cs" 99 | MutualExclusion == ~(InCS(0) /\ InCS(1)) 100 | Inv == TypeOK 101 | /\ MutualExclusion 102 | /\ \A i \in {0,1}: InCS(i) \/ (pc[i] = "e2") => x[i] 103 | Trying(p) == pc[p] \in {"e1", "e2", "e3", "e4"} 104 | DeadlockFree == (Trying(0) \/ Trying(1)) ~> (InCS(0) \/ InCS(1)) 105 | ============================================================================= 106 | -------------------------------------------------------------------------------- /PCalBoundedChannel.tla: -------------------------------------------------------------------------------- 1 | ------------------------- MODULE PCalBoundedChannel ------------------------- 2 | 3 | EXTENDS Integers, Sequences 4 | 5 | CONSTANTS Msg, \* set of messages 6 | N \* max size of bounded channel 7 | 8 | ASSUME /\ N \in Nat \ {0} 9 | /\ Msg # {} 10 | 11 | 12 | 13 | 14 | (* 15 | 16 | --algorithm PCalBoundedChannel { 17 | variables ch = <<>>; \* the channel 18 | 19 | macro Send(m) { 20 | if(Len(ch) < N) { 21 | ch := Append(ch, m); 22 | } 23 | } 24 | 25 | macro Recv() { 26 | if(Len(ch) > 0) { 27 | ch := Tail(ch); 28 | } 29 | } 30 | 31 | process(sender="S") { 32 | s0: 33 | while(TRUE) { 34 | \* Send(m); 35 | await Len(ch) < N; 36 | with(m \in Msg) 37 | ch := Append(ch, m); 38 | } 39 | } 40 | 41 | process(receiver="R") { 42 | r0: 43 | while(TRUE) { 44 | \* Recv(); 45 | await Len(ch) > 0; 46 | ch := Tail(ch); 47 | } 48 | } 49 | 50 | 51 | (*{ 52 | while(TRUE) { 53 | either { 54 | with(m \in Msg) { 55 | Send(m); 56 | } 57 | } 58 | or { 59 | Recv(); 60 | } 61 | } 62 | }*) 63 | } 64 | 65 | 66 | *) 67 | 68 | 69 | \* BEGIN TRANSLATION 70 | VARIABLE ch 71 | 72 | vars == << ch >> 73 | 74 | ProcSet == {"S"} \cup {"R"} 75 | 76 | Init == (* Global variables *) 77 | /\ ch = <<>> 78 | 79 | sender == /\ Len(ch) < N 80 | /\ \E m \in Msg: 81 | ch' = Append(ch, m) 82 | 83 | receiver == /\ Len(ch) > 0 84 | /\ ch' = Tail(ch) 85 | 86 | Next == sender \/ receiver 87 | 88 | Spec == Init /\ [][Next]_vars 89 | 90 | \* END TRANSLATION 91 | 92 | 93 | 94 | 95 | 96 | ============================================================================= 97 | \* Modification History 98 | \* Last modified Tue Mar 24 14:39:27 CET 2015 by bela 99 | \* Created Tue Mar 24 14:11:22 CET 2015 by bela 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /RAFT.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE RAFT -------------------------------- 2 | EXTENDS Naturals, FiniteSets, Sequences, TLC 3 | 4 | (* global constants, variables*) 5 | CONSTANTS Replica 6 | ASSUME Replica = {1,2,3} 7 | CONSTANTS Value 8 | CONSTANTS Follower, Candidate, Leader 9 | CONSTANTS Nil 10 | CONSTANTS RequestVoteRequest, RequestVoteResponse, AppendEntriesRequest, AppendEntriesResponse 11 | VARIABLE messages \* messages is BAG(Message) 12 | VARIABLE elections \* elections is SET(Election) 13 | VARIABLE allLogs \* allLogs is SUBSET Seq(LogEntry) - some set of LogEntry sequences 14 | ----------------------------------------------------------- 15 | (* per server variables*) 16 | 17 | VARIABLE currentTerm \* currentTerm \in [Replicas -> Nat] 18 | VARIABLE state \* state \in [Replicas -> {Leader, Follower, Candidate}] 19 | VARIABLE votedFor \* votedFor \in [Replicas -> Replicas \cup {Nil}] 20 | replicaVars == <> 21 | 22 | VARIABLE log \* log \in [Replicas -> Sequence] 23 | VARIABLE commitIndex \* commitIndex \in [Replicas -> Nat] 24 | logVars == <> 25 | 26 | VARIABLE votesResponded \* votesResponded \in [Replicas -> SUBSET Replicas] 27 | VARIABLE votesGranted \* votesGranted \in [Replicas -> SUBSET Replicas] 28 | VARIABLE voterLog \* voterLog \in [Replicas -> Seq(Replicas)] 29 | candidateVars == <> 30 | 31 | VARIABLE nextIndex \* nextIndex \in [Replicas -> Nat] 32 | VARIABLE lastAgreeIndex \* lastAgreeIndex \in [Replicas -> Nat] 33 | leaderVars == <> 34 | --------------------------------------------------------------- 35 | vars == <> 36 | --------------------------------------------------------------- 37 | (* helpers *) 38 | 39 | Quorum == {i \in SUBSET Replica : Cardinality(i)*2 > Cardinality(Replica)} 40 | LastTerm(xlog) == IF Len(xlog) = 0 THEN 0 ELSE xlog[Len(xlog)].term 41 | 42 | WithMessage(m, msgs) == IF m \in DOMAIN msgs THEN [msgs EXCEPT ![m] = msgs[m]+1] ELSE msgs @@ (m :> 1) 43 | WithoutMessage(m, msgs) == IF m \in DOMAIN msgs THEN [msgs EXCEPT ![m] = msgs[m]-1] ELSE msgs 44 | 45 | (* add a message m to the bag of messages *) 46 | Send(m) == messages' = WithMessage(m, messages) 47 | Discard(m) == messages' = WithoutMessage(m, messages) 48 | 49 | (* add a message response to the bag, then remove a message request *) 50 | Reply(response, request) == messages' = WithoutMessage(request, WithMessage(response, messages)) 51 | 52 | Min(S) == CHOOSE x \in S : \A y \in S : x <= y 53 | Max(S) == CHOOSE x \in S : \A y \in S : x >= y 54 | ------------------------------------------------------------------ 55 | (* initialization for all variables *) 56 | 57 | InitHistoryVars == /\ elections = {} 58 | /\ allLogs = {} 59 | /\ voterLog = [i \in Replica |-> [j \in {} |-> <<>>]] 60 | 61 | InitReplicaVars == /\ currentTerm = [i \in Replica |-> 1] 62 | /\ state = [i \in Replica |-> Follower] 63 | /\ votedFor = [i\in Replica |-> Nil] 64 | 65 | InitCandidateVars == /\ votesResponded = [i \in Replica |-> {}] 66 | /\ votesGranted = [i \in Replica |-> {}] 67 | 68 | InitLeaderVars == /\ nextIndex = [i \in Replica |-> [j \in Replica |-> 1]] 69 | /\ lastAgreeIndex = [i \in Replica |-> [j \in Replica |-> 0]] 70 | 71 | InitLogVars == /\ log = [i \in Replica |-> <<>>] 72 | /\ commitIndex = [i \in Replica |-> 0] 73 | 74 | Init == /\ messages = [m \in {} |-> 0] 75 | /\ InitHistoryVars 76 | /\ InitReplicaVars 77 | /\ InitCandidateVars 78 | /\ InitLeaderVars 79 | /\ InitLogVars 80 | -------------------------------------------------------------------- 81 | (* actions *) 82 | 83 | (* duplicate an existing message in the bag of messages - should this not be CHOOSE instead of \E ?? *) 84 | DuplicateMessage == /\ \E m \in DOMAIN messages : Send(m) 85 | /\ UNCHANGED <> 86 | 87 | (* drop a message from the bag of messages *) 88 | DropMessage == /\ \E m \in DOMAIN messages : Discard(m) 89 | /\ UNCHANGED <> 90 | 91 | (* replica i times out - increment term and reset voting variables *) 92 | Timeout(i) == /\ state[i] \in {Follower, Candidate} 93 | /\ state' = [state EXCEPT ![i] = Candidate] 94 | /\ currentTerm' = [currentTerm EXCEPT ![i] = currentTerm[i]+1] 95 | /\ votedFor' = [votedFor EXCEPT ![i] = Nil] 96 | /\ votesResponded' = [votesResponded EXCEPT ![i] = {}] 97 | /\ votesGranted' = [votesGranted EXCEPT ![i] = {}] 98 | /\ voterLog' = [voterLog EXCEPT ![i] = [j \in {} |-> <<>>]] 99 | /\ UNCHANGED <> 100 | 101 | Restart(i) == /\ state' = [state EXCEPT ![i] = Follower] 102 | /\ votesResponded' = [votesResponded EXCEPT ![i] = {}] 103 | /\ votesGranted' = [votesGranted EXCEPT ![i] = {}] 104 | /\ voterLog' = [voterLog EXCEPT ![i] = [j \in {} |-> <<>>]] 105 | /\ nextIndex' = [nextIndex EXCEPT ![i] = [j \in Replica |-> 1]] 106 | /\ lastAgreeIndex' = [lastAgreeIndex EXCEPT ![i] = [j \in Replica |-> 0]] 107 | /\ UNCHANGED <> 108 | 109 | (* use a LET .. IN to separate out the message construction *) 110 | RequestVote(i,j) == /\ state[i] = Candidate 111 | /\ j \notin votesResponded[i] 112 | /\ Send([mtype |-> RequestVoteRequest, 113 | mterm |-> currentTerm[i], 114 | mlastLogTerm |-> LastTerm(log[i]), 115 | mlastLogIndex |-> Len(log[i]), 116 | msource |-> i, 117 | mdest |-> j]) 118 | /\ UNCHANGED <> 119 | 120 | AppendEntries(i,j) == /\ (i # j) 121 | /\ state[i] = Leader 122 | /\ LET prevLogIndex == nextIndex[i][j] - 1 123 | prevLogTerm == IF prevLogIndex > 0 THEN log[i][prevLogIndex].term ELSE 0 124 | lastEntry == Min({Len(log[i]), nextIndex[i][j]+1}) 125 | (* has to add this to account for upper *) 126 | upper == 10 127 | entries == SubSeq(log[i], nextIndex[i][j], upper) 128 | IN Send([mtype |-> AppendEntriesRequest, 129 | mterm |-> currentTerm[i], 130 | mprevLogIndex |-> prevLogIndex, 131 | mprevLogTerm |-> prevLogTerm, 132 | mentries |-> entries, 133 | mcommitIndex |-> Min({commitIndex[i], lastEntry}), 134 | msource |-> i, 135 | mdest |-> j]) 136 | /\ UNCHANGED <> 137 | 138 | (* on becoming leader, update nextIndex and lastAgreeIndex and record election in hstory variable *) 139 | BecomeLeader(i) == /\ state[i] = Candidate 140 | /\ votesGranted[i] \in Quorum 141 | /\ state' = [state EXCEPT ![i] = Leader] 142 | /\ nextIndex' = [nextIndex EXCEPT ![i] = [j \in Replica |-> Len(log[i])+1]] 143 | /\ lastAgreeIndex' = [lastAgreeIndex EXCEPT ![i] = [j \in Replica |-> 0]] 144 | /\ elections' = elections \cup {[eterm |-> currentTerm[i], 145 | eleader |-> i, 146 | elog |-> log[i], 147 | evotes |-> votesGranted[i], 148 | evoterLog |-> voterLog[i]]} 149 | /\ UNCHANGED <> 150 | 151 | (* update the leader's log with the new entry *) 152 | \* NB: newIndex not used 153 | ClientRequest(i,v) == /\ state[i] = Leader 154 | /\ LET entry == [term |-> currentTerm[i], value |-> v] 155 | newIndex == Len(log[i]) + 1 156 | newLog == Append(log[i], entry) 157 | IN log' = [log EXCEPT ![i] = newLog] 158 | /\ UNCHANGED <> 159 | ------------------------------------------------------------------- 160 | (* message handlers *) 161 | HandleRequestVoteRequest(i,j,m) == 162 | LET logIsCurrent == \/ m.mlastLogTerm > LastTerm(log[i]) 163 | \/ (/\ m.mlastLogTerm = LastTerm(log[i]) 164 | /\ m.mlastLogIndex >= Len(log[i])) 165 | grant == /\ m.mterm = currentTerm[i] 166 | /\ logIsCurrent 167 | /\ votedFor[i] \in {Nil, j} 168 | IN /\ m.mterm <= currentTerm[i] 169 | /\ \/ grant /\ votedFor' = [votedFor EXCEPT ![i] = j] 170 | \/ \neg grant /\ UNCHANGED votedFor 171 | /\ Reply([mtype |-> RequestVoteResponse, 172 | mterm |-> currentTerm[i], 173 | mvoteGranted |-> grant, 174 | mlog |-> log[i], 175 | msource |-> i, 176 | mdest |-> j], 177 | m) 178 | /\ UNCHANGED <> 179 | 180 | HandleRequestVoteResponse(i,j,m) == 181 | /\ m.mterm = currentTerm[i] 182 | /\ votesResponded' = [votesResponded EXCEPT ![i] = votesResponded[i] \cup {j}] 183 | /\ \/ m.mvoteGranted /\ votesGranted' = [votesGranted EXCEPT ![i] = votesGranted[i] \cup {j}] 184 | /\ voterLog' = [voterLog EXCEPT ![i][j] = m.mlog] 185 | \/ \neg m.mvotesGranted /\ UNCHANGED <> 186 | /\ Discard(m) 187 | /\ UNCHANGED <> 188 | 189 | HandleAppendEntriesRequest(i,j,m) == 190 | LET accept == /\ m.mterm = currentTerm[i] 191 | /\ \/ m.mprevLogIndex = 0 192 | \/ /\ m.mprevLogIndex > 0 193 | /\ m.mprevLogIndex <= Len(log[i]) 194 | /\ m.mprevLogTerm = log[i][m.prevLogIndex].term 195 | IN /\ m.mterm <= currentTerm[i] 196 | /\ \/ \* reject request 197 | /\ \neg accept 198 | /\ Reply([mtype |-> AppendEntriesResponse, 199 | mterm |-> currentTerm[i], 200 | mlastAgreeIndex |-> 0, 201 | msource |-> i, 202 | mdest |-> j], 203 | m) 204 | /\ UNCHANGED <> 205 | \/ /\ accept 206 | /\ LET index == m.mPrevLogIndex + 1 207 | IN \/ \* already done with request 208 | /\ \/ m.mentries = <<>> 209 | \/ /\ Len(log[i]) >= index 210 | /\ log[i][index].term = m.mentries[1].term 211 | /\ commitIndex' = [commitIndex EXCEPT ![i] = m.mcommitIndex] 212 | /\ Reply([mtype |-> AppendEntriesResponse, 213 | mterm |-> currentTerm[i], 214 | mlastAgreeIndex |-> m.mprevLogIndex + Len(m.mentries), 215 | msource |-> i, 216 | mdest |-> j], 217 | m) 218 | /\ UNCHANGED <> 219 | \/ \* conflict remove one entry 220 | /\ m.mentries # <<>> 221 | /\ Len(log[i]) >= index 222 | /\ log[i][index].term # m.mentries[1].term 223 | /\ LET new == [index2 \in 1..(Len(log[i]) - 1) |-> log[i][index2]] 224 | IN log' = [log EXCEPT ![i] = new] 225 | /\ UNCHANGED <> 226 | \/ \* no conflict add entry 227 | /\ m.mentries # <<>> 228 | /\ Len(log[i]) = m.mprevLogIndex 229 | /\ log' = [log EXCEPT ![i] = Append(log[i], m.mentries[1])] 230 | /\ UNCHANGED <> 231 | /\ UNCHANGED <> 232 | 233 | HandleAppendEntriesResponse(i,j,m) == 234 | /\ m.mterm = currentTerm[i] 235 | /\ \/ /\ m.mlastAgreeIndex > 0 236 | /\ nextIndex' = [nextIndex EXCEPT ![i][j] = m.mlastAgreeIndex + 1] 237 | /\ lastAgreeIndex' = [lastAgreeIndex EXCEPT ![i][j] = m.mlastAgreeIndex] 238 | /\ LET Agree(index) == {i} \cup {k \in Replica : lastAgreeIndex'[i][k] >= index} 239 | agreeIndexes == {index \in 1..Len(log[i]) : Agree(index) \in Quorum} 240 | newCommitIndex == IF /\ agreeIndexes # {} 241 | /\ log[i][Max(agreeIndexes)].term = currentTerm[i] 242 | THEN 243 | Max(agreeIndexes) 244 | ELSE 245 | commitIndex[i] 246 | IN commitIndex' = [commitIndex EXCEPT ![i] = newCommitIndex] 247 | \/ /\ m.mlastAgreeIndex = 0 248 | /\ nextIndex' = [nextIndex EXCEPT ![i][j] = Max({nextIndex[i][j] - 1, 1})] 249 | /\ UNCHANGED <> 250 | /\ Discard(m) 251 | /\ UNCHANGED <> 252 | 253 | UpdateTerm(i,j,m) == /\ m.mterm > currentTerm[i] 254 | /\ currentTerm' = [currentTerm EXCEPT ![i] = m.mterm] 255 | /\ state' = [state EXCEPT ![i] = Follower] 256 | /\ votedFor' = [votedFor EXCEPT ![i] = Nil] 257 | \* messages is unchanges so m can be processed further 258 | /\ UNCHANGED <> 259 | 260 | DropStaleResponse(i,j,m) == /\ m.mterm < currentTerm[i] 261 | /\ Discard(m) 262 | /\ UNCHANGED <> 263 | 264 | (* receive a message *) 265 | Receive == \E m \in DOMAIN messages : 266 | LET i == m.mdest 267 | j == m.msource 268 | IN \* any RPC with a newer term causes the recipient to advance its term first 269 | \/ UpdateTerm(i,j,m) 270 | \/ m.mtype = RequestVoteRequest /\ HandleRequestVoteRequest(i,j,m) 271 | \/ m.mtype = RequestVoteResponse /\ \/ DropStaleResponse(i,j,m) 272 | \/ HandleRequestVoteRequest(i,j,m) 273 | \/ m.mtype = AppendEntriesRequest /\ HandleAppendEntriesRequest(i,j,m) 274 | \/ m.mtype = AppendEntriesResponse /\ \/ DropStaleResponse(i,j,m) 275 | \/ HandleAppendEntriesRequest(i,j,m) 276 | 277 | --------------------------------------------------------------------------------- 278 | Next == /\ \/ DuplicateMessage 279 | \/ DropMessage 280 | \/ Receive 281 | \/ \E i \in Replica : Timeout(i) 282 | \/ \E i \in Replica : Restart(i) 283 | \/ \E i \in Replica : BecomeLeader(i) 284 | \/ \E i \in Replica, v \in Value : ClientRequest(i,v) 285 | \/ \E i,j \in Replica : RequestVote(i,j) 286 | \/ \E i,j \in Replica : AppendEntries(i,j) 287 | \* history variable that tracks every log instance 288 | /\ allLogs' = allLogs \cup {\A i \in Replica: log[i]} 289 | 290 | \* the specification must start with the initial state and transition according to Next 291 | Spec == Init /\ [][Next]_vars 292 | 293 | ============================================================================= 294 | \* Modification History 295 | \* Last modified Fri Feb 27 14:13:30 EST 2015 by nrla 296 | \* Created Fri Feb 27 08:15:46 EST 2015 by nrla 297 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pluscal 2 | Tests with PlusCal / TLA+ 3 | -------------------------------------------------------------------------------- /TwoPhaseCommit.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | -------------------------------------------------------------------------------- /TwoPhaseCommit.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE TwoPhaseCommit -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | allInState(parts, states, state) == \A i \in parts: states[i] \in state 5 | anyInState(parts, states, state) == \E i \in parts: states[i] \in state 6 | 7 | 8 | 9 | (* 10 | 11 | --fair algorithm TwoPhaseCommit { 12 | variables participants={"A", "B", "C"}, 13 | valid_states={"started", "proposed", "prepared", "failed", "committed", "aborted"}, 14 | states=[i \in participants |-> "started"]; \* in valid_states 15 | 16 | macro setAllTo(state) { 17 | states := [i \in participants |-> state] 18 | } 19 | 20 | 21 | process(participant \in participants) { 22 | p0: 23 | while(TRUE) { 24 | await states[self]="proposed"; 25 | \* Randomly reply either with "prepared" or "failed" 26 | either { 27 | states[self] := "prepared"; 28 | } 29 | or { 30 | states[self] := "failed"; 31 | } 32 | } 33 | } 34 | 35 | 36 | process(leader="L") { 37 | l0: 38 | while(TRUE) { 39 | l1: 40 | if(allInState(participants,states, {"started", "committed", "aborted"})) { 41 | if(allInState(participants,states, {"committed"})) 42 | print <<"all committed", states>>; 43 | setAllTo("proposed"); 44 | } 45 | else if(anyInState(participants, states, {"failed"})) { 46 | setAllTo("aborted"); 47 | } 48 | else if(allInState(participants, states, {"prepared"})) { 49 | print <<"all prepared", states>>; 50 | setAllTo("committed"); 51 | } 52 | \* skip proposed state 53 | } 54 | } 55 | 56 | 57 | } 58 | 59 | *) 60 | 61 | 62 | 63 | 64 | \* BEGIN TRANSLATION 65 | VARIABLES participants, valid_states, states, pc 66 | 67 | vars == << participants, valid_states, states, pc >> 68 | 69 | ProcSet == (participants) \cup {"L"} 70 | 71 | Init == (* Global variables *) 72 | /\ participants = {"A", "B", "C"} 73 | /\ valid_states = {"started", "proposed", "prepared", "failed", "committed", "aborted"} 74 | /\ states = [i \in participants |-> "started"] 75 | /\ pc = [self \in ProcSet |-> CASE self \in participants -> "p0" 76 | [] self = "L" -> "l0"] 77 | 78 | p0(self) == /\ pc[self] = "p0" 79 | /\ states[self]="proposed" 80 | /\ \/ /\ states' = [states EXCEPT ![self] = "prepared"] 81 | \/ /\ states' = [states EXCEPT ![self] = "failed"] 82 | /\ pc' = [pc EXCEPT ![self] = "p0"] 83 | /\ UNCHANGED << participants, valid_states >> 84 | 85 | participant(self) == p0(self) 86 | 87 | l0 == /\ pc["L"] = "l0" 88 | /\ pc' = [pc EXCEPT !["L"] = "l1"] 89 | /\ UNCHANGED << participants, valid_states, states >> 90 | 91 | l1 == /\ pc["L"] = "l1" 92 | /\ IF allInState(participants,states, {"started", "committed", "aborted"}) 93 | THEN /\ IF allInState(participants,states, {"committed"}) 94 | THEN /\ PrintT(<<"all committed", states>>) 95 | ELSE /\ TRUE 96 | /\ states' = [i \in participants |-> "proposed"] 97 | ELSE /\ IF anyInState(participants, states, {"failed"}) 98 | THEN /\ states' = [i \in participants |-> "aborted"] 99 | ELSE /\ IF allInState(participants, states, {"prepared"}) 100 | THEN /\ PrintT(<<"all prepared", states>>) 101 | /\ states' = [i \in participants |-> "committed"] 102 | ELSE /\ TRUE 103 | /\ UNCHANGED states 104 | /\ pc' = [pc EXCEPT !["L"] = "l0"] 105 | /\ UNCHANGED << participants, valid_states >> 106 | 107 | leader == l0 \/ l1 108 | 109 | Next == leader 110 | \/ (\E self \in participants: participant(self)) 111 | 112 | Spec == /\ Init /\ [][Next]_vars 113 | /\ WF_vars(Next) 114 | 115 | \* END TRANSLATION 116 | 117 | 118 | 119 | \* Invariants 120 | ValidStates == \A i \in participants: states[i] \in valid_states 121 | 122 | 123 | \* Properties 124 | all(state) == \A i \in participants: states[i] = state 125 | 126 | AllPrepared == all("prepared") 127 | AllCommitted == all("committed") 128 | AllAborted == all("aborted") 129 | 130 | \* If all participants are in prepared state, then all of them have to eventually commit 131 | \* IfPrepareThenCommit == [](AllPrepared => <> AllCommitted) 132 | IfPrepareThenCommit == AllPrepared ~> AllCommitted 133 | 134 | 135 | \* all participants have either all committed or all aborted states 136 | EventuallyCommittedOrAborted == <>(AllCommitted \/ AllAborted) 137 | 138 | 139 | ============================================================================= 140 | \* Modification History 141 | \* Last modified Thu Mar 26 18:28:21 CET 2015 by bela 142 | \* Created Wed Mar 25 08:25:16 CET 2015 by bela 143 | -------------------------------------------------------------------------------- /add.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE add ------------------------------- 2 | EXTENDS Naturals, TLC 3 | CONSTANT N 4 | ASSUME N \in Nat \{0} 5 | 6 | Procs == 1..N 7 | 8 | (* 9 | Algorithm for having an MPSC queue where producers (after adding to the queue) either become the single consumer, 10 | or terminate. The queue is modelled by 'size', and 'counter' decides which producer gets to act as consumer 11 | next (after adding an element to the queue). The Java algorithm is in DrainTest (JGroups): 12 | https://github.com/belaban/JGroups/blob/master/tests/junit-functional/org/jgroups/tests/DrainTest.java 13 | *) 14 | 15 | (* 16 | --algorithm Add { 17 | variables size=0, counter=0; 18 | 19 | process (p \in Procs) 20 | variables tmp=0; 21 | { 22 | incr_size: size := size +1; \* Add an element to the queue 23 | 24 | incr_counter: tmp := counter || counter := counter+1; 25 | 26 | tmp_check: 27 | if(tmp = 0) { \* start draining the queue 28 | decr_size: 29 | size := size-1; \* Remove an element from the queue 30 | assert ~(size < 0); \* size can never be negative 31 | 32 | decr_counter: 33 | counter := counter-1; 34 | \* print <>; 35 | assert ~(counter < 0); \* counter can never be negative 36 | if(counter # 0) 37 | goto decr_size; 38 | } 39 | } 40 | } 41 | 42 | *) 43 | \* BEGIN TRANSLATION 44 | VARIABLES size, counter, pc, tmp 45 | 46 | vars == << size, counter, pc, tmp >> 47 | 48 | ProcSet == (Procs) 49 | 50 | Init == (* Global variables *) 51 | /\ size = 0 52 | /\ counter = 0 53 | (* Process p *) 54 | /\ tmp = [self \in Procs |-> 0] 55 | /\ pc = [self \in ProcSet |-> "incr_size"] 56 | 57 | incr_size(self) == /\ pc[self] = "incr_size" 58 | /\ size' = size +1 59 | /\ pc' = [pc EXCEPT ![self] = "incr_counter"] 60 | /\ UNCHANGED << counter, tmp >> 61 | 62 | incr_counter(self) == /\ pc[self] = "incr_counter" 63 | /\ /\ counter' = counter+1 64 | /\ tmp' = [tmp EXCEPT ![self] = counter] 65 | /\ pc' = [pc EXCEPT ![self] = "tmp_check"] 66 | /\ size' = size 67 | 68 | tmp_check(self) == /\ pc[self] = "tmp_check" 69 | /\ IF tmp[self] = 0 70 | THEN /\ pc' = [pc EXCEPT ![self] = "decr_size"] 71 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 72 | /\ UNCHANGED << size, counter, tmp >> 73 | 74 | decr_size(self) == /\ pc[self] = "decr_size" 75 | /\ size' = size-1 76 | /\ Assert(~(size' < 0), 77 | "Failure of assertion at line 30, column 20.") 78 | /\ pc' = [pc EXCEPT ![self] = "decr_counter"] 79 | /\ UNCHANGED << counter, tmp >> 80 | 81 | decr_counter(self) == /\ pc[self] = "decr_counter" 82 | /\ counter' = counter-1 83 | /\ Assert(~(counter' < 0), 84 | "Failure of assertion at line 35, column 20.") 85 | /\ IF counter' # 0 86 | THEN /\ pc' = [pc EXCEPT ![self] = "decr_size"] 87 | ELSE /\ pc' = [pc EXCEPT ![self] = "Done"] 88 | /\ UNCHANGED << size, tmp >> 89 | 90 | p(self) == incr_size(self) \/ incr_counter(self) \/ tmp_check(self) 91 | \/ decr_size(self) \/ decr_counter(self) 92 | 93 | Next == (\E self \in Procs: p(self)) 94 | \/ (* Disjunct to prevent deadlock on termination *) 95 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 96 | 97 | Spec == Init /\ [][Next]_vars 98 | 99 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 100 | 101 | \* END TRANSLATION 102 | 103 | \* Only one process can be in state decr_counter at any time (add to Model -> Model Overview -> Invariants) 104 | OnlyOneDecrCounter == \A i,k \in Procs: (i # k) => ~(/\ pc[i] = "decr_counter" /\ pc[k] = "decr_counter") 105 | 106 | \* All the processes are done (state = "Done") 107 | AllDone == \A self \in Procs: pc[self] = "Done" 108 | 109 | \* We cannot have elements left in the queue but no threads to process them 110 | \* Add Correctness to the model's properties (Model -> Model Overview -> Properties) 111 | Correctness == [](AllDone => size = 0 /\ counter = 0) 112 | 113 | ============================================================================= 114 | \* Modification History 115 | \* Last modified Tue Jan 10 13:06:51 CET 2017 by bela 116 | \* Last modified Fri Feb 13 10:00:32 EST 2015 by nrla 117 | \* Created Wed Feb 11 18:05:23 EST 2015 by nrla 118 | -------------------------------------------------------------------------------- /eu.tla: -------------------------------------------------------------------------------- 1 | --------------------------------- MODULE eu --------------------------------- 2 | EXTENDS TLC, Sequences, Integers 3 | 4 | CONSTANTS K,M 5 | 6 | (* 7 | 8 | --algorithm eu { 9 | variables x=K,y=M; \* y will hold the result 10 | 11 | { 12 | while(x # 0) { 13 | if(x < y) 14 | x := y || y := x; 15 | x := x-y; 16 | print <>; 17 | }; 18 | 19 | print <<"gcd", K, M, x, y>>; 20 | } 21 | 22 | } 23 | 24 | *) 25 | \* BEGIN TRANSLATION 26 | VARIABLES x, y, pc 27 | 28 | vars == << x, y, pc >> 29 | 30 | Init == (* Global variables *) 31 | /\ x = K 32 | /\ y = M 33 | /\ pc = "Lbl_1" 34 | 35 | Lbl_1 == /\ pc = "Lbl_1" 36 | /\ IF x # 0 37 | THEN /\ IF x < y 38 | THEN /\ /\ x' = y 39 | /\ y' = x 40 | ELSE /\ TRUE 41 | /\ UNCHANGED << x, y >> 42 | /\ pc' = "Lbl_2" 43 | ELSE /\ PrintT(<<"gcd", K, M, x, y>>) 44 | /\ pc' = "Done" 45 | /\ UNCHANGED << x, y >> 46 | 47 | Lbl_2 == /\ pc = "Lbl_2" 48 | /\ x' = x-y 49 | /\ PrintT(<>) 50 | /\ pc' = "Lbl_1" 51 | /\ y' = y 52 | 53 | Next == Lbl_1 \/ Lbl_2 54 | \/ (* Disjunct to prevent deadlock on termination *) 55 | (pc = "Done" /\ UNCHANGED vars) 56 | 57 | Spec == Init /\ [][Next]_vars 58 | 59 | Termination == <>(pc = "Done") 60 | 61 | \* END TRANSLATION 62 | 63 | 64 | ============================================================================= 65 | \* Modification History 66 | \* Last modified Tue Mar 24 16:24:16 CET 2015 by bela 67 | \* Created Tue Mar 24 16:17:55 CET 2015 by bela 68 | -------------------------------------------------------------------------------- /euclid.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE euclid ------------------------------- 2 | 3 | (* for functions print, .., and assert *) 4 | EXTENDS Naturals, TLC 5 | (* parameterize the algorithm by K, make concrete before checking model *) 6 | \* CONSTANT K 7 | 8 | 9 | (* PlusCal options (-termination) *) 10 | 11 | (* helpers *) 12 | Divides(i,j) == \E k \in 0..j : j = i * k 13 | \*Divides(i,j) == j % i = 0 14 | IsGCD(i,j,k) == Divides(i,j) /\ Divides(i,k) /\ \A r \in 0..j \cup 0..k : Divides(r,j) /\ Divides(r,k) => Divides(r,i) 15 | 16 | THEOREM GCD1 == \A x \in Nat \ {0}: IsGCD(x,x,x) 17 | 18 | (* 19 | --algorithm euclid { 20 | variables m=30, n=18, u=m, v=n, v_ini=v; 21 | { 22 | bela: while (u # 0) { 23 | iff: if (u < v) 24 | u := v || v := u; 25 | tmp: u := u - v; 26 | }; 27 | print <>; 28 | (* correctness condition *) 29 | assert IsGCD(v,m,n) 30 | } 31 | } 32 | *) 33 | \* BEGIN TRANSLATION 34 | VARIABLES m, n, u, v, v_ini, pc 35 | 36 | vars == << m, n, u, v, v_ini, pc >> 37 | 38 | Init == (* Global variables *) 39 | /\ m = 30 40 | /\ n = 18 41 | /\ u = m 42 | /\ v = n 43 | /\ v_ini = v 44 | /\ pc = "bela" 45 | 46 | bela == /\ pc = "bela" 47 | /\ IF u # 0 48 | THEN /\ pc' = "iff" 49 | ELSE /\ PrintT(<>) 50 | /\ Assert(IsGCD(v,m,n), 51 | "Failure of assertion at line 29, column 5.") 52 | /\ pc' = "Done" 53 | /\ UNCHANGED << m, n, u, v, v_ini >> 54 | 55 | iff == /\ pc = "iff" 56 | /\ IF u < v 57 | THEN /\ /\ u' = v 58 | /\ v' = u 59 | ELSE /\ TRUE 60 | /\ UNCHANGED << u, v >> 61 | /\ pc' = "tmp" 62 | /\ UNCHANGED << m, n, v_ini >> 63 | 64 | tmp == /\ pc = "tmp" 65 | /\ u' = u - v 66 | /\ pc' = "bela" 67 | /\ UNCHANGED << m, n, v, v_ini >> 68 | 69 | Next == bela \/ iff \/ tmp 70 | \/ (* Disjunct to prevent deadlock on termination *) 71 | (pc = "Done" /\ UNCHANGED vars) 72 | 73 | Spec == /\ Init /\ [][Next]_vars 74 | /\ WF_vars(Next) 75 | 76 | Termination == <>(pc = "Done") 77 | 78 | \* END TRANSLATION 79 | 80 | 81 | ============================================================================= 82 | \* Modification History 83 | \* Last modified Thu Jan 05 18:44:44 CET 2017 by bela 84 | \* Created Mon Mar 09 16:07:32 CET 2015 by bela 85 | -------------------------------------------------------------------------------- /test.cfg: -------------------------------------------------------------------------------- 1 | SPECIFICATION Spec 2 | \* Add statements after this line. 3 | -------------------------------------------------------------------------------- /test.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE test -------------------------------- 2 | EXTENDS Naturals, Integers, FiniteSets, Bags, Sequences, TLC 3 | 4 | 5 | 6 | 7 | 8 | Remove(i,seq) == [ j \in 1..(Len(seq)-1) |-> IF j < i THEN seq[j] ELSE seq[j+1]] 9 | 10 | tail(seq) == [i \in 1..Len(seq)-1 |-> seq[i+1]] 11 | 12 | 13 | \* add[i, j \in Nat] == i+j 14 | \*add == [i, j \in Nat |-> i+j] 15 | 16 | (* 17 | --algorithm test { 18 | variable seq = <<10,11,12,13,14,15>>; 19 | 20 | 21 | { 22 | print <>; 23 | 24 | } 25 | 26 | 27 | } 28 | 29 | *) 30 | 31 | \* BEGIN TRANSLATION 32 | VARIABLES seq, pc 33 | 34 | vars == << seq, pc >> 35 | 36 | Init == (* Global variables *) 37 | /\ seq = <<10,11,12,13,14,15>> 38 | /\ pc = "Lbl_1" 39 | 40 | Lbl_1 == /\ pc = "Lbl_1" 41 | /\ PrintT(<>) 42 | /\ pc' = "Done" 43 | /\ seq' = seq 44 | 45 | Next == Lbl_1 46 | \/ (* Disjunct to prevent deadlock on termination *) 47 | (pc = "Done" /\ UNCHANGED vars) 48 | 49 | Spec == Init /\ [][Next]_vars 50 | 51 | Termination == <>(pc = "Done") 52 | 53 | \* END TRANSLATION 54 | 55 | 56 | 57 | ============================================================================= 58 | --------------------------------------------------------------------------------