├── .gitignore ├── LICENSE ├── README.md ├── count-down ├── CountDown.tla ├── CountDown.toolbox │ └── CountDown___Model_1.launch └── README.md ├── deadlock ├── Deadlock.tla ├── Deadlock.toolbox │ └── Deadlock___Model_1.launch └── README.md ├── extends ├── ExtendsA.tla ├── ExtendsB.tla ├── ExtendsB.toolbox │ └── ExtendsB___Model_1.launch └── README.md ├── implements ├── ImplementsA.tla ├── ImplementsB.tla ├── ImplementsB.toolbox │ └── ImplementsB___Model_1.launch └── README.md ├── linear-search ├── LinearSearch.tla ├── LinearSearch.toolbox │ └── LinearSearch___Model_1.launch └── README.md ├── list-operations ├── ListOperations.tla ├── ListOperations.toolbox │ └── ListOperations___Model_1.launch └── README.md ├── race-condition ├── README.md ├── RaceCondition.tla └── RaceCondition.toolbox │ └── RaceCondition___Model_1.launch ├── secret-santa ├── README.md ├── SecretSanta.tla └── SecretSanta.toolbox │ └── SecretSanta___Model_1.launch ├── simple-logic ├── README.md ├── SimpleLogic.tla └── SimpleLogic.toolbox │ └── SimpleLogic___Model_1.launch └── state-machine ├── PlusCalDiagram.png ├── README.md ├── StateMachine ├── StateMachine.tla ├── StateMachine.toolbox └── StateMachine___Model_1.launch └── StateMachineDiagram.png /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X folder state 2 | .DS_Store 3 | 4 | # Exclude TLA+ Toolbox working state and snapshots 5 | **/*.toolbox/* 6 | # (but include model files) 7 | !**/*.toolbox/*.launch 8 | # (but don't include snapshots of model files) 9 | **/*.toolbox/*_Snapshot_*.launch 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tiny-tlaplus-examples 2 | 3 | ## Description 4 | 5 | This repo contains *very small* examples of TLA+ functionality or applications, intended to help you learn TLA+. 6 | 7 | For more sophisticated examples, see: 8 | 9 | * https://github.com/tlaplus/Examples 10 | * https://www.hillelwayne.com/post/list-of-tla-examples/ 11 | 12 | ## List of Examples 13 | 14 | ### [state-machine](./state-machine) 15 | 16 | 17 | TLA+ works by modeling systems via state machines. If we model an actual state machine this way, how does the generated state graph compare to the machine we're modeling? 18 | 19 | `#safety` 20 | 21 | ### [linear-search](./linear-search) 22 | 23 | What does it look like to validate the correctness of a procedural algorithm? 24 | 25 | `#correctness` 26 | 27 | ### [secret-santa](./secret-santa) 28 | 29 | What does it look like to model a concurrent system? 30 | 31 | `#concurrency` `#safety` `#correctness` 32 | 33 | ### [count-down](./count-down) 34 | 35 | What does it look like to specify a temporal condition? 36 | 37 | `#correctness` 38 | 39 | ### [simple-logic](./simple-logic) 40 | 41 | How can you use TLA+ to validate formulas in predicate logic? 42 | 43 | `#correctness` 44 | 45 | ### [deadlock](./deadlock) 46 | 47 | How can you use TLA+ to find deadlocks in a concurrent system? 48 | 49 | `#concurrency` `#liveness` 50 | 51 | ### [race-condition](./race-condition) 52 | 53 | What does it look like when two processes interact incorrectly due to a race condition? 54 | 55 | `#concurrency` `#safety` 56 | 57 | ### [extends](./extends) 58 | 59 | How can one module use operators defined in another module? 60 | 61 | `#modules` 62 | 63 | ### [implements](./implements) 64 | 65 | How can one module parameterize and/or namespace operators defined in another module? 66 | 67 | `#modules` 68 | 69 | ### [list-operations](./list-operations) 70 | 71 | Basic functional list operations(Head/Tail/Append/Len/Reverse) on sequences. 72 | 73 | `#sequences` `#recursion` 74 | 75 | 76 | ## Contributing 77 | 78 | Contributions are welcome. 79 | * Please make sure examples are *very small*. 80 | * Put each example in its own subfolder with a README.md file. (These examples use README files instead of putting all documentation into the spec itself so that they will be easier to explore on Github. As soon as a user navigates to a folder they will be reading the documentation in their browser, rather than having to navigate into the individual spec file.) 81 | * Name sub-folders and examples using the existing pattern of "spec-name" for the folder and "SpecName.tla" for the TLA+ file. 82 | -------------------------------------------------------------------------------- /count-down/CountDown.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE CountDown ---------------------------- 2 | 3 | \* See README.md 4 | 5 | EXTENDS Naturals, TLC 6 | 7 | (* PlusCal options (-termination) *) 8 | 9 | (*--algorithm LinearSearch 10 | 11 | variables 12 | N = 10, 13 | i = N, 14 | prev = i+1; 15 | 16 | define 17 | \* Invariant for the model checker: i is less than prev 18 | \* This is a safety condition we can check in every state 19 | CountHasDecreased == i(i=0) 28 | end define; 29 | 30 | begin 31 | 32 | while i > 0 do 33 | prev := i; 34 | i := i - 1; 35 | end while; 36 | 37 | 38 | end algorithm;*) 39 | \* BEGIN TRANSLATION 40 | VARIABLES N, i, prev, pc 41 | 42 | (* define statement *) 43 | CountHasDecreased == i(i=0) 52 | 53 | 54 | vars == << N, i, prev, pc >> 55 | 56 | Init == (* Global variables *) 57 | /\ N = 10 58 | /\ i = N 59 | /\ prev = i+1 60 | /\ pc = "Lbl_1" 61 | 62 | Lbl_1 == /\ pc = "Lbl_1" 63 | /\ IF i > 0 64 | THEN /\ prev' = i 65 | /\ i' = i - 1 66 | /\ pc' = "Lbl_1" 67 | ELSE /\ pc' = "Done" 68 | /\ UNCHANGED << i, prev >> 69 | /\ N' = N 70 | 71 | Next == Lbl_1 72 | \/ (* Disjunct to prevent deadlock on termination *) 73 | (pc = "Done" /\ UNCHANGED vars) 74 | 75 | Spec == /\ Init /\ [][Next]_vars 76 | /\ WF_vars(Next) 77 | 78 | Termination == <>(pc = "Done") 79 | 80 | \* END TRANSLATION 81 | ============================================================================= 82 | -------------------------------------------------------------------------------- /count-down/CountDown.toolbox/CountDown___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /count-down/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: Count Down 3 | 4 | This is a trivial countdown timer that counts from 10 to 0. No rocket 5 | ship is included. 6 | 7 | 8 | To validate that the count decreases, we can either model check with the invariant `CountHasDecreased` or with the equivalent temporal property `CountDecreases`. 9 | 10 | To ensure that the count reaches zero, we can check the temporal property `CountReachesZero`. 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /deadlock/Deadlock.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE Deadlock ---------------------------- 2 | 3 | \* See README.md 4 | 5 | EXTENDS Integers, TLC 6 | 7 | (*--algorithm Deadlock 8 | 9 | variables 10 | NOBODY = "", 11 | COMPETITORS = {"A", "B"}, 12 | LOCKS = {1, 2}, 13 | lock_holder = [ i \in LOCKS |-> NOBODY ]; 14 | 15 | define 16 | NotHeldByOther(lock, me) == lock_holder[lock]=me \/ lock_holder[lock]=NOBODY 17 | end define; 18 | 19 | process competitor \in COMPETITORS 20 | begin 21 | Loop: 22 | while TRUE do 23 | either 24 | Lock1: 25 | await NotHeldByOther(1, self); 26 | lock_holder[1] := self; 27 | or 28 | Lock2: 29 | await NotHeldByOther(2, self); 30 | lock_holder[2] := self; 31 | or 32 | Unlock1: 33 | if lock_holder[1] = self then 34 | lock_holder[1] := NOBODY; 35 | end if; 36 | or 37 | Unlock2: 38 | if lock_holder[2] = self then 39 | lock_holder[2] := NOBODY; 40 | end if; 41 | end either; 42 | end while; 43 | end process; 44 | 45 | 46 | 47 | end algorithm;*) 48 | \* BEGIN TRANSLATION 49 | VARIABLES NOBODY, COMPETITORS, LOCKS, lock_holder, pc 50 | 51 | (* define statement *) 52 | NotHeldByOther(lock, me) == lock_holder[lock]=me \/ lock_holder[lock]=NOBODY 53 | 54 | 55 | vars == << NOBODY, COMPETITORS, LOCKS, lock_holder, pc >> 56 | 57 | ProcSet == (COMPETITORS) 58 | 59 | Init == (* Global variables *) 60 | /\ NOBODY = "" 61 | /\ COMPETITORS = {"A", "B"} 62 | /\ LOCKS = {1, 2} 63 | /\ lock_holder = [ i \in LOCKS |-> NOBODY ] 64 | /\ pc = [self \in ProcSet |-> "Loop"] 65 | 66 | Loop(self) == /\ pc[self] = "Loop" 67 | /\ \/ /\ pc' = [pc EXCEPT ![self] = "Lock1"] 68 | \/ /\ pc' = [pc EXCEPT ![self] = "Lock2"] 69 | \/ /\ pc' = [pc EXCEPT ![self] = "Unlock1"] 70 | \/ /\ pc' = [pc EXCEPT ![self] = "Unlock2"] 71 | /\ UNCHANGED << NOBODY, COMPETITORS, LOCKS, lock_holder >> 72 | 73 | Lock1(self) == /\ pc[self] = "Lock1" 74 | /\ NotHeldByOther(1, self) 75 | /\ lock_holder' = [lock_holder EXCEPT ![1] = self] 76 | /\ pc' = [pc EXCEPT ![self] = "Loop"] 77 | /\ UNCHANGED << NOBODY, COMPETITORS, LOCKS >> 78 | 79 | Lock2(self) == /\ pc[self] = "Lock2" 80 | /\ NotHeldByOther(2, self) 81 | /\ lock_holder' = [lock_holder EXCEPT ![2] = self] 82 | /\ pc' = [pc EXCEPT ![self] = "Loop"] 83 | /\ UNCHANGED << NOBODY, COMPETITORS, LOCKS >> 84 | 85 | Unlock1(self) == /\ pc[self] = "Unlock1" 86 | /\ IF lock_holder[1] = self 87 | THEN /\ lock_holder' = [lock_holder EXCEPT ![1] = NOBODY] 88 | ELSE /\ TRUE 89 | /\ UNCHANGED lock_holder 90 | /\ pc' = [pc EXCEPT ![self] = "Loop"] 91 | /\ UNCHANGED << NOBODY, COMPETITORS, LOCKS >> 92 | 93 | Unlock2(self) == /\ pc[self] = "Unlock2" 94 | /\ IF lock_holder[2] = self 95 | THEN /\ lock_holder' = [lock_holder EXCEPT ![2] = NOBODY] 96 | ELSE /\ TRUE 97 | /\ UNCHANGED lock_holder 98 | /\ pc' = [pc EXCEPT ![self] = "Loop"] 99 | /\ UNCHANGED << NOBODY, COMPETITORS, LOCKS >> 100 | 101 | competitor(self) == Loop(self) \/ Lock1(self) \/ Lock2(self) 102 | \/ Unlock1(self) \/ Unlock2(self) 103 | 104 | Next == (\E self \in COMPETITORS: competitor(self)) 105 | 106 | Spec == Init /\ [][Next]_vars 107 | 108 | \* END TRANSLATION 109 | 110 | ============================================================================= 111 | -------------------------------------------------------------------------------- /deadlock/Deadlock.toolbox/Deadlock___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /deadlock/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: Deadlock 3 | 4 | This is the simplest example I could think of that exhibits deadlock. 5 | 6 | Two processes (`A` and `B`) compete for two resources (`Lock1` and `Lock2`). 7 | 8 | There are no invariants or properties to check here. Just create a default model and run it, and it will fail with a reported deadlock. A typical example looks like this in the stack trace: 9 | 10 | 11 | ``` 12 | lock_holder: <<"A", "B">> 13 | pc: [A |-> "Lock2", B |-> "Lock1"] 14 | ``` 15 | 16 | In this case, `Lock1` is held by `A` (the first element of `lock_holder`) and `Lock2` is held by `B`. `A` now wants to acquire `Lock2` and `B` wants to acquire `Lock1`, so nobody can proceed. 17 | 18 | 19 | -------------------------------------------------------------------------------- /extends/ExtendsA.tla: -------------------------------------------------------------------------------- 1 | 2 | ---- MODULE ExtendsA ---- 3 | EXTENDS Integers 4 | 5 | \* See README.md 6 | 7 | IsEven(x) == (x % 2) = 0 8 | IsOdd(x) == (x % 2) = 1 9 | 10 | ==== 11 | 12 | 13 | -------------------------------------------------------------------------------- /extends/ExtendsB.tla: -------------------------------------------------------------------------------- 1 | 2 | ---- MODULE ExtendsB ---- 3 | 4 | EXTENDS ExtendsA, TLC 5 | 6 | \* see README.md 7 | 8 | (*--algorithm ExtendsB 9 | begin 10 | 11 | assert( IsEven(2) ); 12 | assert( IsEven(-2) ); 13 | assert( IsOdd(3) ); 14 | assert( IsOdd(-3) ); 15 | 16 | end algorithm;*) 17 | \* BEGIN TRANSLATION 18 | VARIABLE pc 19 | 20 | vars == << pc >> 21 | 22 | Init == /\ pc = "Lbl_1" 23 | 24 | Lbl_1 == /\ pc = "Lbl_1" 25 | /\ Assert(( IsEven(2) ), 26 | "Failure of assertion at line 11, column 1.") 27 | /\ Assert(( IsEven(-2) ), 28 | "Failure of assertion at line 12, column 1.") 29 | /\ Assert(( IsOdd(3) ), 30 | "Failure of assertion at line 13, column 1.") 31 | /\ Assert(( IsOdd(-3) ), 32 | "Failure of assertion at line 14, column 1.") 33 | /\ pc' = "Done" 34 | 35 | Next == Lbl_1 36 | \/ (* Disjunct to prevent deadlock on termination *) 37 | (pc = "Done" /\ UNCHANGED vars) 38 | 39 | Spec == Init /\ [][Next]_vars 40 | 41 | Termination == <>(pc = "Done") 42 | 43 | \* END TRANSLATION 44 | 45 | ==== 46 | -------------------------------------------------------------------------------- /extends/ExtendsB.toolbox/ExtendsB___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /extends/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: Extends 3 | 4 | This is a trivial example of having one module (`ExtendsB`) extend and use definitions 5 | from another (`ExtendsA`). 6 | 7 | `ExtendsA` just defines the two operators `IsEven` and `IsOdd`. 8 | 9 | `ExtendsB` EXTENDS `ExtendsA` and models a trivial program that exercises the operators. 10 | 11 | When creating a spec in the TLA+ explorer, use `ExtendsB` as the root-module file. The explorer will automatically load both modules into editor tabs. 12 | 13 | -------------------------------------------------------------------------------- /implements/ImplementsA.tla: -------------------------------------------------------------------------------- 1 | 2 | ---- MODULE ImplementsA ---- 3 | LOCAL INSTANCE Integers 4 | CONSTANT A 5 | ASSUME A \in Int 6 | 7 | \* See README.md 8 | 9 | IsEven == (A % 2) = 0 10 | IsOdd == (A % 2) = 1 11 | 12 | ==== 13 | -------------------------------------------------------------------------------- /implements/ImplementsB.tla: -------------------------------------------------------------------------------- 1 | 2 | ---- MODULE ImplementsB ---- 3 | 4 | EXTENDS TLC, Integers 5 | 6 | \* see README.md 7 | 8 | 9 | 10 | (*--algorithm ImplementsB 11 | variables 12 | a = 2, 13 | b = 3; 14 | 15 | define 16 | \* We can initialize our instances from variables, but 17 | \* as we see below, changing the variable does not then update 18 | \* the instance 19 | X == INSTANCE ImplementsA WITH A <- a 20 | Y == INSTANCE ImplementsA WITH A <- b 21 | \* We can also have an instance that requires parameters 22 | \* when it's used. 23 | Z(A) == INSTANCE ImplementsA 24 | end define; 25 | 26 | begin 27 | 28 | assert( X!IsEven ); 29 | assert( Y!IsOdd ); 30 | 31 | \* This changes a but not X!A 32 | a := 3; 33 | assert( X!IsEven ); 34 | 35 | \* This changes Z!A 36 | a := 3; 37 | assert( Z(a)!IsOdd ); 38 | a := 4; 39 | assert( Z(a)!IsEven ); 40 | 41 | 42 | end algorithm;*) 43 | \* BEGIN TRANSLATION 44 | VARIABLES a, b, pc 45 | 46 | (* define statement *) 47 | X == INSTANCE ImplementsA WITH A <- a 48 | Y == INSTANCE ImplementsA WITH A <- b 49 | 50 | 51 | Z(A) == INSTANCE ImplementsA 52 | 53 | 54 | vars == << a, b, pc >> 55 | 56 | Init == (* Global variables *) 57 | /\ a = 2 58 | /\ b = 3 59 | /\ pc = "Lbl_1" 60 | 61 | Lbl_1 == /\ pc = "Lbl_1" 62 | /\ Assert(( X!IsEven ), 63 | "Failure of assertion at line 28, column 1.") 64 | /\ Assert(( Y!IsOdd ), "Failure of assertion at line 29, column 1.") 65 | /\ a' = 3 66 | /\ Assert(( X!IsEven ), 67 | "Failure of assertion at line 33, column 1.") 68 | /\ pc' = "Lbl_2" 69 | /\ b' = b 70 | 71 | Lbl_2 == /\ pc = "Lbl_2" 72 | /\ a' = 3 73 | /\ Assert(( Z(a')!IsOdd ), 74 | "Failure of assertion at line 37, column 1.") 75 | /\ pc' = "Lbl_3" 76 | /\ b' = b 77 | 78 | Lbl_3 == /\ pc = "Lbl_3" 79 | /\ a' = 4 80 | /\ Assert(( Z(a')!IsEven ), 81 | "Failure of assertion at line 39, column 1.") 82 | /\ pc' = "Done" 83 | /\ b' = b 84 | 85 | Next == Lbl_1 \/ Lbl_2 \/ Lbl_3 86 | \/ (* Disjunct to prevent deadlock on termination *) 87 | (pc = "Done" /\ UNCHANGED vars) 88 | 89 | Spec == Init /\ [][Next]_vars 90 | 91 | Termination == <>(pc = "Done") 92 | 93 | \* END TRANSLATION 94 | 95 | ==== 96 | -------------------------------------------------------------------------------- /implements/ImplementsB.toolbox/ImplementsB___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /implements/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: Implements 3 | 4 | This is a trivial example of having one module (`ImplementsB`) extend and use definitions 5 | from another (`ImplementsA`) with parameterization and namespacing. 6 | 7 | `ImplementsA` just defines the (local) variable `A`. 8 | 9 | `ImplementsB` creates two instances of `ImplementsA`, one named `X` and one named `Y`. 10 | 11 | When creating a spec in the TLA+ explorer, use `ImplementsB` as the root-module file. The explorer will automatically load both modules into editor tabs. 12 | 13 | -------------------------------------------------------------------------------- /linear-search/LinearSearch.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE LinearSearch ---------------------------- 2 | 3 | \* See README.md 4 | 5 | EXTENDS Naturals, TLC 6 | 7 | CONSTANT N (* Size of arrays *) 8 | CONSTANT MAXINT (* Max integer value *) 9 | 10 | (* PlusCal options (-termination) *) 11 | 12 | (*--algorithm LinearSearch 13 | 14 | variables 15 | ar \in [ 1..N -> 0..MAXINT ], (* Array of N integers in 0..MAXINT *) 16 | x \in 0..MAXINT, (* Value to find *) 17 | found = FALSE, 18 | i = 1; 19 | 20 | begin 21 | 22 | Loop: 23 | while i <= N /\ ~found do 24 | found := ar[i]=x; 25 | i := i + 1; 26 | end while; 27 | 28 | FinalCheck: 29 | assert( found <=> (\E j \in 1..N : ar[j] = x) ) 30 | 31 | end algorithm;*) 32 | \* BEGIN TRANSLATION 33 | VARIABLES ar, x, found, i, pc 34 | 35 | vars == << ar, x, found, i, pc >> 36 | 37 | Init == (* Global variables *) 38 | /\ ar \in [ 1..N -> 0..MAXINT ] 39 | /\ x \in 0..MAXINT 40 | /\ found = FALSE 41 | /\ i = 1 42 | /\ pc = "Loop" 43 | 44 | Loop == /\ pc = "Loop" 45 | /\ IF i <= N /\ ~found 46 | THEN /\ found' = (ar[i]=x) 47 | /\ i' = i + 1 48 | /\ pc' = "Loop" 49 | ELSE /\ pc' = "FinalCheck" 50 | /\ UNCHANGED << found, i >> 51 | /\ UNCHANGED << ar, x >> 52 | 53 | FinalCheck == /\ pc = "FinalCheck" 54 | /\ Assert(( found <=> (\E j \in 1..N : ar[j] = x) ), 55 | "Failure of assertion at line 29, column 5.") 56 | /\ pc' = "Done" 57 | /\ UNCHANGED << ar, x, found, i >> 58 | 59 | Next == Loop \/ FinalCheck 60 | \/ (* Disjunct to prevent deadlock on termination *) 61 | (pc = "Done" /\ UNCHANGED vars) 62 | 63 | Spec == /\ Init /\ [][Next]_vars 64 | /\ WF_vars(Next) 65 | 66 | Termination == <>(pc = "Done") 67 | 68 | \* END TRANSLATION 69 | 70 | ============================================================================= 71 | -------------------------------------------------------------------------------- /linear-search/LinearSearch.toolbox/LinearSearch___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /linear-search/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: Linear Search 3 | 4 | This is an extremely simple example of using TLA+ to validate the behavior of a single-threaded algorithm. 5 | 6 | This is adapted from the the binary search example at: 7 | http://herbrete.vvv.enseirb-matmeca.fr/IF311/lecture01.xhtml 8 | 9 | 10 | To validate this algorithm, create a model with values like 11 | 12 | ``` 13 | N = 3 14 | MAXINT = 5 15 | ``` 16 | 17 | The model checker will examine all MAXINT^N possible input arrays for 18 | all MAXINT possible values of X and ensure the correct result is returned. 19 | 20 | To see the model checker FAIL, just break the final condition, for example 21 | by specifying `ar[j] = x-1` 22 | 23 | 24 | -------------------------------------------------------------------------------- /list-operations/ListOperations.tla: -------------------------------------------------------------------------------- 1 | --------------------------- MODULE ListOperations --------------------------- 2 | 3 | \* See README.md 4 | 5 | EXTENDS Naturals, Sequences, TLC 6 | 7 | CONSTANT N (* Number of elements *) 8 | 9 | (* PlusCal options (-termination) *) 10 | 11 | (*--algorithm ListOperations 12 | 13 | variables 14 | list = << >>, 15 | rlist = << >>, 16 | i = 1; 17 | 18 | define 19 | \* List reversal operator in declarative style 20 | Reverse(seq) == [ j \in 1..Len(seq) |-> seq[ Len(seq) - j + 1 ] ] 21 | 22 | \* List reversal operator in functional style 23 | RECURSIVE RecursiveReverse2(_, _) 24 | RecursiveReverse2(seq, accum) == IF seq = <<>> THEN accum ELSE RecursiveReverse2( Tail(seq), << Head(seq) >> \o accum ) 25 | RecursiveReverse(seq) == RecursiveReverse2(seq, << >>) 26 | end define; 27 | 28 | begin 29 | 30 | Loop: 31 | while i <= N do 32 | 33 | AppendToList: 34 | list := Append(list, i); 35 | rlist := << i >> \o rlist; 36 | 37 | CheckList: 38 | assert( Head(list) = 1 ); 39 | assert( Head(rlist) = i ); 40 | 41 | assert( list[i] = i ); 42 | assert( rlist[i] = 1 ); 43 | 44 | assert( Len(list) = i ); 45 | assert( Len(rlist) = i ); 46 | 47 | assert( Reverse(list) = rlist ); 48 | assert( Reverse(rlist) = list ); 49 | 50 | assert( RecursiveReverse(list) = rlist ); 51 | assert( RecursiveReverse(rlist) = list ); 52 | 53 | IncrementCounter: 54 | i := i + 1; 55 | 56 | end while; 57 | 58 | 59 | end algorithm;*) 60 | \* BEGIN TRANSLATION 61 | VARIABLES list, rlist, i, pc 62 | 63 | (* define statement *) 64 | Reverse(seq) == [ j \in 1..Len(seq) |-> seq[ Len(seq) - j + 1 ] ] 65 | 66 | 67 | RECURSIVE RecursiveReverse2(_, _) 68 | RecursiveReverse2(seq, accum) == IF seq = <<>> THEN accum ELSE RecursiveReverse2( Tail(seq), << Head(seq) >> \o accum ) 69 | RecursiveReverse(seq) == RecursiveReverse2(seq, << >>) 70 | 71 | 72 | vars == << list, rlist, i, pc >> 73 | 74 | Init == (* Global variables *) 75 | /\ list = << >> 76 | /\ rlist = << >> 77 | /\ i = 1 78 | /\ pc = "Loop" 79 | 80 | Loop == /\ pc = "Loop" 81 | /\ IF i <= N 82 | THEN /\ pc' = "AppendToList" 83 | ELSE /\ pc' = "Done" 84 | /\ UNCHANGED << list, rlist, i >> 85 | 86 | AppendToList == /\ pc = "AppendToList" 87 | /\ list' = Append(list, i) 88 | /\ rlist' = << i >> \o rlist 89 | /\ pc' = "CheckList" 90 | /\ i' = i 91 | 92 | CheckList == /\ pc = "CheckList" 93 | /\ Assert(( Head(list) = 1 ), 94 | "Failure of assertion at line 38, column 9.") 95 | /\ Assert(( Head(rlist) = i ), 96 | "Failure of assertion at line 39, column 9.") 97 | /\ Assert(( list[i] = i ), 98 | "Failure of assertion at line 41, column 9.") 99 | /\ Assert(( rlist[i] = 1 ), 100 | "Failure of assertion at line 42, column 9.") 101 | /\ Assert(( Len(list) = i ), 102 | "Failure of assertion at line 44, column 9.") 103 | /\ Assert(( Len(rlist) = i ), 104 | "Failure of assertion at line 45, column 9.") 105 | /\ Assert(( Reverse(list) = rlist ), 106 | "Failure of assertion at line 47, column 9.") 107 | /\ Assert(( Reverse(rlist) = list ), 108 | "Failure of assertion at line 48, column 9.") 109 | /\ Assert(( RecursiveReverse(list) = rlist ), 110 | "Failure of assertion at line 50, column 9.") 111 | /\ Assert(( RecursiveReverse(rlist) = list ), 112 | "Failure of assertion at line 51, column 9.") 113 | /\ pc' = "IncrementCounter" 114 | /\ UNCHANGED << list, rlist, i >> 115 | 116 | IncrementCounter == /\ pc = "IncrementCounter" 117 | /\ i' = i + 1 118 | /\ pc' = "Loop" 119 | /\ UNCHANGED << list, rlist >> 120 | 121 | Next == Loop \/ AppendToList \/ CheckList \/ IncrementCounter 122 | \/ (* Disjunct to prevent deadlock on termination *) 123 | (pc = "Done" /\ UNCHANGED vars) 124 | 125 | Spec == /\ Init /\ [][Next]_vars 126 | /\ WF_vars(Next) 127 | 128 | Termination == <>(pc = "Done") 129 | 130 | \* END TRANSLATION 131 | 132 | ============================================================================= 133 | -------------------------------------------------------------------------------- /list-operations/ListOperations.toolbox/ListOperations___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /list-operations/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: List Operations 3 | 4 | This specification just demonstrates how to perform basic list operations on sequences. 5 | 6 | In addition to using the operators `Append`, `Head`, `Tail`, and `\o` from the `Sequences` module, this example defines two versions of a `Reverse` operator, one in a declarative style and one in a functional style using a recursive operator definition. 7 | 8 | 9 | -------------------------------------------------------------------------------- /race-condition/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: Race Condition 3 | 4 | This is the simplest example I could think of that exhibits a race condition. 5 | 6 | We start with a variable, `count=0`. 7 | 8 | Two processes (`A` and `B`) each attempt to read count and increment it. 9 | 10 | If this were atomic, then we would end with `count=2`, but since the reads can overlap this sometimes fails. 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /race-condition/RaceCondition.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE RaceCondition ---------------------------- 2 | 3 | \* See README.md 4 | 5 | EXTENDS Integers, TLC 6 | 7 | (* PlusCal options (-termination) *) 8 | 9 | (*--algorithm RaceCondition 10 | 11 | variables 12 | count = 0; 13 | 14 | define 15 | \* Temporal property to check: After each of our two processes has incremented 16 | \* count, count should be 2. (This fails because one process may read a stale value 17 | \* while the other is in the middle of incrementing.) 18 | CountReachesTwo == <>[](count=2) 19 | end define; 20 | 21 | process p \in {"A", "B"} 22 | variable v = -1; 23 | begin 24 | Read: 25 | v := count; 26 | Write: 27 | count := v+1; 28 | end process; 29 | 30 | end algorithm;*) 31 | \* BEGIN TRANSLATION 32 | VARIABLES count, pc 33 | 34 | (* define statement *) 35 | CountReachesTwo == <>[](count=2) 36 | 37 | VARIABLE v 38 | 39 | vars == << count, pc, v >> 40 | 41 | ProcSet == ({"A", "B"}) 42 | 43 | Init == (* Global variables *) 44 | /\ count = 0 45 | (* Process p *) 46 | /\ v = [self \in {"A", "B"} |-> -1] 47 | /\ pc = [self \in ProcSet |-> "Read"] 48 | 49 | Read(self) == /\ pc[self] = "Read" 50 | /\ v' = [v EXCEPT ![self] = count] 51 | /\ pc' = [pc EXCEPT ![self] = "Write"] 52 | /\ count' = count 53 | 54 | Write(self) == /\ pc[self] = "Write" 55 | /\ count' = v[self]+1 56 | /\ pc' = [pc EXCEPT ![self] = "Done"] 57 | /\ v' = v 58 | 59 | p(self) == Read(self) \/ Write(self) 60 | 61 | Next == (\E self \in {"A", "B"}: p(self)) 62 | \/ (* Disjunct to prevent deadlock on termination *) 63 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 64 | 65 | Spec == /\ Init /\ [][Next]_vars 66 | /\ \A self \in {"A", "B"} : WF_vars(p(self)) 67 | 68 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 69 | 70 | \* END TRANSLATION 71 | 72 | \* END TRANSLATION 73 | 74 | ============================================================================= 75 | -------------------------------------------------------------------------------- /race-condition/RaceCondition.toolbox/RaceCondition___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /secret-santa/README.md: -------------------------------------------------------------------------------- 1 | # Tiny TLA+ Example: Secret Santa 2 | 3 | This is an extremely simple example of using TLA+ to model a concurrent system. 4 | 5 | In our system, we have N people participating in a "Secret Santa" exchange. Each person is assigned one other person to give a gift to. They can do this in any order. Once the exchange completes, we expect each person to have received a gift. 6 | 7 | There's no opportunity for deadlock here. 8 | 9 | To validate that the exchange succeeds, create a model with values like 10 | 11 | ``` 12 | N = 3 13 | ``` 14 | 15 | The model can also specify the invariants 16 | 17 | ``` 18 | Termination 19 | EverybodyGetsAGift 20 | ``` 21 | 22 | The model checker will run the gift exchange with every possible assignment 23 | of givers to receivers and confirm that they all succeed. 24 | 25 | -------------------------------------------------------------------------------- /secret-santa/SecretSanta.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE SecretSanta ---------------------------- 2 | 3 | \* See README.md 4 | 5 | EXTENDS Naturals, TLC 6 | 7 | CONSTANT N (* Number of participants *) 8 | 9 | (* PlusCal options (-termination) *) 10 | 11 | (*--algorithm SecretSanta 12 | 13 | variables 14 | (* a legal assignment maps each giver to a unique receiver different from themself *) 15 | assignment \in {p \in Permutations(1..N) : (\A i \in (1..N): (p[i] /= i))}, 16 | (* keep track of who has received a gift *) 17 | received = [ i \in 1..N |-> FALSE], 18 | (* keep track of how many gifts have been given *) 19 | gifts_given = 0; 20 | 21 | define 22 | (* Temporal Invariant -- add this as a property of the model. 23 | Eventually everybody has a gift. *) 24 | EverybodyGetsGift == <>[]( \A i \in 1..N: received[i] ) 25 | end define; 26 | 27 | 28 | (* Everybody will, in their own time, give a gift to their assigned receiver *) 29 | process giver \in (1..N) 30 | variables 31 | recipient = assignment[ self ]; 32 | begin 33 | Give: 34 | received[ recipient ] := TRUE; 35 | gifts_given := gifts_given + 1; 36 | end process; 37 | 38 | (* We could also have a monitor who checks that the gift exchange has 39 | succeeded. For this spec it's redundant since we have a stronger 40 | check with the invariant EverybodyGetsGift. But if it were possible 41 | for some behaviors where everybody gives their gift and some behaviors 42 | where the exchange never finishes, then this monitor would still validate 43 | the end state for behaviors where the exchange does complete. *) 44 | process monitor = N+1 45 | begin 46 | Monitor: 47 | await gifts_given=N; 48 | assert( \A i \in 1..N : received[i]) 49 | end process; 50 | 51 | 52 | end algorithm;*) 53 | \* BEGIN TRANSLATION 54 | VARIABLES assignment, received, gifts_given, pc 55 | 56 | (* define statement *) 57 | EverybodyGetsGift == <>[]( \A i \in 1..N: received[i] ) 58 | 59 | VARIABLE recipient 60 | 61 | vars == << assignment, received, gifts_given, pc, recipient >> 62 | 63 | ProcSet == ((1..N)) \cup {N+1} 64 | 65 | Init == (* Global variables *) 66 | /\ assignment \in {p \in Permutations(1..N) : (\A i \in (1..N): (p[i] /= i))} 67 | /\ received = [ i \in 1..N |-> FALSE] 68 | /\ gifts_given = 0 69 | (* Process giver *) 70 | /\ recipient = [self \in (1..N) |-> assignment[ self ]] 71 | /\ pc = [self \in ProcSet |-> CASE self \in (1..N) -> "Give" 72 | [] self = N+1 -> "Monitor"] 73 | 74 | Give(self) == /\ pc[self] = "Give" 75 | /\ received' = [received EXCEPT ![ recipient[self] ] = TRUE] 76 | /\ gifts_given' = gifts_given + 1 77 | /\ pc' = [pc EXCEPT ![self] = "Done"] 78 | /\ UNCHANGED << assignment, recipient >> 79 | 80 | giver(self) == Give(self) 81 | 82 | Monitor == /\ pc[N+1] = "Monitor" 83 | /\ gifts_given=N 84 | /\ Assert(( \A i \in 1..N : received[i]), 85 | "Failure of assertion at line 48, column 5.") 86 | /\ pc' = [pc EXCEPT ![N+1] = "Done"] 87 | /\ UNCHANGED << assignment, received, gifts_given, recipient >> 88 | 89 | monitor == Monitor 90 | 91 | Next == monitor 92 | \/ (\E self \in (1..N): giver(self)) 93 | \/ (* Disjunct to prevent deadlock on termination *) 94 | ((\A self \in ProcSet: pc[self] = "Done") /\ UNCHANGED vars) 95 | 96 | Spec == /\ Init /\ [][Next]_vars 97 | /\ \A self \in (1..N) : WF_vars(giver(self)) 98 | /\ WF_vars(monitor) 99 | 100 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 101 | 102 | \* END TRANSLATION 103 | 104 | ============================================================================= 105 | -------------------------------------------------------------------------------- /secret-santa/SecretSanta.toolbox/SecretSanta___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /simple-logic/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tiny TLA+ Example: Simple Logic 3 | 4 | This is a TLA+ module that asserts some simple logical formulas to 5 | be true. It is modeled on https://github.com/tlaplus/Examples/blob/master/specifications/SpecifyingSystems/SimpleMath/SimpleMath.tla 6 | 7 | Running model code like this is a good way to: 8 | 9 | * confirm your grasp of TLA+ mathematical syntax 10 | * check truth-tables if your 1st order logic is rusty 11 | * etc. 12 | 13 | You can run this model with the model checker specifying the invariant `Implication`. 14 | 15 | 16 | At the end of the TLA file there is also an `ASSUME` statement. This is checked as part of loading the model, and it covers exactly the same logical equation in a single statement. You can execute this test by running the model checker with **No behavior spec** selected. If you try this with a broken formula, for example by changing the last clause from `~F \/ G` to `~F \/ ~G` the model checking results will show you an error even though no state count or other model checking information is generated. 17 | -------------------------------------------------------------------------------- /simple-logic/SimpleLogic.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE SimpleLogic ---------------------------- 2 | 3 | (***************************************************************************) 4 | (* Some simple experiments in logic *) 5 | (***************************************************************************) 6 | 7 | VARIABLES X, Y 8 | 9 | vars == << X, Y >> 10 | 11 | 12 | (***************************************************************************) 13 | (* Discover all entries in a 2-value truth table. *) 14 | (* *) 15 | (* This Init/Next spec will visit all 4 entries. Since state transitions *) 16 | (* are arbitrary, it will never deadlock. *) 17 | (***************************************************************************) 18 | 19 | Init == 20 | /\ X \in {TRUE, FALSE} 21 | /\ Y \in {TRUE, FALSE} 22 | 23 | Next == 24 | /\ X' \in {TRUE, FALSE} 25 | /\ Y' \in {TRUE, FALSE} 26 | 27 | (***************************************************************************) 28 | (* Check the definition of implication. *) 29 | (* -> Specify Implication as an invariant of the model. *) 30 | (***************************************************************************) 31 | 32 | Foo == 33 | /\ X => Y 34 | 35 | Bar == 36 | /\ ~X \/ Y 37 | 38 | Implication == 39 | /\ (Foo = Bar) 40 | 41 | 42 | 43 | 44 | 45 | (***************************************************************************) 46 | (* Check the same thing using an ASSUME. *) 47 | (* No model steps necessary, you can run the model with no behavioral spec.*) 48 | (***************************************************************************) 49 | 50 | ASSUME 51 | \A F, G \in {TRUE, FALSE} : (F => G) <=> ~F \/ G 52 | 53 | 54 | 55 | ============================================================================= 56 | \* Modification History 57 | \* Last modified Mon Apr 01 16:45:57 CDT 2019 by eric.johnson 58 | \* Created Thu Oct 11 15:43:40 CDT 2018 by eric.johnson 59 | 60 | -------------------------------------------------------------------------------- /simple-logic/SimpleLogic.toolbox/SimpleLogic___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /state-machine/PlusCalDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lostbearlabs/tiny-tlaplus-examples/7c25faa7495da0e29dd5796a0330b4e303a3e2ae/state-machine/PlusCalDiagram.png -------------------------------------------------------------------------------- /state-machine/README.md: -------------------------------------------------------------------------------- 1 | # Tiny TLA+ Example: State Machine 2 | 3 | This example models a trivial state machine that accepts the regular expression `X*Y` (i.e. zero or more X followed by a Y). 4 | 5 | The state diagram looks like this: 6 | 7 | ![State Machine for X*Y](StateMachineDiagram.png) 8 | 9 | Since we have modelled the machine using PlusCal, the TLA+ model also includes a variable called `PC` (for "program counter") representing the current point of execution. The model checker therefore finds *4* states (instead of 3) with the following state diagram: 10 | 11 | 12 | ![PlusCal State Machine](PlusCalDiagram.png) 13 | -------------------------------------------------------------------------------- /state-machine/StateMachine: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lostbearlabs/tiny-tlaplus-examples/7c25faa7495da0e29dd5796a0330b4e303a3e2ae/state-machine/StateMachine -------------------------------------------------------------------------------- /state-machine/StateMachine.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE StateMachine ---------------------------- 2 | 3 | \* See README.md 4 | 5 | (*--fair algorithm stateMachine 6 | variables 7 | state = "start"; 8 | 9 | define 10 | \* INVARIANT: we should always be in a valid state 11 | StateValid == state \in {"start", "reading", "end"} 12 | 13 | \* TEMPORAL FORMULA: since the process is fair, we will always 14 | \* eventually get a Y and terminate, and once we terminate we will 15 | \* stay terminated. 16 | \* 17 | \* NOTE: this condition does not actually hold true. 18 | \* See: https://stackoverflow.com/questions/55128505/tla-why-does-fair-algorithm-still-stutter 19 | MachineTerminates == <>[](state = "end") 20 | end define; 21 | 22 | begin 23 | 24 | 25 | Loop: 26 | while state /= "end" do 27 | either 28 | \* we got an X, keep going 29 | state := "reading" 30 | or 31 | \* we got a Y, terminate 32 | state := "end" 33 | end either; 34 | end while; 35 | 36 | 37 | end algorithm;*) 38 | \* BEGIN TRANSLATION 39 | VARIABLES state, pc 40 | 41 | (* define statement *) 42 | StateValid == state \in {"start", "reading", "end"} 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | MachineTerminates == <>[](state = "end") 51 | 52 | 53 | vars == << state, pc >> 54 | 55 | Init == (* Global variables *) 56 | /\ state = "start" 57 | /\ pc = "Loop" 58 | 59 | Loop == /\ pc = "Loop" 60 | /\ IF state /= "end" 61 | THEN /\ \/ /\ state' = "reading" 62 | \/ /\ state' = "end" 63 | /\ pc' = "Loop" 64 | ELSE /\ pc' = "Done" 65 | /\ state' = state 66 | 67 | Next == Loop 68 | \/ (* Disjunct to prevent deadlock on termination *) 69 | (pc = "Done" /\ UNCHANGED vars) 70 | 71 | Spec == /\ Init /\ [][Next]_vars 72 | /\ WF_vars(Next) 73 | 74 | Termination == <>(pc = "Done") 75 | 76 | \* END TRANSLATION 77 | 78 | 79 | ============================================================================= 80 | -------------------------------------------------------------------------------- /state-machine/StateMachine.toolbox/StateMachine___Model_1.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /state-machine/StateMachineDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lostbearlabs/tiny-tlaplus-examples/7c25faa7495da0e29dd5796a0330b4e303a3e2ae/state-machine/StateMachineDiagram.png --------------------------------------------------------------------------------