├── .gitignore ├── LICENSE ├── LinQueue.tla ├── LinQueuePlusCal.tla ├── Linearizability.tla ├── README.md ├── TestLegalQueue.tla ├── TestLinQueue.tla ├── TestLinearizability.tla ├── TestRespectsPrecedenceOrdering.tla ├── Utilities.tla ├── fig1.png ├── h2.png └── h3.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.toolbox 2 | *.old 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LinQueue.tla: -------------------------------------------------------------------------------- 1 | ----------------------------- MODULE LinQueue ------------------------------- 2 | EXTENDS Naturals, Sequences, TLC 3 | 4 | opInvocations == {"E", "D"} 5 | opResponse == "Ok" 6 | 7 | Values == {"x", "y", "z"} 8 | Processes == {"A", "B", "C"} 9 | 10 | \* Process subhistory 11 | H|P == SelectSeq(H, LAMBDA e : e.proc = P) 12 | 13 | PossibleResponses(e) == 14 | CASE e.op = "E" -> {[op|->"Ok", proc|->e.proc]} 15 | [] e.op = "D" -> [op:{"Ok"}, proc:{e.proc}, val:Values] 16 | 17 | IsInvocation(e) == e.op \in opInvocations 18 | IsResponse(e) == e.op = opResponse 19 | 20 | Matches(H, i, j) == 21 | /\ H[i].proc = H[j].proc 22 | /\ H[i].op \in opInvocations 23 | /\ H[j].op = opResponse 24 | /\ "val" \in DOMAIN H[j] <=> H[i].op = "D" \* Only dequeues have values in the response 25 | /\ ~\E k \in (i+1)..(j-1) : H[k].proc = H[i].proc 26 | 27 | 28 | RECURSIVE LegalQueue(_, _) 29 | 30 | \* Check if a history h is legal given an initial queue state q 31 | LegalQueue(h, q) == \/ h = << >> 32 | \/ LET first == Head(h) 33 | rest == Tail(h) 34 | IN \/ /\ first.op = "E" 35 | /\ LegalQueue(rest, Append(q, first.val)) 36 | \/ /\ first.op = "D" 37 | /\ Len(q)>0 38 | /\ first.val = Head(q) 39 | /\ LegalQueue(rest, Tail(q)) 40 | 41 | IsLegalQueueHistory(h) == LegalQueue(h, << >>) 42 | 43 | \* Given a sequential history H, true if it represents a legal queue history 44 | IsLegal(H) == 45 | LET RECURSIVE Helper(_, _) 46 | Helper(h, s) == IF h = << >> THEN IsLegalQueueHistory(s) 47 | ELSE LET hr == Tail(Tail(h)) 48 | inv == h[1] 49 | res == h[2] 50 | e == [op|->inv.op, val|-> IF inv.op = "E" THEN inv.val ELSE res.val] 51 | IN Helper(hr, Append(s, e)) 52 | IN Helper(H, <<>>) 53 | 54 | L == INSTANCE Linearizability 55 | 56 | IsLinearizableHistory(H) == L!IsLinearizableHistory(H) 57 | IsLin(H) == IsLinearizableHistory(H) \* Shorter alias 58 | 59 | Linearize(H) == L!Linearize(H) 60 | 61 | ============================================================================= 62 | -------------------------------------------------------------------------------- /LinQueuePlusCal.tla: -------------------------------------------------------------------------------- 1 | -------------------------- MODULE LinQueuePlusCal -------------------------- 2 | EXTENDS LinQueue, Utilities 3 | 4 | CONSTANT H 5 | 6 | (* 7 | --algorithm FindLinearization 8 | variables 9 | linearizable = FALSE, 10 | completeHp, f, S; 11 | 12 | begin 13 | 14 | with Hp \in L!ExtendedHistories(H) do 15 | completeHp := L!Complete(Hp); 16 | end with; 17 | with x \in L!Orderings(Len(completeHp)) do 18 | f := x; 19 | end with; 20 | S := completeHp ** f; 21 | linearizable := /\ L!IsSequential(S) 22 | /\ IsLegal(S) 23 | /\ L!AreEquivalent(S, completeHp) 24 | /\ L!RespectsPrecedenceOrdering(H, S) 25 | end algorithm 26 | *) 27 | \* BEGIN TRANSLATION 28 | CONSTANT defaultInitValue 29 | VARIABLES linearizable, completeHp, f, S, pc 30 | 31 | vars == << linearizable, completeHp, f, S, pc >> 32 | 33 | Init == (* Global variables *) 34 | /\ linearizable = FALSE 35 | /\ completeHp = defaultInitValue 36 | /\ f = defaultInitValue 37 | /\ S = defaultInitValue 38 | /\ pc = "Lbl_1" 39 | 40 | Lbl_1 == /\ pc = "Lbl_1" 41 | /\ \E Hp \in L!ExtendedHistories(H): 42 | completeHp' = L!Complete(Hp) 43 | /\ \E x \in L!Orderings(Len(completeHp')): 44 | f' = x 45 | /\ S' = completeHp' ** f' 46 | /\ linearizable' = (/\ L!IsSequential(S') 47 | /\ IsLegal(S') 48 | /\ L!AreEquivalent(S', completeHp') 49 | /\ L!RespectsPrecedenceOrdering(H, S')) 50 | /\ pc' = "Done" 51 | 52 | Next == Lbl_1 53 | \/ (* Disjunct to prevent deadlock on termination *) 54 | (pc = "Done" /\ UNCHANGED vars) 55 | 56 | Spec == Init /\ [][Next]_vars 57 | 58 | Termination == <>(pc = "Done") 59 | 60 | \* END TRANSLATION 61 | 62 | 63 | 64 | ============================================================================= 65 | \* Modification History 66 | \* Last modified Mon Oct 22 19:40:16 PDT 2018 by lhochstein 67 | \* Created Sun Oct 21 19:33:05 PDT 2018 by lhochstein 68 | -------------------------------------------------------------------------------- /Linearizability.tla: -------------------------------------------------------------------------------- 1 | -------------------------- MODULE Linearizability -------------------------- 2 | 3 | EXTENDS Naturals, Sequences, Utilities 4 | 5 | CONSTANT PossibleResponses(_) \* Argument is a history 6 | CONSTANT IsInvocation(_) \* Argument is event 7 | CONSTANT IsResponse(_) \* Argument is event 8 | CONSTANT Matches(_, _, _) \* Arguments are sequence, index, index 9 | CONSTANT IsLegal(_) 10 | CONSTANT _|_ 11 | CONSTANT Processes 12 | 13 | \* Transpose a set of sets 14 | \* Collect({{"a","b"}, {"x","y"}}) => {{"x", "a"}, {"x", "b"}, {"a", "y"}, {"b", "y"}} 15 | RECURSIVE Collect(_) 16 | 17 | Collect(S) == 18 | IF S = {} THEN {{}} ELSE 19 | LET s == CHOOSE s \in S : TRUE 20 | R == Collect(S \ {s}) 21 | er == {<> \in s \X R : TRUE } 22 | IN {{e} \union r : <> \in er } 23 | 24 | \* Given a history, return the invocations that don't have an associated response 25 | InvocationsWithoutResponses(H) == 26 | LET N == Len(H) 27 | inds == {i \in 1..N : IsInvocation(H[i]) /\ ~\E j \in i+1..N : Matches(H,i,j) } 28 | IN {H[i] : i \in inds } 29 | 30 | \* Return a set with all of the possible sets of events that could 31 | \* be appended to H to extend it by completing operations 32 | Extensions(H) == 33 | LET R == { PossibleResponses(inv) : inv \in InvocationsWithoutResponses(H)} 34 | IN Collect(R) 35 | 36 | 37 | 38 | \* Given a history, return the set of all extended histories 39 | ExtendedHistories(H) == 40 | LET Ps(s) == UNION({Perms(x) : x \in SUBSET(s)}) 41 | ExtHistory(s) == { H \o ext : ext \in Ps(s) } 42 | IN UNION({ExtHistory(s) : s \in Extensions(H)}) 43 | \cup IF Extensions(H) = {{}} THEN {H} ELSE {} 44 | 45 | \* Two histories H and H’ are equivalent if for every process P, H|P = H’|P. 46 | AreEquivalent(H1,H2) == \A p \in Processes : H1|p = H2|p 47 | 48 | \* Set of times (indexes) when an op occurs in a history 49 | OpTimes(H, op) == { i \in 1..Len(H) : H[i] = op } 50 | 51 | \* Set of pairs of times (indexes) when an event (inv, res pair) pair occurs in a history 52 | EventTimes(H, e) == 53 | LET inv == e[1] 54 | res == e[2] 55 | IN {<> \in OpTimes(H,inv) \X OpTimes(H,res) : Matches(H, i, j)} 56 | 57 | \* operation e1 precedes operation e2 if the response of e1 happens before 58 | \* the invocation of e2 59 | \* Operations are pairs (tuples) of events << inv, res >> 60 | Precedes(H, e1, e2) == 61 | \E t1 \in EventTimes(H, e1) : \E t2 \in EventTimes(H,e2) : t1[2] < t2[1] 62 | 63 | \* Operations are pairs (tuples) of events << inv, res >> 64 | Operations(H) == 65 | LET N == Len(H) 66 | ts == {<> \in (1..N) \X (1..N) : Matches(H,i,j)} 67 | IN {<> : t \in ts} 68 | 69 | \* True if <_H ⊆ <_S 70 | \* 71 | \* A history H induces an irreflexive partial order < H on operations: 72 | \* e0 <_H e1 if res(e0) precedes inv(e1) in H 73 | RespectsPrecedenceOrdering(H, S) == 74 | LET LTH(x, y) == Precedes(H, x, y) 75 | LTS(x, y) == Precedes(S, x, y) 76 | ops == Operations(H) \union Operations(S) 77 | Pairs(h, LT(_, _)) == {e \in ops \X ops: LT(e[1], e[2]) } 78 | IN Pairs(H, LTH) \subseteq Pairs(S, LTS) 79 | 80 | 81 | \* TRUE if history contains only invocations and matching responses 82 | OnlyInvAndMatchingResponses(H) == InvocationsWithoutResponses(H) = {} 83 | 84 | \* If H is a history, complete(H) is the maximal subsequence of H consisting only 85 | \* of invocations and matching responses. 86 | Complete(H) == 87 | LET subseqs == Subsequences(H) 88 | IN CHOOSE CH \in subseqs : 89 | /\ OnlyInvAndMatchingResponses(CH) 90 | /\ \A s \in subseqs : OnlyInvAndMatchingResponses(s) => Len(s) <= Len(CH) \* maximal 91 | 92 | \* A history H is sequential if: 93 | \* 1. The first event of H is an invocation. 94 | \* 2. Each invocation, except possibly the last, is immediately followed by a 95 | \* matching response. Each response is immediately followed by a matching 96 | \* invocation. 97 | IsSequential(H) == 98 | LET IsLastInvocation(h,i) == \A j \in 1..Len(h) : IsInvocation(h[j]) => j<=i 99 | IN /\ Len(H)>0 => IsInvocation(H[1]) 100 | /\ \A i \in 1..Len(H) : IsInvocation(H[i]) => (IsLastInvocation(H,i) \/ Matches(H, i, i+1)) 101 | /\ \A i \in 1..Len(H) : IsResponse(H[i]) => Matches(H,i-1,i) 102 | 103 | (*************************************************************************** 104 | 105 | Herlihy & Wing 1990, p469: 106 | 107 | A history H is linearizable if it can be extended (by appending zero or more 108 | response events) to some history H’ such that: 109 | 110 | L1: complete(H’) is equivalent to some legal sequential history S, and 111 | L2: <_H ⊆ <_S 112 | 113 | Two histories H and H’ are equivalent if for every process P, H|P = H’|P. 114 | 115 | If H is a history, complete(H) is the maximal subsequence of H consisting only 116 | of invocations and matching responses. 117 | 118 | ***************************************************************************) 119 | 120 | IsLinearizableHistory(H) == 121 | \E Hp \in ExtendedHistories(H) : 122 | LET completeHp == Complete(Hp) 123 | IN \E f \in Orderings(Len(completeHp)) : 124 | LET S == completeHp ** f 125 | IN /\ IsSequential(S) 126 | /\ IsLegal(S) 127 | /\ AreEquivalent(S, completeHp) 128 | /\ RespectsPrecedenceOrdering(H, S) 129 | 130 | Linearize(H) == 131 | LET Hp == CHOOSE Hp \in ExtendedHistories(H) : 132 | LET completeHp == Complete(Hp) 133 | IN \E f \in Orderings(Len(completeHp)) : 134 | LET S == completeHp ** f 135 | IN /\ IsSequential(S) 136 | /\ IsLegal(S) 137 | /\ AreEquivalent(S, completeHp) 138 | /\ RespectsPrecedenceOrdering(H, S) 139 | completeHp == Complete(Hp) 140 | f == CHOOSE f \in Orderings(Len(completeHp)) : 141 | LET S == completeHp ** f 142 | IN /\ IsSequential(S) 143 | /\ IsLegal(S) 144 | /\ AreEquivalent(S, completeHp) 145 | /\ RespectsPrecedenceOrdering(H, S) 146 | IN Hp**f 147 | ============================================================================= 148 | \* Modification History 149 | \* Last modified Tue Oct 23 18:49:20 PDT 2018 by lhochstein 150 | \* Created Sat Oct 20 09:56:44 PDT 2018 by lhochstein 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reading the Herlihy & Wing Linearizability paper with TLA+ 2 | 3 | This repository contains a TLA+ model for checking if an object history is 4 | linearizable. 5 | 6 | See also: [Reading the Herlihy & Wing Linearizability paper with TLA+, part 2: prophecy variables](https://github.com/lorin/tla-prophecy) 7 | 8 | The [Herlihy & Wing 1990](http://dx.doi.org/10.1145/78969.78972) paper entitled 9 | *Linearizability: a correctness condition for concurrent objects* 10 | introduced *linearizability* as a correctness condition for reasoning about the 11 | behavior of concurrent data structures. 12 | 13 | Peter Bailis's blog entry [Linearizability versus Serializability][bailis-lin] 14 | has a good definition: 15 | 16 | > Linearizability is a guarantee about single operations on single objects. It 17 | > provides a real-time (i.e., wall-clock) guarantee on the behavior of a set of 18 | > single operations (often reads and writes) on a single object (e.g., 19 | > distributed register or data item). 20 | 21 | > In plain English, under linearizability, writes should appear to be 22 | > instantaneous. Imprecisely, once a write completes, all later reads (where 23 | > “later” is defined by wall-clock start time) should return the value of that 24 | > write or the value of a later write. Once a read returns a particular value, 25 | > all later reads should return that value or the value of a later write. 26 | 27 | [bailis-lin]: http://www.bailis.org/blog/linearizability-versus-serializability/ 28 | 29 | There are several linearizable data stores whose behaviors have been specified 30 | with [TLA+]: 31 | 32 | * Lamport's book [Specifying Systems][specifying-systems] uses an example of a linearizable memory in 33 | Section 5.3. 34 | * The [Raft][raft] Consensus algorithm supports linearizable semantics and has a 35 | [TLA+ specification][raft-tla] written by [Diego Ongaro]. 36 | * [Azure Cosmos DB][cosmosdb] supports a consistency model with linearizable reads and has 37 | [high-level TLA+ specifications][cosmosdb-tla] written by [Murat Demirbas]. 38 | 39 | [Diego Ongaro]: https://ongardie.net/diego/ 40 | [Murat Demirbas]: http://muratbuffalo.blogspot.com/ 41 | 42 | [TLA+]: https://lamport.azurewebsites.net/tla/tla.html 43 | [specifying-systems]: https://lamport.azurewebsites.net/tla/book.html 44 | [raft]: https://raft.github.io/ 45 | [raft-tla]: https://github.com/ongardie/raft.tla 46 | [cosmosdb]: http://cosmosdb.com/ 47 | [cosmosdb-tla]: https://github.com/Azure/azure-cosmos-tla 48 | 49 | However, none of these models use the definition of linearizability outlined in 50 | the original paper by Herlihy & Wing. 51 | 52 | Indeed, the definition in the original paper is awkward to use with TLC (the 53 | TLA+ model checker), because it involves reordering of event histories, which 54 | leads to state space explosion. 55 | 56 | However, I found it useful to work directly with the definition of 57 | linearizability as an exercise for practicing with TLA+, as well as to gain a 58 | better understanding of how linearizability is defined. 59 | 60 | ## Files in this repository 61 | 62 | * [Linearizability.tla](Linearizability.tla) contains a definition of 63 | linearizability. In particular, the `IsLinearizableHistory` operator 64 | returns true if an event history is linearizable. 65 | * [LinQueue.tla](LinQueue.tla) instantiates the Linearizability module for 66 | a queue (FIFO) object. It contains an `IsLinearizableHistory` operator that returns true 67 | if an event history for a queue is linearizable. 68 | * [LinQueuePlusCal.tla](LinQueuePlusCal.tla) is a PlusCal version. If a 69 | history is linearizable, using TLC with this module makes it easy to see 70 | a valid linearization. 71 | * [Utilities.tla](Utilities.tla) conatins some general-purpose operators. 72 | 73 | ## Histories 74 | 75 | On p469, the paper defines a *linearizable object* as an object whose concurrent 76 | histories are linearizable with respect to some sequential specification. 77 | 78 | To understand linearizability, we need to understand what a concurrent history 79 | is. 80 | 81 | As a motivating example, figure one from the paper shows several possible 82 | histories for a concurrently accessed queue. Figures 1(a) and 1(c) are 83 | linearizable, and Figures 1(b) and 1(d) are not. 84 | 85 | ![Figure 1](fig1.png) 86 | 87 | Each interval represents an operation. There are two types of operations: {E, 88 | D} for enqueue and dequeue. There are three processes: {A, B, C}. There are three 89 | items that can be added to the queue: {x, y, z}. 90 | 91 | Here's how I modeled these four histories in TLA+: 92 | 93 | ``` 94 | H1 == << 95 | [op|->"E", val|->"x", proc|->"A"], 96 | [op|->"E", val|->"y", proc|->"B"], 97 | [op|->"Ok", proc|->"B"], 98 | [op|->"Ok", proc|->"A"], 99 | [op|->"D", proc|->"B"], 100 | [op|->"Ok", val|->"x", proc|->"B"], 101 | [op|->"D", proc|->"A"], 102 | [op|->"Ok", val|->"y", proc|->"A"], 103 | [op|->"E", val|->"z", proc|->"A"]>> 104 | 105 | H2 == << 106 | [op|->"E", val|->"x", proc|->"A"], 107 | [op|->"Ok", proc|->"A"], 108 | [op|->"E", val|->"y", proc|->"B"], 109 | [op|->"D", proc|->"A"], 110 | [op|->"Ok", proc|->"B"], 111 | [op|->"Ok", val|->"y", proc|->"A"] 112 | >> 113 | 114 | H3 == << 115 | [op|->"E", val|->"x", proc|->"A"], 116 | [op|->"D", proc|-> "B"], 117 | [op|->"Ok", val|->"x", proc|->"B"]>> 118 | 119 | 120 | H4 == << 121 | [op|->"E", val|->"x", proc|->"A"], 122 | [op|->"E", val|->"y", proc|->"B"], 123 | [op|->"Ok", proc|->"A"], 124 | [op|->"Ok", proc|->"B"], 125 | [op|->"D", proc|-> "A"], 126 | [op|->"D", proc|-> "C"], 127 | [op|->"Ok", val|->"y", proc|->"A"], 128 | [op|->"Ok", val|->"y", proc|->"C"] 129 | >> 130 | ``` 131 | 132 | We can use the `IsLinearizableHistory` operator from LinQueue.tla to verify that 133 | H2 is not linearizable. 134 | 135 | ![Evaluating IsLinearizable(H2)](h2.png) 136 | 137 | For H3, we can use the `FindLinearization` algorithm from LinQueuePlusCal.tla to 138 | find a linearization. Specify `linearizable = FALSE` as the invariant and 139 | run the model checker. The variable `S` contains the linearization: 140 | 141 | ![H3 linearization](h3.png) 142 | 143 | Here's the value for `S`: 144 | 145 | ``` 146 | << 147 | [op|->"E", val|->"x", proc|->"A"], 148 | [op|->"Ok", proc|->"A"], 149 | [op|->"D", proc|-> "B"], 150 | [op|->"Ok", val|->"x", proc|->"B"] 151 | >> 152 | ``` 153 | 154 | ## Some excerpts of the model 155 | 156 | I endeavored to make the TLA+ representation as close as possible to how the 157 | definitions were written in the paper, rather than trying to optimize for 158 | reducing the state space of the TLC model checker. 159 | 160 | ### Linearizable history 161 | 162 | p469: 163 | 164 | A history H is linearizable if it can be extended (by appending zero or more 165 | response events) to some history H’ such that: 166 | 167 | * L1: complete(H’) is equivalent to some legal sequential history S, and 168 | * L2: <H ⊆ <S 169 | 170 | 171 | Here's how I modeled this in TLA+: 172 | 173 | ``` 174 | IsLinearizableHistory(H) == 175 | \E Hp \in ExtendedHistories(H) : 176 | LET completeHp == Complete(Hp) 177 | IN \E f \in Orderings(Len(completeHp)) : 178 | LET S == completeHp ** f 179 | IN /\ IsSequential(S) \* L1 180 | /\ IsLegal(S) \* L1 181 | /\ AreEquivalent(S, completeHp) \* L1 182 | /\ RespectsPrecedenceOrdering(H, S) \* L2 183 | ``` 184 | 185 | ### complete(H) 186 | 187 | From p467: 188 | 189 | If H is a history, complete(H) is the maximal subsequence of H consisting only of invocations and matching responses. 190 | 191 | A subsequence is a sequence that can be derived from another sequence by 192 | deleting some or no elements without changing the order of the remaining 193 | elements (source: [Wikipedia](https://en.wikipedia.org/wiki/Subsequence)) 194 | 195 | ``` 196 | Complete(H) == 197 | LET subseqs == Subsequences(H) 198 | IN CHOOSE CH \in subseqs : 199 | /\ OnlyInvAndMatchingResponses(CH) 200 | /\ \A s \in subseqs : OnlyInvAndMatchingResponses(s) => Len(s) <= Len(CH) \* maximal 201 | ``` 202 | 203 | ### Sequential 204 | 205 | p467 206 | 207 | A history H is sequential if: 208 | 1. The first event of H is an invocation. 209 | 2. Each invocation, except possibly the last, is immediately followed by a 210 | matching response. Each response is immediately followed by a matching 211 | invocation. 212 | 213 | ``` 214 | IsSequential(H) == 215 | LET IsLastInvocation(h,i) == \A j \in 1..Len(h) : IsInvocation(h[j]) => j<=i 216 | IN /\ Len(H)>0 => IsInvocation(H[1]) 217 | /\ \A i \in 1..Len(H) : IsInvocation(H[i]) => (IsLastInvocation(H,i) \/ Matches(H, i, i+1)) 218 | /\ \A i \in 1..Len(H) : IsResponse(H[i]) => Matches(H,i-1,i) 219 | ``` 220 | 221 | ### Legal sequential history 222 | 223 | The specificiation for a legal sequential history varies based on the kind of 224 | object whose behavior you are trying to model. The paper uses a queue (FIFO) as the example object being 225 | modeled. 226 | 227 | Most of the work is done by the recursive `LegalQueue` function. 228 | 229 | ``` 230 | RECURSIVE LegalQueue(_, _) 231 | 232 | \* Check if a history h is legal given an initial queue state q 233 | LegalQueue(h, q) == \/ h = << >> 234 | \/ LET first == Head(h) 235 | rest == Tail(h) 236 | IN \/ /\ first.op = "E" 237 | /\ LegalQueue(rest, Append(q, first.val)) 238 | \/ /\ first.op = "D" 239 | /\ Len(q)>0 240 | /\ first.val = Head(q) 241 | /\ LegalQueue(rest, Tail(q)) 242 | 243 | IsLegalQueueHistory(h) == LegalQueue(h, << >>) 244 | 245 | \* Given a sequential history H, true if it represents a legal queue history 246 | IsLegal(H) == 247 | LET RECURSIVE Helper(_, _) 248 | Helper(h, s) == IF h = << >> THEN IsLegalQueueHistory(s) 249 | ELSE LET hr == Tail(Tail(h)) 250 | inv == h[1] 251 | res == h[2] 252 | e == [op|->inv.op, val|-> IF inv.op = "E" THEN inv.val ELSE res.val] 253 | IN Helper(hr, Append(s, e)) 254 | IN Helper(H, <<>>) 255 | ``` 256 | -------------------------------------------------------------------------------- /TestLegalQueue.tla: -------------------------------------------------------------------------------- 1 | --------------------------- MODULE TestLegalQueue --------------------------- 2 | 3 | EXTENDS LinQueue 4 | 5 | 6 | 7 | 8 | ============================================================================= 9 | \* Modification History 10 | \* Last modified Sun Oct 21 14:51:48 PDT 2018 by lhochstein 11 | \* Created Sun Oct 21 14:51:34 PDT 2018 by lhochstein 12 | -------------------------------------------------------------------------------- /TestLinQueue.tla: -------------------------------------------------------------------------------- 1 | ---------------------------- MODULE TestLinQueue ---------------------------- 2 | EXTENDS LinQueue 3 | 4 | Hmk == << 5 | \* A enqueues x. 6 | [op|->"E", val|->"x", proc|->"A"], 7 | [op|->"Ok", proc|->"A"] 8 | >> 9 | ASSUME L!Complete(Hmk) = Hmk 10 | ASSUME L!InvocationsWithoutResponses(Hmk) = {} 11 | ASSUME L!Extensions(Hmk) = {{}} 12 | ASSUME L!ExtendedHistories(Hmk) = {Hmk} 13 | ASSUME IsLinearizableHistory(Hmk) 14 | 15 | --------- 16 | 17 | Enq == [op|->"E", val|->"y", proc|->"B"] 18 | Ok == [op|->"Ok", proc|->"B"] 19 | Hmk2 == Hmk \o <> 20 | ASSUME L!InvocationsWithoutResponses(Hmk2) = {Enq} 21 | ASSUME L!Extensions(Hmk2) = {{Ok}} 22 | ASSUME L!ExtendedHistories(Hmk2) = {Hmk2 \o <>} 23 | ASSUME IsLinearizableHistory(Hmk2) 24 | ASSUME L!Complete(Hmk2) = Hmk 25 | ASSUME L!Complete(Hmk2 \o <>) = Hmk2 \o <> 26 | 27 | Hmk3 == << 28 | \* A and B concurrently enqueue x and y. 29 | [op|->"E", val|->"x", proc|->"A"], 30 | [op|->"E", val|->"y", proc|->"B"], 31 | [op|->"Ok", proc|->"B"], 32 | [op|->"Ok", proc|->"A"], 33 | \* B dequeues x. 34 | [op|->"D", proc|->"B"], 35 | [op|->"Ok", val|->"x", proc|->"B"], 36 | \* \* A dequeues y. 37 | [op|->"D", proc|->"A"], 38 | [op|->"Ok", val|->"y", proc|->"A"] 39 | >> 40 | ASSUME L!InvocationsWithoutResponses(Hmk3) = {} 41 | ASSUME L!Extensions(Hmk3) = {{}} 42 | ASSUME L!ExtendedHistories(Hmk3) = {Hmk3} 43 | ASSUME L!Complete(Hmk3) = Hmk3 44 | ASSUME IsLinearizableHistory(Hmk3) 45 | 46 | --------- 47 | 48 | H1 == << 49 | \* A and B concurrently enqueue x and y. 50 | [op|->"E", val|->"x", proc|->"A"], 51 | [op|->"E", val|->"y", proc|->"B"], 52 | [op|->"Ok", proc|->"B"], 53 | [op|->"Ok", proc|->"A"], 54 | \* B dequeues x. 55 | [op|->"D", proc|->"B"], 56 | [op|->"Ok", val|->"x", proc|->"B"], 57 | \* \* A dequeues y. 58 | [op|->"D", proc|->"A"], 59 | [op|->"Ok", val|->"y", proc|->"A"], 60 | \* InL!Complete enqueue of z by A. 61 | [op|->"E", val|->"z", proc|->"A"]>> 62 | ASSUME L!InvocationsWithoutResponses(H1) = {[op|->"E", val|->"z", proc|->"A"]} 63 | ASSUME L!Extensions(H1) = {{[op|->"Ok", proc|->"A"]}} 64 | ASSUME L!ExtendedHistories(H1) = {H1 \o <<[op|->"Ok", proc|->"A"]>>} 65 | \* Strip inL!Complete enqueue of z by A. 66 | ASSUME L!Complete(H1) = SubSeq(H1, 1, Len(H1) - 1) 67 | ASSUME L!Complete(H1 \o <<[op|->"Ok", proc|->"A"]>>) = H1 \o <<[op|->"Ok", proc|->"A"]>> 68 | ASSUME IsLinearizableHistory(H1) 69 | 70 | H2 == << 71 | [op|->"E", val|->"x", proc|->"A"], 72 | [op|->"Ok", proc|->"A"], 73 | [op|->"E", val|->"y", proc|->"B"], 74 | [op|->"D", proc|->"A"], 75 | [op|->"Ok", proc|->"B"], 76 | [op|->"Ok", val|->"y", proc|->"A"] 77 | >> 78 | ASSUME ~IsLinearizableHistory(H2) 79 | 80 | H3 == << 81 | [op|->"E", val|->"x", proc|->"A"], 82 | [op|->"D", proc|-> "B"], 83 | [op|->"Ok", val|->"x", proc|->"B"]>> 84 | ASSUME IsLinearizableHistory(H3) 85 | 86 | H4 == << 87 | [op|->"E", val|->"x", proc|->"A"], 88 | [op|->"E", val|->"y", proc|->"B"], 89 | [op|->"Ok", proc|->"A"], 90 | [op|->"Ok", proc|->"B"], 91 | [op|->"D", proc|-> "A"], 92 | [op|->"D", proc|-> "C"], 93 | [op|->"Ok", val|->"y", proc|->"A"], 94 | [op|->"Ok", val|->"y", proc|->"C"] 95 | >> 96 | ASSUME ~IsLinearizableHistory(H4) 97 | 98 | ============================================================================= 99 | \* Modification History 100 | \* Last modified Sun Oct 21 15:21:29 PDT 2018 by lhochstein 101 | \* Created Sun Oct 21 10:56:40 PDT 2018 by lhochstein -------------------------------------------------------------------------------- /TestLinearizability.tla: -------------------------------------------------------------------------------- 1 | ------------------------ MODULE TestLinearizability ------------------------ 2 | EXTENDS Naturals, Sequences, LinQueue 3 | 4 | \* L == INSTANCE Linearizability 5 | 6 | H3 == << 7 | [op|->"E", val|->"x", proc|->"A"], 8 | [op|->"D", proc|-> "B"], 9 | [op|->"Ok", val|->"x", proc|->"B"] 10 | >> 11 | 12 | TestCollect == L!Collect({ 13 | {[op|->"Ok", proc|->"A"]}, 14 | {[op|->"Ok", proc|->"B", val|->"x"], [op|->"Ok", proc|->"B", val|->"y"]}}) = 15 | { 16 | {[op|->"Ok", proc|->"A"], [op|->"Ok", proc|->"B", val|->"x"]}, 17 | {[op|->"Ok", proc|->"A"], [op|->"Ok", proc|->"B", val|->"y"]} 18 | } 19 | ASSUME TestCollect 20 | 21 | TestInvocationsWithoutResponse == L!InvocationsWithoutResponses(H3) = {[op|->"E", val|->"x", proc|->"A"]} 22 | ASSUME TestInvocationsWithoutResponse 23 | 24 | TestExtensions == 25 | LET H == <<[op|->"E", val|->"x", proc|->"A"], 26 | [op|->"D", proc|-> "B"]>> 27 | IN L!Extensions(H) = { 28 | {[op|->"Ok", proc|->"A"], [op|->"Ok", proc|->"B", val|->"x"]}, 29 | {[op|->"Ok", proc|->"A"], [op|->"Ok", proc|->"B", val|->"y"]}, 30 | {[op|->"Ok", proc|->"A"], [op|->"Ok", proc|->"B", val|->"z"]} 31 | } 32 | ASSUME TestExtensions 33 | 34 | TestExtendedHistories == 35 | LET H == <<[op|->"E", val|->"x", proc|->"A"], [op|->"D", proc|-> "B"]>> 36 | IN L!ExtendedHistories(H) = { 37 | << [op |-> "E", val |-> "x", proc |-> "A"], 38 | [op |-> "D", proc |-> "B"], 39 | [op |-> "Ok", proc |-> "A"] >>, 40 | << [op |-> "E", val |-> "x", proc |-> "A"], 41 | [op |-> "D", proc |-> "B"], 42 | [op |-> "Ok", val |-> "x", proc |-> "B"] >>, 43 | << [op |-> "E", val |-> "x", proc |-> "A"], 44 | [op |-> "D", proc |-> "B"], 45 | [op |-> "Ok", val |-> "y", proc |-> "B"] >>, 46 | << [op |-> "E", val |-> "x", proc |-> "A"], 47 | [op |-> "D", proc |-> "B"], 48 | [op |-> "Ok", val |-> "z", proc |-> "B"] >>, 49 | << [op |-> "E", val |-> "x", proc |-> "A"], 50 | [op |-> "D", proc |-> "B"], 51 | [op |-> "Ok", proc |-> "A"], 52 | [op |-> "Ok", val |-> "x", proc |-> "B"] >>, 53 | << [op |-> "E", val |-> "x", proc |-> "A"], 54 | [op |-> "D", proc |-> "B"], 55 | [op |-> "Ok", proc |-> "A"], 56 | [op |-> "Ok", val |-> "y", proc |-> "B"] >>, 57 | << [op |-> "E", val |-> "x", proc |-> "A"], 58 | [op |-> "D", proc |-> "B"], 59 | [op |-> "Ok", proc |-> "A"], 60 | [op |-> "Ok", val |-> "z", proc |-> "B"] >>, 61 | << [op |-> "E", val |-> "x", proc |-> "A"], 62 | [op |-> "D", proc |-> "B"], 63 | [op |-> "Ok", val |-> "x", proc |-> "B"], 64 | [op |-> "Ok", proc |-> "A"] >>, 65 | << [op |-> "E", val |-> "x", proc |-> "A"], 66 | [op |-> "D", proc |-> "B"], 67 | [op |-> "Ok", val |-> "y", proc |-> "B"], 68 | [op |-> "Ok", proc |-> "A"] >>, 69 | << [op |-> "E", val |-> "x", proc |-> "A"], 70 | [op |-> "D", proc |-> "B"], 71 | [op |-> "Ok", val |-> "z", proc |-> "B"], 72 | [op |-> "Ok", proc |-> "A"] >> } 73 | ASSUME TestExtendedHistories 74 | 75 | TestSubseq == L!Subseq(<<"a", "b", "c", "d", "e">>, {2,3,5}) = <<"b", "c", "e">> 76 | ASSUME TestSubseq 77 | 78 | TestSubsequences == L!Subsequences(<<"a", "b", "c">>) = { 79 | << >>, 80 | <<"a">>, 81 | <<"b">>, 82 | <<"c">>, 83 | <<"a","b">>, 84 | <<"a","c">>, 85 | <<"b","c">>, 86 | <<"a","b","c">> 87 | } 88 | ASSUME TestSubsequences 89 | 90 | TestComplete == L!Complete( 91 | << [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "D", proc |-> "B"], [op |-> "Ok", proc |-> "A"] >>) 92 | ASSUME TestComplete = <<[op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"]>> 93 | 94 | TestAreEquivalentTrue == L!AreEquivalent( 95 | << [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "D", proc |-> "B"], [op |-> "Ok", proc |-> "A"], [op |-> "Ok", val |-> "x", proc |-> "B"] >>, 96 | << [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"], [op |-> "D", proc |-> "B"], [op |-> "Ok", val |-> "x", proc |-> "B"] >> 97 | ) 98 | ASSUME TestAreEquivalentTrue 99 | 100 | TestAreEquivalentFalse == L!AreEquivalent( 101 | << [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "D", proc |-> "B"], [op |-> "Ok", proc |-> "A"], [op |-> "Ok", val |-> "x", proc |-> "B"] >>, 102 | << [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"], [op |-> "D", proc |-> "B"], [op |-> "Ok", val |-> "y", proc |-> "B"] >> 103 | ) 104 | ASSUME ~TestAreEquivalentFalse 105 | 106 | TestPrecedes == 107 | LET H == << 108 | [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"], 109 | [op |-> "D", proc |-> "B"], [op |-> "Ok", val |-> "x", proc |-> "B"], 110 | [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"]>> 111 | e1 == <> 112 | e2 == <> 113 | e3 == <> 114 | IN /\ L!Precedes(H, e1, e2) 115 | /\ L!Precedes(H, e2, e3) 116 | ASSUME TestPrecedes 117 | 118 | TestOperations == 119 | LET H == << 120 | [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"], 121 | [op |-> "D", proc |-> "B"], [op |-> "Ok", val |-> "x", proc |-> "B"], 122 | [op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"]>> 123 | IN L!Operations(H) = { 124 | <<[op |-> "E", val |-> "x", proc |-> "A"], [op |-> "Ok", proc |-> "A"]>>, 125 | <<[op |-> "D", proc |-> "B"], [op |-> "Ok", val |-> "x", proc |-> "B"]>>} 126 | ASSUME TestOperations 127 | 128 | Test == TestOperations 129 | ASSUME Test 130 | 131 | \* The only possible extension for H3 is completing the enqueue 132 | ExtH3 == L!Extensions(H3) = {{[op|->"Ok", proc|->"A"]}} 133 | ASSUME ExtH3 134 | 135 | ============================================================================= 136 | \* Modification History 137 | \* Last modified Sun Oct 21 10:18:05 PDT 2018 by lhochstein 138 | \* Created Sat Oct 20 13:43:05 PDT 2018 by lhochstein 139 | -------------------------------------------------------------------------------- /TestRespectsPrecedenceOrdering.tla: -------------------------------------------------------------------------------- 1 | ------------------- MODULE TestRespectsPrecedenceOrdering ------------------- 2 | EXTENDS LinQueue 3 | 4 | TestNoChange == 5 | \* Two overlapping 6 | LET H == << 7 | [op|->"E", val|->"x", proc|->"A"], 8 | [op|->"D", proc|-> "B"], 9 | [op|->"Ok", val|->"x", proc|->"B"], 10 | [op|->"Ok", proc|->"A"]>> 11 | IN L!RespectsPrecedenceOrdering(H, H) 12 | 13 | \* This should be false 14 | TestReverseOrderingViolatesPrecedence == 15 | LET H1 == << 16 | [op|->"E", val|->"x", proc|->"A"], 17 | [op|->"Ok", proc|->"A"], 18 | [op|->"D", proc|-> "B"], 19 | [op|->"Ok", val|->"x", proc|->"B"]>> 20 | H2 == << 21 | [op|->"D", proc|-> "B"], 22 | [op|->"Ok", val|->"x", proc|->"B"], 23 | [op|->"E", val|->"x", proc|->"A"], 24 | [op|->"Ok", proc|->"A"] 25 | >> 26 | IN \/ L!RespectsPrecedenceOrdering(H1, H2) 27 | \/ L!RespectsPrecedenceOrdering(H2, H1) 28 | 29 | TestMovingOverlapIsFine == 30 | \* This is Figure 1, H2(b) 31 | LET H1 == << 32 | [op|->"E", val|->"x", proc|->"A"], 33 | [op|->"Ok", proc|->"A"], 34 | [op|->"E", val|->"y", proc|->"B"], 35 | [op|->"D", proc|-> "A"], 36 | [op|->"Ok", proc|->"B"], 37 | [op|->"Ok", val|->"y", proc|->"A"]>> 38 | H2 == << 39 | [op|->"E", val|->"x", proc|->"A"], 40 | [op|->"Ok", proc|->"A"], 41 | [op|->"E", val|->"y", proc|->"B"], 42 | [op|->"Ok", proc|->"B"], 43 | [op|->"D", proc|-> "A"], 44 | [op|->"Ok", val|->"y", proc|->"A"]>> 45 | H3 == << 46 | [op|->"E", val|->"x", proc|->"A"], 47 | [op|->"Ok", proc|->"A"], 48 | [op|->"D", proc|-> "A"], 49 | [op|->"Ok", val|->"y", proc|->"A"], 50 | [op|->"E", val|->"y", proc|->"B"], 51 | [op|->"Ok", proc|->"B"]>> 52 | IN /\ L!RespectsPrecedenceOrdering(H1, H2) 53 | /\ L!RespectsPrecedenceOrdering(H1, H3) 54 | /\ ~L!RespectsPrecedenceOrdering(H2, H3) 55 | 56 | ============================================================================= 57 | \* Modification History 58 | \* Last modified Sun Oct 21 10:55:45 PDT 2018 by lhochstein 59 | \* Created Sun Oct 21 10:20:03 PDT 2018 by lhochstein 60 | -------------------------------------------------------------------------------- /Utilities.tla: -------------------------------------------------------------------------------- 1 | ----------------------------- MODULE Utilities ----------------------------- 2 | EXTENDS Naturals, Sequences, FiniteSets, TLC 3 | 4 | \* Composition 5 | f ** g == [x \in DOMAIN(g) |-> f[g[x]]] 6 | 7 | \* Pick a subsequence of S that matches the set of indices, inds 8 | Subseq(S, inds) == 9 | LET F[i \in 0..Len(S)] == 10 | IF i = 0 THEN << >> 11 | ELSE IF i \in inds THEN Append(F[i-1], S[i]) 12 | ELSE F[i-1] 13 | IN F[Len(S)] 14 | 15 | \* All subssequences of S 16 | \* 17 | \* A subsequence is a sequence that can be derived from another sequence by deleting 18 | \* some or no elements without changing the order of the remaining elements (Wikipedia). 19 | Subsequences(S) == {Subseq(S,inds) : inds \in SUBSET(1..Len(S))} 20 | 21 | \* Given a set, return a sequence made of its elements 22 | RECURSIVE ToSeq(_) 23 | ToSeq(S) == IF S = {} THEN << >> 24 | ELSE LET e == CHOOSE e \in S : TRUE 25 | T == S \ {e} 26 | IN Append(ToSeq(T), e) 27 | 28 | \* Returns a set of functions on 1..N->1..N that represent permutations 29 | \* for reordering a sequence of events 30 | \* 31 | \* This is a simple implementation that filters down from a larger set. 32 | OrderingsSimple(N) == LET S == 1..N 33 | Range(f) == { f[x] : x \in DOMAIN f } 34 | Onto(f) == DOMAIN f = Range(f) 35 | IN {f \in [S->S] : Onto(f)} 36 | 37 | \* Returns a set of functions on 1..N->1..N that represent permutations 38 | \* for reordering a sequence of events 39 | \* 40 | \* This constructs the set rather than filtering down. 41 | \* 42 | RECURSIVE Orderings(_) 43 | Orderings(N) == 44 | CASE N=0 -> {} 45 | [] N=1 -> {[x \in {1}|->1]} 46 | [] OTHER -> LET fs == Orderings(N-1) 47 | Helper(n, fp) == LET f == Append(fp,n) IN {[f EXCEPT ![x]=f[n], ![n]=f[x]] : x \in 1..n } 48 | IN UNION({Helper(N,f): f \in fs}) 49 | 50 | \* Given a set, return a set of sequences that are permutations 51 | Perms(S) == LET fs == Orderings(Cardinality(S)) 52 | s == ToSeq(S) 53 | IN {s**f: f \in fs} 54 | 55 | 56 | ============================================================================= 57 | \* Modification History 58 | \* Last modified Tue Oct 23 18:40:06 PDT 2018 by lhochstein 59 | \* Created Mon Oct 22 19:21:10 PDT 2018 by lhochstein 60 | -------------------------------------------------------------------------------- /fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorin/tla-linearizability/4a5e1b2dd296f6b70f16aded1a1706ef62b74a9d/fig1.png -------------------------------------------------------------------------------- /h2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorin/tla-linearizability/4a5e1b2dd296f6b70f16aded1a1706ef62b74a9d/h2.png -------------------------------------------------------------------------------- /h3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorin/tla-linearizability/4a5e1b2dd296f6b70f16aded1a1706ef62b74a9d/h3.png --------------------------------------------------------------------------------