├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── distsys ├── archetypeinterface.go ├── archetyperesource.go ├── fairness.go ├── go.mod ├── go.sum ├── hashmap │ └── hashmap.go ├── mpcalctx.go ├── resources │ ├── aworset.go │ ├── aworset_test.go │ ├── channels.go │ ├── conn.go │ ├── crdt.go │ ├── dummy.go │ ├── fd.go │ ├── filesystem.go │ ├── gcounter.go │ ├── hashmap.go │ ├── incmap.go │ ├── localshared.go │ ├── lww.go │ ├── mailboxes.go │ ├── nestedarch.go │ ├── persistent.go │ ├── placeholder.go │ ├── relaxedmailboxes.go │ ├── tcpmailboxes.go │ ├── twopc.go │ └── twopc_test.go ├── tla │ ├── builtins.go │ ├── symbols.go │ ├── value.go │ └── value_test.go └── trace │ ├── event.go │ ├── recorder.go │ ├── state.go │ └── vclock.go ├── doc └── papers │ ├── SPLASHAbstract.pdf │ └── asplosb23main-p12-p-e73de3693c-62943-final.pdf ├── go_versions.sc ├── piggo.svg ├── project.scala ├── src └── pgo │ ├── PGo.scala │ ├── checker │ ├── CheckingErrors.scala │ ├── CriticalSectionInterpreter.scala │ ├── StateExplorer.scala │ ├── TraceChecker.scala │ ├── TraceConsumer.scala │ ├── TraceElement.scala │ └── VClock.scala │ ├── model │ ├── Definitions.scala │ ├── PGoError.scala │ ├── RefersTo.scala │ ├── Rewritable.scala │ ├── SourceLocatable.scala │ ├── SourceLocation.scala │ ├── Visitable.scala │ ├── mpcal │ │ └── AST.scala │ ├── pcal │ │ └── AST.scala │ └── tla │ │ ├── AST.scala │ │ └── BuiltinModules.scala │ ├── parser │ ├── LineColumnAwareCharReader.scala │ ├── MPCalParser.scala │ ├── MPCalParserContext.scala │ ├── PCalParser.scala │ ├── PCalParserContext.scala │ ├── ParsingErrors.scala │ ├── ParsingUtils.scala │ ├── TLAMeta.scala │ ├── TLAParser.scala │ └── TLAParserContext.scala │ ├── trans │ ├── MPCalGoCodegenPass.scala │ ├── MPCalNormalizePass.scala │ ├── MPCalPCalCodegenPass.scala │ ├── MPCalSemanticCheckPass.scala │ └── PCalRenderPass.scala │ └── util │ ├── ById.scala │ ├── Description.scala │ ├── MPCalPassUtils.scala │ ├── NameCleaner.scala │ ├── TLAExprInterpreter.scala │ ├── Unreachable.scala │ └── package.scala ├── syntax ├── pygments │ ├── README.md │ ├── latex │ │ ├── main.pdf │ │ └── main.tex │ ├── mpcallexer │ │ ├── __init__.py │ │ └── lexer.py │ ├── requirements.txt │ └── setup.py └── vscode │ └── mpcal │ ├── .vscode │ └── launch.json │ ├── CHANGELOG.md │ ├── README.md │ ├── cfg-language-configuration.json │ ├── mpcal-0.0.1.vsix │ ├── mpcal-language-configuration.json │ ├── package.json │ ├── pluscal-language-configuration.json │ ├── syntaxes │ ├── cfg.tmLanguage.json │ ├── mpcal.tmLanguage.json │ ├── pluscal.tmLanguage.json │ └── tlaplus.tmLanguage.json │ └── tlaplus-language-configuration.json ├── systems ├── README.md ├── dqueue │ ├── dqueue.go │ ├── dqueue.tla │ ├── dqueue_test.go │ ├── go.mod │ └── go.sum ├── gcounter │ ├── gcounter.go │ ├── gcounter.tla │ ├── gcounter_test.go │ ├── go.mod │ ├── go.sum │ └── util.go ├── loadbalancer │ ├── go.mod │ ├── go.sum │ ├── load_balancer.go │ ├── load_balancer.tla │ └── loadbalancer_test.go ├── locksvc │ ├── Makefile │ ├── go.mod │ ├── go.sum │ ├── locksvc.cfg │ ├── locksvc.go │ ├── locksvc.tla │ └── locksvc_test.go ├── nestedcrdtimpl │ ├── NestedCRDTImpl.go │ ├── NestedCRDTImpl.tla │ ├── go.mod │ ├── go.sum │ ├── nestedcrdtimpl_test.go │ └── timer.go ├── pbkvs │ ├── Makefile │ ├── README.md │ ├── bootstrap │ │ ├── client.go │ │ ├── helper.go │ │ └── server.go │ ├── cmd │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── configs │ │ ├── config.go │ │ ├── local-1-1.yaml │ │ ├── local-3-3.yaml │ │ └── local-bench.yaml │ ├── go.mod │ ├── go.sum │ ├── leaderelection.go │ ├── pbkvs.cfg │ ├── pbkvs.go │ ├── pbkvs.tla │ └── pbkvs_test.go ├── proxy │ ├── go.mod │ ├── go.sum │ ├── proxy.go │ ├── proxy.tla │ └── proxy_test.go ├── raftkvs │ ├── Makefile │ ├── README.md │ ├── bootstrap │ │ ├── client.go │ │ ├── helper.go │ │ └── server.go │ ├── cmd │ │ ├── client │ │ │ └── main.go │ │ └── server │ │ │ └── main.go │ ├── configs │ │ ├── azure-3.yaml │ │ ├── azure-5.yaml │ │ ├── config.go │ │ ├── local-1.yaml │ │ ├── local-3.yaml │ │ ├── test-1-1.yaml │ │ ├── test-2-1.yaml │ │ ├── test-3-1.yaml │ │ ├── test-3-3.yaml │ │ ├── test-3-5.yaml │ │ └── test-5-1.yaml │ ├── customch.go │ ├── go.mod │ ├── go.sum │ ├── models │ │ ├── MC.cfg │ │ └── SIM.cfg │ ├── persistentlog.go │ ├── raftkvs.go │ ├── raftkvs.tla │ ├── raftkvs_test.go │ └── timer.go ├── raftres │ ├── Makefile │ ├── README.md │ ├── cmd │ │ └── server │ │ │ └── main.go │ ├── configs │ │ ├── config.go │ │ └── local-3.yaml │ ├── go.mod │ ├── go.sum │ ├── kv │ │ ├── Makefile │ │ ├── client.go │ │ ├── helper.go │ │ ├── kv.cfg │ │ ├── kv.go │ │ ├── kv.tla │ │ ├── kv_test.go │ │ └── server.go │ ├── raft │ │ ├── Makefile │ │ ├── bootstrap │ │ │ ├── helper.go │ │ │ └── server.go │ │ ├── customch.go │ │ ├── models │ │ │ ├── MC.cfg │ │ │ └── SIM.cfg │ │ ├── persistentlog.go │ │ ├── raft.go │ │ ├── raft.tla │ │ ├── raft_test.go │ │ └── timer.go │ ├── raftres_test.go │ └── server.go ├── replicatedkv │ ├── go.mod │ ├── go.sum │ ├── replicated_kv.go │ └── replicated_kv.tla ├── shcounter │ ├── go.mod │ ├── go.sum │ ├── shcounter.go │ ├── shcounter.tla │ ├── shcounter_test.go │ └── util.go └── shopcart │ ├── Makefile │ ├── README.md │ ├── bootstrap.go │ ├── cmd │ └── main.go │ ├── configs │ ├── config.go │ └── local-4.yaml │ ├── go.mod │ ├── go.sum │ ├── shopcart.cfg │ ├── shopcart.go │ ├── shopcart.tla │ └── shopcart_test.go ├── test ├── files │ ├── general │ │ ├── AssignmentRules.tla │ │ ├── AssignmentRules.tla.noMultipleWrites │ │ ├── CallLabelingRules.tla │ │ ├── ExprTests.tla │ │ ├── ExprTests.tla.expectpcal │ │ ├── ExprTests.tla.gotests │ │ │ ├── ExprTests.go │ │ │ ├── ExprTests_test.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── IfEitherLabelingRules.tla │ │ ├── IndexingLocals.tla │ │ ├── IndexingLocals.tla.expectpcal │ │ ├── IndexingLocals.tla.gotests │ │ │ ├── IndexingLocals.go │ │ │ ├── IndexingLocals_test.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── InvalidAssignment.tla │ │ ├── LabelBeforeWhile.tla │ │ ├── LabelNotDefined.tla │ │ ├── MPCalKindMatching.tla │ │ ├── MacroRules.tla │ │ ├── MappingMacroRules.tla │ │ ├── MappingMacroWithCallGoto.tla │ │ ├── MappingWithLabels.tla │ │ ├── NestedCRDTImpl.tla │ │ ├── NestedCRDTImpl.tla.expectpcal │ │ ├── NoFirstLabel.tla │ │ ├── NonDetExploration.tla │ │ ├── NonDetExploration.tla.expectpcal │ │ ├── NonDetExploration.tla.gotests │ │ │ ├── NonDetExploration.go │ │ │ ├── NonDetExploration_test.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── PBFail4_bug125.tla │ │ ├── PBFail4_bug125.tla.expectpcal │ │ ├── PBFail4_bug125.tla.gotests │ │ │ ├── PBFail4_bug125.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── ProcedureSpaghetti.tla │ │ ├── ProcedureSpaghetti.tla.expectpcal │ │ ├── ProcedureSpaghetti.tla.gotests │ │ │ ├── ProcedureSpaghetti.go │ │ │ ├── ProcedureSpaghetti_test.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── ProxyRejoin_bug133.tla │ │ ├── RecursiveMacroRules.tla │ │ ├── ReservedLabels.tla │ │ ├── ReturnGotoLabelingRules.tla │ │ ├── WithRules.tla │ │ ├── aworset.tla │ │ ├── aworset.tla.expectpcal │ │ ├── bug2_124.tla │ │ ├── bug2_124.tla.expectpcal │ │ ├── bug2_124.tla.gotests │ │ │ ├── bug2_124.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── bug3_127.tla │ │ ├── bug_119.tla │ │ ├── bug_119.tla.expectpcal │ │ ├── bug_119.tla.gotests │ │ │ ├── bug_119.go │ │ │ ├── bug_119_test.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── bug_123.tla │ │ ├── dqueue.tla │ │ ├── dqueue.tla.expectpcal │ │ ├── evallock.tla │ │ ├── evallock.tla.expectpcal │ │ ├── gcounter.tla │ │ ├── gcounter.tla.expectpcal │ │ ├── hello.tla │ │ ├── hello.tla.expectpcal │ │ ├── hello.tla.gotests │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ ├── hello.go │ │ │ └── hello_test.go │ │ ├── load_balancer.tla │ │ ├── load_balancer.tla.expectpcal │ │ ├── pbkvs.tla │ │ ├── pbkvs.tla.expectpcal │ │ ├── proxy.tla │ │ ├── proxy.tla.expectpcal │ │ ├── raft.tla │ │ ├── raft.tla.expectpcal │ │ ├── raft.tla.gotests.ignore │ │ │ ├── customch.go │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ ├── raft.go │ │ │ ├── raft_test.go │ │ │ └── timer.go │ │ ├── raftkvs.tla │ │ ├── raftkvs.tla.expectpcal │ │ ├── raftkvs.tla.gotests.ignore │ │ │ ├── customch.go │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ ├── persistentlog.go │ │ │ ├── raftkvs.go │ │ │ ├── raftkvs_test.go │ │ │ └── timer.go │ │ ├── replicated_kv.tla │ │ ├── replicated_kv.tla.expectpcal │ │ ├── shcounter.tla │ │ └── shcounter.tla.expectpcal │ ├── gogen │ │ ├── EmptyBlock.tla │ │ ├── EmptyBlock.tla.gotests │ │ │ ├── EmptyBlock.go │ │ │ ├── emptyblock_test.go │ │ │ ├── go.mod │ │ │ └── go.sum │ │ ├── UnsupportedOps.tla │ │ ├── bug_167.tla │ │ └── bug_167.tla.gotests │ │ │ ├── bug_167.go │ │ │ ├── go.mod │ │ │ └── go.sum │ ├── pcalgen │ │ ├── AssignmentRulesNoMultipleWrites.tla │ │ ├── AssignmentRulesNoMultipleWrites.tla.expectpcal │ │ ├── BadNonRecursiveScoping1.tla │ │ ├── BadNonRecursiveScoping2.tla │ │ ├── BadPCalMarkerPlacementBeginMissing.tla │ │ ├── BadPCalMarkerPlacementBothMissing.tla │ │ ├── BadPCalMarkerPlacementEndMissing.tla │ │ ├── BadPCalMarkerPlacementSwapped.tla │ │ ├── DontEatMyLabels.tla │ │ ├── DontEatMyLabels.tla.expectpcal │ │ ├── EmptyBlock.tla │ │ ├── MPCalInstanceClauseSelf.tla │ │ ├── MPCalInstanceClauseSelf.tla.expectpcal │ │ ├── MPCalMultidimensionalAssignIndexOrder.tla │ │ ├── MPCalMultidimensionalAssignIndexOrder.tla.expectpcal │ │ ├── MPCalScopeBleed.tla │ │ ├── MPCalScopeBleed.tla.expectpcal │ │ ├── MappingMacroDeferredScoping.tla │ │ ├── MappingMacroNestedWithExpansion.tla │ │ ├── MappingMacroNestedWithExpansion.tla.expectpcal │ │ ├── MappingMacroRWExpansion.tla │ │ ├── ModuleWithoutExtends.tla │ │ ├── ModuleWithoutExtends.tla.expectpcal │ │ ├── MultiWithSemantics.tla │ │ ├── MultiWithSemantics.tla.expectpcal │ │ ├── ResourceAccessBoundByWith.tla │ │ ├── ResourceAccessBoundByWith.tla.expectpcal │ │ ├── StringBracketBug.tla │ │ ├── StringBracketBug.tla.expectpcal │ │ ├── TLAUnitsHorizontalLine.tla │ │ ├── TLAUnitsHorizontalLine.tla.expectpcal │ │ ├── UnboundRecursive.tla │ │ ├── bug_174_raft.tla │ │ ├── bug_195.tla │ │ ├── bug_195.tla.expectpcal │ │ ├── bug_195_simple.tla │ │ └── bug_195_simple.tla.expectpcal │ └── tla │ │ ├── 2pc.tla │ │ ├── DijkstraMutex.tla │ │ ├── Euclid.tla │ │ ├── Queens.tla │ │ ├── counter.tla │ │ ├── pgo2pc.tla │ │ └── round_robin.tla ├── pgo │ ├── CheckerTraceTests.scala │ ├── Commands.scala │ ├── DistsysTests.scala │ ├── FileTestSuite.scala │ ├── GoGenFileTests.scala │ ├── PCalGenFileTests.scala │ ├── PCalReadWriteTests.scala │ ├── TLAExpressionFuzzTests.scala │ ├── TLCTests.scala │ ├── TraversalSmokeTests.scala │ ├── model │ │ └── RewritableTests.scala │ └── util │ │ ├── TLAExprInterpreterTests.scala │ │ └── TLAExpressionFuzzTestUtils.scala └── traces │ ├── IndexingLocals_trace_1.txt │ ├── dqueue_trace_2.txt │ ├── hello_config.sc │ ├── hello_persistent_trace.txt │ ├── hello_shared_trace.txt │ ├── hello_simple_trace.txt │ ├── proxy_all_servers_running_RelaxedMailboxes.txt │ ├── proxy_all_servers_running_TCPMailboxes.txt │ ├── proxy_first_server_crashing_RelaxedMailboxes.txt │ ├── proxy_first_server_crashing_TCPMailboxes.txt │ ├── proxy_no_server_running_RelaxedMailboxes.txt │ ├── proxy_no_server_running_TCPMailboxes.txt │ ├── proxy_second_server_running_RelaxedMailboxes.txt │ └── proxy_second_server_running_TCPMailboxes.txt └── tools └── tla2tools.jar /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ 'main' ] 6 | pull_request: 7 | branches: [ 'main' ] 8 | 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | java-version: ['11', '21'] 15 | golang-version: ['1.22', '1.23'] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: actions/setup-go@v5 20 | with: 21 | go-version: ${{ matrix.golang-version }} 22 | - uses: VirtusLab/scala-cli-setup@main 23 | with: 24 | jvm: ${{ matrix.java-version }} 25 | - name: 'Run tests' 26 | run: scala-cli test . -- -l org.scalatest.tags.Slow 27 | - name: 'Upload Fuzz Test Results on Test Failure (in case there are some)' 28 | if: ${{ failure() }} 29 | uses: actions/upload-artifact@v4 30 | with: 31 | name: fuzz-test-results 32 | path: fuzz_output/* 33 | -------------------------------------------------------------------------------- /distsys/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/UBC-NSS/pgo/distsys 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | require ( 8 | github.com/benbjohnson/immutable v0.4.3 9 | github.com/dgraph-io/badger/v3 v3.2103.5 10 | github.com/segmentio/fasthash v1.0.3 11 | go.uber.org/multierr v1.11.0 12 | golang.org/x/sync v0.9.0 13 | ) 14 | 15 | require ( 16 | github.com/cespare/xxhash v1.1.0 // indirect 17 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 18 | github.com/dgraph-io/ristretto v0.1.1 // indirect 19 | github.com/dustin/go-humanize v1.0.1 // indirect 20 | github.com/gogo/protobuf v1.3.2 // indirect 21 | github.com/golang/glog v1.0.0 // indirect 22 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 23 | github.com/golang/protobuf v1.5.2 // indirect 24 | github.com/golang/snappy v0.0.4 // indirect 25 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 26 | github.com/klauspost/compress v1.15.15 // indirect 27 | github.com/pkg/errors v0.9.1 // indirect 28 | go.opencensus.io v0.24.0 // indirect 29 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 30 | golang.org/x/net v0.7.0 // indirect 31 | golang.org/x/sys v0.5.0 // indirect 32 | google.golang.org/protobuf v1.28.1 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /distsys/hashmap/hashmap.go: -------------------------------------------------------------------------------- 1 | package hashmap 2 | 3 | import ( 4 | "github.com/UBC-NSS/pgo/distsys/tla" 5 | ) 6 | 7 | type Entry[V any] struct { 8 | Key tla.Value 9 | Value V 10 | } 11 | 12 | type HashMap[V any] struct { 13 | m map[uint32][]Entry[V] 14 | keys []tla.Value 15 | } 16 | 17 | func New[V any]() *HashMap[V] { 18 | return &HashMap[V]{m: make(map[uint32][]Entry[V])} 19 | } 20 | 21 | func (h *HashMap[V]) Set(k tla.Value, v V) { 22 | entry := Entry[V]{ 23 | Key: k, 24 | Value: v, 25 | } 26 | hash := k.Hash() 27 | if _, ok := h.m[hash]; !ok { 28 | h.keys = append(h.keys, k) 29 | h.m[hash] = append(h.m[hash], entry) 30 | } else { 31 | for i := range h.m[hash] { 32 | if h.m[hash][i].Key.Equal(k) { 33 | h.m[hash][i].Value = v 34 | return 35 | } 36 | } 37 | h.keys = append(h.keys, k) 38 | h.m[hash] = append(h.m[hash], entry) 39 | } 40 | } 41 | 42 | func (h *HashMap[V]) Get(k tla.Value) (V, bool) { 43 | var v V 44 | entries, ok := h.m[k.Hash()] 45 | if !ok { 46 | return v, false 47 | } 48 | 49 | for _, e := range entries { 50 | if e.Key.Equal(k) { 51 | return e.Value, true 52 | } 53 | } 54 | return v, false 55 | } 56 | 57 | func (h *HashMap[V]) Keys() []tla.Value { 58 | return h.keys 59 | } 60 | 61 | func (h *HashMap[V]) Clear() { 62 | for k := range h.m { 63 | delete(h.m, k) 64 | } 65 | h.keys = nil 66 | } 67 | -------------------------------------------------------------------------------- /distsys/resources/conn.go: -------------------------------------------------------------------------------- 1 | package resources 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "time" 7 | ) 8 | 9 | type readWriterConnTimeout struct { 10 | conn net.Conn 11 | timeout time.Duration 12 | } 13 | 14 | var _ io.ReadWriter = &readWriterConnTimeout{} 15 | 16 | func makeReadWriterConnTimeout(conn net.Conn, timeout time.Duration) readWriterConnTimeout { 17 | return readWriterConnTimeout{ 18 | conn: conn, 19 | timeout: timeout, 20 | } 21 | } 22 | 23 | func (rw readWriterConnTimeout) Read(data []byte) (n int, err error) { 24 | if deadlineErr := rw.conn.SetReadDeadline(time.Now().Add(rw.timeout)); deadlineErr != nil { 25 | return 0, deadlineErr 26 | } 27 | n, err = rw.conn.Read(data) 28 | if deadlineErr := rw.conn.SetReadDeadline(time.Time{}); deadlineErr != nil { 29 | return n, deadlineErr 30 | } 31 | return 32 | } 33 | 34 | func (rw readWriterConnTimeout) Write(data []byte) (n int, err error) { 35 | if deadlineErr := rw.conn.SetWriteDeadline(time.Now().Add(rw.timeout)); deadlineErr != nil { 36 | return 0, deadlineErr 37 | } 38 | n, err = rw.conn.Write(data) 39 | if deadlineErr := rw.conn.SetWriteDeadline(time.Time{}); deadlineErr != nil { 40 | return n, deadlineErr 41 | } 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /distsys/resources/dummy.go: -------------------------------------------------------------------------------- 1 | package resources 2 | 3 | import ( 4 | "github.com/UBC-NSS/pgo/distsys" 5 | "github.com/UBC-NSS/pgo/distsys/tla" 6 | "github.com/UBC-NSS/pgo/distsys/trace" 7 | ) 8 | 9 | type DummyOption func(d *Dummy) 10 | 11 | func WithDummyValue(v tla.Value) DummyOption { 12 | return func(d *Dummy) { 13 | d.value = v 14 | } 15 | } 16 | 17 | func NewDummy(opts ...DummyOption) *Dummy { 18 | d := &Dummy{} 19 | for _, opt := range opts { 20 | opt(d) 21 | } 22 | return d 23 | } 24 | 25 | type Dummy struct { 26 | value tla.Value 27 | } 28 | 29 | func (res *Dummy) Abort() chan struct{} { 30 | return nil 31 | } 32 | 33 | func (res *Dummy) PreCommit() chan error { 34 | return nil 35 | } 36 | 37 | func (res *Dummy) Commit() chan struct{} { 38 | return nil 39 | } 40 | 41 | func (res *Dummy) ReadValue() (tla.Value, error) { 42 | return res.value, nil 43 | } 44 | 45 | func (res *Dummy) WriteValue(value tla.Value) error { 46 | return nil 47 | } 48 | 49 | func (res *Dummy) Index(index tla.Value) (distsys.ArchetypeResource, error) { 50 | return res, nil 51 | } 52 | 53 | func (res *Dummy) Close() error { 54 | return nil 55 | } 56 | 57 | func (res *Dummy) VClockHint(archClock trace.VClock) trace.VClock { 58 | return archClock 59 | } 60 | -------------------------------------------------------------------------------- /distsys/resources/placeholder.go: -------------------------------------------------------------------------------- 1 | package resources 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/UBC-NSS/pgo/distsys/trace" 7 | 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | 10 | "github.com/UBC-NSS/pgo/distsys" 11 | ) 12 | 13 | var ErrPlaceHolderAccess = errors.New("no access is allowed to PlaceHolder") 14 | 15 | type PlaceHolder struct{} 16 | 17 | // NewPlaceHolder produces a distsys.ArchetypeResource that does 18 | // nothing. It's just for usage of passing as placeholder for an archetype's 19 | // argument and calling any of its methods causes a panic. 20 | func NewPlaceHolder() distsys.ArchetypeResource { 21 | return &PlaceHolder{} 22 | } 23 | 24 | var _ distsys.ArchetypeResource = &PlaceHolder{} 25 | 26 | func (res *PlaceHolder) Abort() chan struct{} { 27 | panic(ErrPlaceHolderAccess) 28 | } 29 | 30 | func (res *PlaceHolder) PreCommit() chan error { 31 | panic(ErrPlaceHolderAccess) 32 | } 33 | 34 | func (res *PlaceHolder) Commit() chan struct{} { 35 | panic(ErrPlaceHolderAccess) 36 | } 37 | 38 | func (res *PlaceHolder) ReadValue() (tla.Value, error) { 39 | panic(ErrPlaceHolderAccess) 40 | } 41 | 42 | func (res *PlaceHolder) WriteValue(value tla.Value) error { 43 | panic(ErrPlaceHolderAccess) 44 | } 45 | 46 | func (res *PlaceHolder) Index(index tla.Value) (distsys.ArchetypeResource, error) { 47 | panic(ErrPlaceHolderAccess) 48 | } 49 | 50 | func (res *PlaceHolder) Close() error { 51 | return nil 52 | } 53 | 54 | func (res *PlaceHolder) VClockHint(archClock trace.VClock) trace.VClock { 55 | return archClock 56 | } 57 | -------------------------------------------------------------------------------- /distsys/tla/value_test.go: -------------------------------------------------------------------------------- 1 | package tla 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestTLAModel(t *testing.T) { 8 | type Record struct { 9 | Name string 10 | Operation func() Value 11 | ExpectedResult string 12 | } 13 | 14 | tests := []Record{ 15 | { 16 | Name: "Seq({})", 17 | Operation: func() Value { 18 | return ModuleSeq(MakeSet()) 19 | }, 20 | ExpectedResult: "{<<>>}", 21 | }, 22 | { 23 | Name: "\\E foo \\in {} : TRUE", 24 | Operation: func() Value { 25 | return QuantifiedExistential([]Value{MakeSet()}, func([]Value) bool { 26 | return true 27 | }) 28 | }, 29 | ExpectedResult: "FALSE", 30 | }, 31 | { 32 | Name: "[x \\in {} |-> x]", 33 | Operation: func() Value { 34 | return MakeRecord(nil) 35 | }, 36 | ExpectedResult: "[x \\in {} |-> x]", 37 | }, 38 | { 39 | Name: "1 .. 3", 40 | Operation: func() Value { 41 | return ModuleDotDotSymbol(MakeNumber(1), MakeNumber(4)) 42 | }, 43 | ExpectedResult: "{1, 2, 3, 4}", 44 | }, 45 | { 46 | Name: "function over empty set short-circuit", 47 | Operation: func() Value { 48 | return MakeFunction([]Value{MakeSet(MakeNumber(12)), MakeSet()}, func([]Value) Value { 49 | panic("should not be called") 50 | }) 51 | }, 52 | ExpectedResult: "[x \\in {} |-> x]", 53 | }, 54 | } 55 | 56 | for _, test := range tests { 57 | t.Run(test.Name, func(t *testing.T) { 58 | actualValue := test.Operation() 59 | actualStr := actualValue.String() 60 | if actualStr != test.ExpectedResult { 61 | t.Errorf("result %s did not equal expected value %s", actualStr, test.ExpectedResult) 62 | } 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /distsys/trace/recorder.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "sync" 7 | ) 8 | 9 | type Recorder interface { 10 | RecordEvent(event Event) 11 | } 12 | 13 | type localFileRecorder struct { 14 | lock sync.Mutex 15 | file *os.File 16 | encoder *json.Encoder 17 | } 18 | 19 | func MakeLocalFileRecorder(filename string) Recorder { 20 | file, err := os.Create(filename) 21 | if err != nil { 22 | panic(err) 23 | } 24 | return &localFileRecorder{ 25 | file: file, 26 | encoder: json.NewEncoder(file), 27 | } 28 | } 29 | 30 | func (recorder *localFileRecorder) RecordEvent(event Event) { 31 | recorder.lock.Lock() 32 | defer recorder.lock.Unlock() 33 | 34 | err := recorder.encoder.Encode(event) 35 | if err != nil { 36 | panic(err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /doc/papers/SPLASHAbstract.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistCompiler/pgo/0008579c554dd5d66a343ea4c727b75ad68d4ee5/doc/papers/SPLASHAbstract.pdf -------------------------------------------------------------------------------- /doc/papers/asplosb23main-p12-p-e73de3693c-62943-final.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistCompiler/pgo/0008579c554dd5d66a343ea4c727b75ad68d4ee5/doc/papers/asplosb23main-p12-p-e73de3693c-62943-final.pdf -------------------------------------------------------------------------------- /go_versions.sc: -------------------------------------------------------------------------------- 1 | //val minGoVersion = "1.22" 2 | 3 | os.walk(os.pwd) 4 | .filter(os.isDir) 5 | .filter(dir => os.exists(dir / "go.mod")) 6 | .foreach { dir => 7 | println(s"updating $dir...") 8 | 9 | os.call(List("go", "get", "-u"), cwd = dir) 10 | os.call(List("go", "mod", "tidy"), cwd = dir) 11 | 12 | // val modLines = os.read.lines(dir / "go.mod") 13 | 14 | // os.write.over(dir / "go.mod", modLines 15 | // .iterator 16 | // .flatMap { 17 | // case s"go $version" => Iterator.single(s"go $minGoVersion") 18 | // case s"toolchain $_" => Iterator.empty 19 | // case line => Iterator.single(line) 20 | // } 21 | // .map(_ ++ "\n") 22 | // ) 23 | } 24 | -------------------------------------------------------------------------------- /project.scala: -------------------------------------------------------------------------------- 1 | // Main 2 | //> using scala "2.13.15" 3 | //> using options "-deprecation" 4 | 5 | //> using dependency "com.lihaoyi:::ammonite:3.0.0" 6 | //> using dependency "com.lihaoyi::os-lib:0.11.3" 7 | //> using dependency "com.lihaoyi::upickle:1.5.0" 8 | //> using dependency "io.github.java-diff-utils:java-diff-utils:4.12" 9 | //> using dependency "org.rogach::scallop:5.1.0" 10 | //> using dependency "org.scala-lang.modules::scala-parser-combinators:2.4.0" 11 | //> using dependency "org.scala-lang:scala-reflect:2.13.15" 12 | 13 | //> using exclude "systems/" 14 | 15 | // Test 16 | //> using testFramework org.scalatest.tools.Framework 17 | //> using test.dependency "com.github.daddykotex::courier:3.2.0" 18 | //> using test.dependency "com.lihaoyi::mainargs:0.7.6" 19 | //> using test.dependency "com.lihaoyi::pprint:0.9.0" 20 | //> using test.dependency "com.lihaoyi::upickle:4.0.2" 21 | //> using test.dependency "org.scalacheck::scalacheck:1.18.1" 22 | //> using test.dependency "org.scalatest::scalatest:3.2.19" 23 | 24 | -------------------------------------------------------------------------------- /src/pgo/checker/CheckingErrors.scala: -------------------------------------------------------------------------------- 1 | package pgo.checker 2 | 3 | import pgo.model.{PGoError, SourceLocation} 4 | import pgo.util.Description 5 | import Description._ 6 | import pgo.util.TLAExprInterpreter.TLAValue 7 | 8 | sealed abstract class CheckingError(override val description: Description, 9 | override val sourceLocation: SourceLocation) extends PGoError with PGoError.Error { 10 | override def errors: List[PGoError.Error] = List(this) 11 | } 12 | 13 | final case class InitialStateError(reason: Description) extends CheckingError( 14 | description = reason, 15 | sourceLocation = SourceLocation.unknown) 16 | -------------------------------------------------------------------------------- /src/pgo/checker/TraceElement.scala: -------------------------------------------------------------------------------- 1 | package pgo.checker 2 | 3 | import CriticalSectionInterpreter.CSElement 4 | import pgo.util.Description 5 | import pgo.util.TLAExprInterpreter.TLAValue 6 | 7 | import Description._ 8 | 9 | final case class TraceElement(archetypeName: String, self: TLAValue, elements: List[CSElement], clock: VClock) { 10 | def clockKey: (String,TLAValue) = (archetypeName, self) 11 | def index: Long = clock(clockKey) 12 | 13 | def describe: Description = 14 | d"trace element at $archetypeName(${self.describe}) with ${ 15 | clock.describe 16 | }\nand ${ 17 | if(elements.isEmpty) { 18 | d"no known operations" 19 | } else { 20 | d"operations" + 21 | elements.view 22 | .map(_.describe.ensureLineBreakBefore) 23 | .flattenDescriptions 24 | .indented 25 | } 26 | }" 27 | } 28 | 29 | object TraceElement { 30 | def fromJSON(v: ujson.Value): TraceElement = 31 | TraceElement( 32 | archetypeName = v("archetypeName").str, 33 | self = TLAValue.parseFromString(v("self").str), 34 | elements = 35 | v("csElements").arr.iterator 36 | .map(CSElement.fromJSON) 37 | .toList, 38 | clock = VClock.fromJSON(v("clock"))) 39 | } 40 | -------------------------------------------------------------------------------- /src/pgo/model/Definitions.scala: -------------------------------------------------------------------------------- 1 | package pgo.model 2 | 3 | import scala.collection.View 4 | 5 | sealed trait Definition { 6 | def singleDefinitions: View[DefinitionOne] 7 | } 8 | 9 | object Definition { 10 | sealed abstract class ScopeIdentifier { 11 | def sourceLocation: SourceLocation 12 | } 13 | object ScopeIdentifier { 14 | implicit val scopeIdentifierOrdered: Ordering[ScopeIdentifier] = Ordering.by[ScopeIdentifier,(Boolean,String)] { 15 | case ScopeIdentifierName(name) => (false, name.id) 16 | case ScopeIdentifierSymbol(symbol) => (true, symbol.symbol.representations.head) 17 | } 18 | } 19 | final case class ScopeIdentifierName(name: tla.TLAIdentifier) extends ScopeIdentifier { 20 | override def sourceLocation: SourceLocation = name.sourceLocation 21 | } 22 | final case class ScopeIdentifierSymbol(symbol: tla.TLASymbol) extends ScopeIdentifier { 23 | override def sourceLocation: SourceLocation = symbol.sourceLocation 24 | } 25 | } 26 | 27 | trait DefinitionOne extends Definition with RefersTo.HasReferences { 28 | override def singleDefinitions: View[DefinitionOne] = View(this) 29 | 30 | override def canonicalIdString: String = 31 | identifier match { 32 | case Definition.ScopeIdentifierName(name) => 33 | name.id 34 | case Definition.ScopeIdentifierSymbol(symbol) => 35 | symbol.symbol.stringReprDefn 36 | } 37 | 38 | def arity: Int 39 | def identifier: Definition.ScopeIdentifier 40 | 41 | def isModuleInstance: Boolean = false 42 | def isLocal: Boolean = false 43 | def scope: Map[Definition.ScopeIdentifier, DefinitionOne] = Map.empty 44 | } 45 | 46 | trait DefinitionComposite extends Definition { 47 | def definitions: View[Definition] 48 | override def singleDefinitions: View[DefinitionOne] = 49 | definitions.flatMap(_.singleDefinitions) 50 | } 51 | -------------------------------------------------------------------------------- /src/pgo/model/PGoError.scala: -------------------------------------------------------------------------------- 1 | package pgo.model 2 | 3 | import pgo.util.Description 4 | 5 | import Description._ 6 | 7 | abstract class PGoError extends RuntimeException { 8 | override def getMessage: String = 9 | errors.view.map { err => 10 | val locDesc = 11 | if(err.sourceLocation ne SourceLocation.unknown) { 12 | d" at ${err.sourceLocation.longDescription}" 13 | } else { 14 | d"" 15 | } 16 | 17 | d"${err.description}$locDesc".ensureLineBreakBefore 18 | } 19 | .flattenDescriptions 20 | .linesIterator 21 | .mkString("\n") 22 | 23 | def errors: List[PGoError.Error] 24 | } 25 | 26 | object PGoError { 27 | trait Error { 28 | def sourceLocation: SourceLocation 29 | def description: Description 30 | def productPrefix: String 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/pgo/model/RefersTo.scala: -------------------------------------------------------------------------------- 1 | package pgo.model 2 | 3 | import scala.reflect.ClassTag 4 | 5 | trait RefersTo[T <: RefersTo.HasReferences] extends Rewritable { 6 | private var refersTo_ : Option[T] = None 7 | def refersTo: T = refersTo_.get 8 | def setRefersTo(refersTo: T): this.type = { 9 | refersTo_ = Some(refersTo) 10 | this 11 | } 12 | def hasRefersTo: Boolean = refersTo_.nonEmpty 13 | 14 | override def decorateLike(succ: this.type): this.type = 15 | super.decorateLike(succ.setRefersTo(refersTo)) 16 | } 17 | 18 | object RefersTo { 19 | def unapply[T <: HasReferences](candidate: RefersTo[T])(implicit classTag: ClassTag[T]): Option[T] = 20 | candidate match { 21 | case refersTo: RefersTo[T @unchecked] if classTag.runtimeClass.isInstance(refersTo.refersTo) => 22 | Some(refersTo.refersTo) 23 | case _ => None 24 | } 25 | 26 | trait HasReferences { 27 | def canonicalIdString: String 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/pgo/model/SourceLocatable.scala: -------------------------------------------------------------------------------- 1 | package pgo.model 2 | 3 | trait SourceLocatable { 4 | private var _sourceLocation: SourceLocation = SourceLocation.unknown 5 | 6 | def sourceLocation: SourceLocation = _sourceLocation 7 | 8 | def setSourceLocation(sourceLocation: SourceLocation): this.type = { 9 | _sourceLocation = sourceLocation 10 | this 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/pgo/parser/LineColumnAwareCharReader.scala: -------------------------------------------------------------------------------- 1 | package pgo.parser 2 | 3 | import pgo.model.SourceLocation 4 | 5 | import scala.util.parsing.input.{Position, Reader} 6 | 7 | /** 8 | * A generic Char reader wrapper, which counts line and column numbers within some input as it reads. 9 | * 10 | * TLA+ parsing assumes this wrapper is available, because parsing of /\ and \/ relies on knowing indentation. 11 | * 12 | * @param underlying the underlying Reader to wrap 13 | * @param line the current line. Either leave at its default of 0, or set this to the line 14 | * from which you want to start counting. 15 | * @param column the current column. See above. 16 | */ 17 | final class LineColumnAwareCharReader(val underlying : Reader[Char], val underlyingText: SourceLocation.UnderlyingText, 18 | val line : Int = 0, val column : Int = 0) extends Reader[Char] { 19 | override def first: Char = underlying.first 20 | 21 | override def toString: String = underlying.toString 22 | 23 | override lazy val rest: LineColumnAwareCharReader = 24 | if (atEnd) { 25 | this 26 | } else { 27 | if (first == '\n') { 28 | new LineColumnAwareCharReader(underlying.rest, underlyingText, line + 1, 0) 29 | } else { 30 | new LineColumnAwareCharReader(underlying.rest, underlyingText, line, column + 1) 31 | } 32 | } 33 | 34 | def currentSourceLocation: SourceLocation = 35 | SourceLocation(underlyingText, startOffset = offset, endOffset = offset, 36 | startLine = line, endLine = line, startColumn = column, endColumn = column) 37 | 38 | override def pos: Position = underlying.pos 39 | override def atEnd: Boolean = underlying.atEnd 40 | override def source: CharSequence = underlying.source 41 | override def offset: Int = underlying.offset 42 | } 43 | -------------------------------------------------------------------------------- /src/pgo/parser/MPCalParserContext.scala: -------------------------------------------------------------------------------- 1 | package pgo.parser 2 | 3 | import pgo.model.Definition 4 | import pgo.model.mpcal.{MPCalArchetype, MPCalMappingMacro} 5 | import pgo.model.tla.TLAIdentifier 6 | 7 | final case class MPCalParserContext(mappingMacros: Map[TLAIdentifier,MPCalMappingMacro] = Map.empty, 8 | archetypes: Map[TLAIdentifier,MPCalArchetype] = Map.empty)(implicit val ctx: PCalParserContext) { 9 | def withDefinition(defn: Definition): MPCalParserContext = 10 | copy()(ctx.withDefinition(defn)) 11 | 12 | def withArchetype(archetype: MPCalArchetype): MPCalParserContext = 13 | copy(archetypes = archetypes.updated(archetype.name, archetype)) 14 | 15 | def withMappingMacro(mappingMacro: MPCalMappingMacro): MPCalParserContext = 16 | copy(mappingMacros = mappingMacros.updated(mappingMacro.name, mappingMacro)) 17 | 18 | def withLateBinding: MPCalParserContext = 19 | copy()(ctx.withLateBinding) 20 | } 21 | 22 | object MPCalParserContext { 23 | implicit def getPCalParserContext(implicit ctx: MPCalParserContext): PCalParserContext = ctx.ctx 24 | } 25 | -------------------------------------------------------------------------------- /src/pgo/parser/PCalParserContext.scala: -------------------------------------------------------------------------------- 1 | package pgo.parser 2 | 3 | import pgo.model.pcal._ 4 | import pgo.model.Definition 5 | import pgo.model.tla.TLAIdentifier 6 | 7 | final case class PCalParserContext()(implicit val ctx: TLAParserContext) { 8 | def withDefinition(defn: Definition): PCalParserContext = 9 | copy()(ctx.withDefinition(defn)) 10 | 11 | def withProcessSelf(self: PCalVariableDeclarationBound): PCalParserContext = 12 | copy()(ctx.copy( 13 | currentScope = ctx.currentScope.updated( 14 | Definition.ScopeIdentifierName(TLAIdentifier("self").setSourceLocation(self.sourceLocation)), 15 | self))) 16 | 17 | def withLateBinding: PCalParserContext = 18 | copy()(ctx.withLateBinding) 19 | } 20 | 21 | object PCalParserContext { 22 | implicit def getTLAParserContext(implicit ctx: PCalParserContext): TLAParserContext = ctx.ctx 23 | } 24 | -------------------------------------------------------------------------------- /src/pgo/parser/ParsingUtils.scala: -------------------------------------------------------------------------------- 1 | package pgo.parser 2 | 3 | import pgo.model.SourceLocation 4 | 5 | import scala.util.parsing.combinator.Parsers 6 | import scala.util.parsing.input.CharSequenceReader 7 | 8 | trait ParsingUtils extends Parsers { 9 | def buildReader(seq: CharSequence, underlyingText: SourceLocation.UnderlyingText): LineColumnAwareCharReader = { 10 | val reader = new CharSequenceReader(seq) 11 | val lcReader = new LineColumnAwareCharReader(reader, underlyingText) 12 | lcReader 13 | } 14 | 15 | def checkResult[T](result: =>ParseResult[T]): T = 16 | result match { 17 | case Success(result, _) => result 18 | case NoSuccess(err, in) => 19 | throw ParseFailureError(err, in.asInstanceOf[LineColumnAwareCharReader].currentSourceLocation) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/pgo/util/NameCleaner.scala: -------------------------------------------------------------------------------- 1 | package pgo.util 2 | 3 | import scala.collection.mutable 4 | 5 | class NameCleaner { 6 | private val namesSeen = mutable.HashSet[String]() 7 | // optimisation: if the same hint is used multiple times, avoid recomputing all previously checked variations 8 | // by restarting the search for a clean name at the last clean name's index + 1 9 | private val hintCounterAcc = mutable.HashMap[String,Int]() 10 | 11 | def addKnownName(name: String): this.type = { 12 | namesSeen += name 13 | this 14 | } 15 | 16 | def copy(): NameCleaner = { 17 | val cleaner = new NameCleaner 18 | cleaner.namesSeen ++= namesSeen 19 | cleaner.hintCounterAcc ++= hintCounterAcc 20 | cleaner 21 | } 22 | 23 | def cleanName(hint: String): String = { 24 | if(namesSeen(hint)) { 25 | var currSuffix = hintCounterAcc.getOrElse(hint, 0) 26 | var currName = s"$hint$currSuffix" 27 | while(namesSeen(currName)) { 28 | currSuffix += 1 29 | currName = s"$hint$currSuffix" 30 | } 31 | namesSeen += currName 32 | hintCounterAcc(hint) = currSuffix + 1 33 | currName 34 | } else { 35 | namesSeen += hint 36 | hint 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/pgo/util/Unreachable.scala: -------------------------------------------------------------------------------- 1 | package pgo.util 2 | 3 | case class Unreachable() extends RuntimeException("this code should never be reached") 4 | -------------------------------------------------------------------------------- /src/pgo/util/package.scala: -------------------------------------------------------------------------------- 1 | package pgo 2 | 3 | package object util { 4 | def !!! : Nothing = throw Unreachable() 5 | } 6 | -------------------------------------------------------------------------------- /syntax/pygments/README.md: -------------------------------------------------------------------------------- 1 | # MPCal Pygments 2 | 3 | Pygments plugin for MPCal. 4 | 5 | ## Install 6 | 7 | Run the following commands in the `pygments` directory: 8 | ```shell 9 | pip install -r requirements.txt 10 | sudo python3 setup.py develop 11 | ``` 12 | 13 | ## Lexers 14 | 15 | There are two available lexers: 16 | 17 | * `mpcal`: 18 | For highlighting MPCal code blocks. 19 | * `tla+`: 20 | TLA+ highlighting that supports PlusCal and MPCal languages in the comments. 21 | 22 | ## Usage 23 | 24 | You can use this plugin with command line. For example, output an HTML file, `output.html`, from an 25 | input TLA file, `/path/to/input.tla`: 26 | ```shell 27 | pygmentize -f html -O full -o output.html /path/to/input.tla 28 | ``` 29 | 30 | For syntax highlighting inside Latex docuemtns, we use [minted](https://ctan.org/pkg/minted?lang=en) package. For more 31 | information see the provided [example](latex/main.tex). -------------------------------------------------------------------------------- /syntax/pygments/latex/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistCompiler/pgo/0008579c554dd5d66a343ea4c727b75ad68d4ee5/syntax/pygments/latex/main.pdf -------------------------------------------------------------------------------- /syntax/pygments/mpcallexer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistCompiler/pgo/0008579c554dd5d66a343ea4c727b75ad68d4ee5/syntax/pygments/mpcallexer/__init__.py -------------------------------------------------------------------------------- /syntax/pygments/requirements.txt: -------------------------------------------------------------------------------- 1 | Pygments==2.9.0 -------------------------------------------------------------------------------- /syntax/pygments/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='mpcallexer', 5 | packages=find_packages(), 6 | entry_points= 7 | """ 8 | [pygments.lexers] 9 | tlapluslexer = mpcallexer.lexer:TLAplusLexer 10 | mpcallexer = mpcallexer.lexer:MPCalLexer 11 | """, 12 | ) 13 | -------------------------------------------------------------------------------- /syntax/vscode/mpcal/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /syntax/vscode/mpcal/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "mpcal" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /syntax/vscode/mpcal/README.md: -------------------------------------------------------------------------------- 1 | # MPCal VSCode Extension 2 | 3 | This extension provides MPCal syntax highlighting for vscode. 4 | 5 | ## Install 6 | 7 | * To start using this extension with Visual Studio Code copy this folder into the `/.vscode/extensions` folder and restart Code. 8 | * Note: you should copy *the entire mpcal folder* over, to make `/.vscode/extensions/mpcal`. 9 | ``` 10 | cp -r ./ ~/.vscode/extensions/mpcal 11 | ``` -------------------------------------------------------------------------------- /syntax/vscode/mpcal/cfg-language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "\\*", 4 | "blockComment": [ "(*", "*)" ] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /syntax/vscode/mpcal/mpcal-0.0.1.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistCompiler/pgo/0008579c554dd5d66a343ea4c727b75ad68d4ee5/syntax/vscode/mpcal/mpcal-0.0.1.vsix -------------------------------------------------------------------------------- /syntax/vscode/mpcal/mpcal-language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "\\*", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "(*", "*)" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | ["\"", "\""], 20 | ["'", "'"] 21 | ], 22 | // symbols that can be used to surround a selection 23 | "surroundingPairs": [ 24 | ["{", "}"], 25 | ["[", "]"], 26 | ["(", ")"], 27 | ["\"", "\""], 28 | ["'", "'"] 29 | ] 30 | } -------------------------------------------------------------------------------- /syntax/vscode/mpcal/pluscal-language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "\\*", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "(*", "*)" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | ["\"", "\""], 20 | ["'", "'"] 21 | ], 22 | // symbols that can be used to surround a selection 23 | "surroundingPairs": [ 24 | ["{", "}"], 25 | ["[", "]"], 26 | ["(", ")"], 27 | ["\"", "\""], 28 | ["'", "'"] 29 | ] 30 | } -------------------------------------------------------------------------------- /syntax/vscode/mpcal/syntaxes/cfg.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "TLA+ Model Config", 4 | "patterns": [ 5 | { 6 | "include": "#keywords" 7 | }, 8 | { 9 | "include": "#line_comments" 10 | }, 11 | { 12 | "include": "#block_comments" 13 | }, 14 | { 15 | "include": "#strings" 16 | }, 17 | { 18 | "include": "#constants" 19 | }, 20 | { 21 | "include": "#const_definitions" 22 | } 23 | ], 24 | "repository": { 25 | "keywords": { 26 | "name": "keyword.control", 27 | "match": "\\b(SPECIFICATION|INVARIANT(S)?|PROPERT(Y|IES)|CONSTANT(S)?|INIT|NEXT|SYMMETRY|CONSTRAINT(S)?|ACTION_CONSTRAINT(S)?|VIEW|CHECK_DEADLOCK|POSTCONDITION|ALIAS)\\b" 28 | }, 29 | "line_comments": { 30 | "name": "comment.line", 31 | "begin": "\\\\\\*", 32 | "end": "$" 33 | }, 34 | "block_comments": { 35 | "name": "comment.block", 36 | "begin": "\\(\\*", 37 | "end": "\\*\\)" 38 | }, 39 | "strings": { 40 | "name": "string.quoted.double.tlaplus", 41 | "begin": "\"", 42 | "end": "\"", 43 | "patterns": [ 44 | { 45 | "name": "constant.character.escape.tlaplus", 46 | "match": "\\\\." 47 | } 48 | ] 49 | }, 50 | "constants": { 51 | "name": "support.constant.tlaplus", 52 | "match": "\\b(TRUE|FALSE|\\d+)\\b" 53 | }, 54 | "const_definitions": { 55 | "match": "(\\w+)\\s*(?:=|<-)", 56 | "captures": { 57 | "1": { 58 | "name": "support.constant.tlaplus" 59 | } 60 | } 61 | } 62 | }, 63 | "scopeName": "source.cfg" 64 | } 65 | -------------------------------------------------------------------------------- /syntax/vscode/mpcal/syntaxes/mpcal.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "mpcal", 4 | "patterns": [ 5 | { 6 | "include": "#code-block" 7 | }, 8 | { 9 | "include": "#keywords-control" 10 | }, 11 | { 12 | "include": "#keywords" 13 | }, 14 | { 15 | "include": "#variables" 16 | }, 17 | { 18 | "include": "#strings" 19 | }, 20 | { 21 | "include": "#comment-line" 22 | }, 23 | { 24 | "include": "#comment-block" 25 | }, 26 | { 27 | "include": "source.tlaplus.pluscal" 28 | } 29 | ], 30 | "repository": { 31 | "code-block": { 32 | "begin": "{", 33 | "end": "}", 34 | "patterns": [{ 35 | "include": "$self" 36 | }] 37 | }, 38 | "keywords-control": { 39 | "name": "keyword.control.mpcal", 40 | "match": "\\b(yield)\\b" 41 | }, 42 | "keywords": { 43 | "name": "keyword.other.mpcal", 44 | "match": "\\b(archetype|instance|ref|mapping|read|write|via)\\b" 45 | }, 46 | "variables": { 47 | "name": "variable.parameter", 48 | "match": "(\\$variable|\\$value)\\b" 49 | }, 50 | "strings": { 51 | "name": "string.quoted.double.mpcal", 52 | "begin": "\"", 53 | "end": "\"", 54 | "patterns": [ 55 | { 56 | "name": "constant.character.escape.mpcal", 57 | "match": "\\\\." 58 | } 59 | ] 60 | }, 61 | "comment-line": { 62 | "name": "comment.line.mpcal", 63 | "begin": "\\\\\\*", 64 | "end": "$" 65 | }, 66 | "comment-block": { 67 | "name": "comment.block.mpcal", 68 | "begin": "\\(\\*", 69 | "end": "\\*\\)" 70 | } 71 | }, 72 | "scopeName": "source.tlaplus.mpcal" 73 | } -------------------------------------------------------------------------------- /syntax/vscode/mpcal/syntaxes/pluscal.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "pluscal", 4 | "patterns": [ 5 | { 6 | "include": "#code-block" 7 | }, 8 | { 9 | "include": "#keywords-control" 10 | }, 11 | { 12 | "include": "#keywords" 13 | }, 14 | { 15 | "include": "#variable-language" 16 | }, 17 | { 18 | "include": "#strings" 19 | }, 20 | { 21 | "include": "#comment-line" 22 | }, 23 | { 24 | "include": "#comment-block" 25 | }, 26 | { 27 | "include": "#punctuation" 28 | }, 29 | { 30 | "include": "source.tlaplus" 31 | } 32 | ], 33 | "repository": { 34 | "code-block": { 35 | "begin": "{", 36 | "end": "}", 37 | "patterns": [{ 38 | "include": "$self" 39 | }] 40 | }, 41 | "keywords-control": { 42 | "name": "keyword.control.pluscal", 43 | "match": "\\b(do|call|else|elsif|goto|if|return|skip|then|while|either|or|await|when|fair|with)\\b" 44 | }, 45 | "keywords": { 46 | "name": "keyword.other.pluscal", 47 | "match": "\\b(define|macro|procedure|process|assert|begin|end|print|algorithm|variable(s)?)\\b" 48 | }, 49 | "variable-language": { 50 | "name": "variable.language.pluscal", 51 | "match": "\\b(self)\\b" 52 | }, 53 | "strings": { 54 | "name": "string.quoted.double.pluscal", 55 | "begin": "\"", 56 | "end": "\"", 57 | "patterns": [ 58 | { 59 | "name": "constant.character.escape.pluscal", 60 | "match": "\\\\." 61 | } 62 | ] 63 | }, 64 | "comment-line": { 65 | "name": "comment.line.pluscal", 66 | "begin": "\\\\\\*", 67 | "end": "$" 68 | }, 69 | "comment-block": { 70 | "name": "comment.block.pluscal", 71 | "begin": "\\(\\*", 72 | "end": "\\*\\)" 73 | }, 74 | "punctuation": { 75 | "name": "punctuation.pluscal", 76 | "match": "(:=|\\|\\|)" 77 | } 78 | }, 79 | "scopeName": "source.tlaplus.pluscal" 80 | } -------------------------------------------------------------------------------- /syntax/vscode/mpcal/tlaplus-language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "\\*", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "(*", "*)" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | ["\"", "\""], 20 | ["'", "'"] 21 | ], 22 | // symbols that can be used to surround a selection 23 | "surroundingPairs": [ 24 | ["{", "}"], 25 | ["[", "]"], 26 | ["(", ")"], 27 | ["\"", "\""], 28 | ["'", "'"] 29 | ] 30 | } -------------------------------------------------------------------------------- /systems/README.md: -------------------------------------------------------------------------------- 1 | # Systems 2 | 3 | This directory contains the systems built using PGo. Each system has the MPCal 4 | model, generated Go code and handwritten Go code for bootstrapping the system. 5 | 6 | Here is the list of systems and a short description of them. Check out each 7 | system for more details. 8 | 9 | - [dqueue](/systems/dqueue/) demonstrates a simple distributed queue, where 10 | producers and consumers communicate through the queue. 11 | - [gcounter](/systems/gcounter/) is a grow-only counter CRDT. 12 | - [loadbalancer](/systems/loadbalancer/) has a load balancer node that distributes incoming client 13 | requests to backend servers. 14 | - [locksvc](/systems/locksvc/) is a simple lock service system. 15 | - [nestedcrdtimpl](/systems/nestedcrdtimpl/) is generic CRDT-based system 16 | where the CRDT resource is built using PGo and MPCal. This system demonstrates 17 | the idea of verified resources. 18 | - [proxy](/systems/proxy/) is fault-tolerant proxy server. It sends the incoming requests to 19 | backend servers, and it can tolerate back-end servers failures. 20 | - [raftkvs](/systems/raftkvs/) is a monolithic key-value store based on the Raft consensus 21 | protocol. 22 | - [raftres](/systems/raftres/) is a modular composition of the pure Raft 23 | protocol and a naive distributed key-value store. This system demonstrates the 24 | modular verification and composition of PGo-based systems. 25 | - [replicatedkv](/systems/replicatedkv/) is a legacy replicated key-value store. 26 | - [shcounter](/systems/shcounter/) is a shared counter that shows the usage of 27 | 2PC-based shared resource. 28 | - [shopcart](/systems/shopcart/) is a CRDT-based set. -------------------------------------------------------------------------------- /systems/dqueue/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/dqueue 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.1.1 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/glog v1.0.0 // indirect 20 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 21 | github.com/golang/protobuf v1.5.2 // indirect 22 | github.com/golang/snappy v0.0.4 // indirect 23 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 24 | github.com/klauspost/compress v1.15.15 // indirect 25 | github.com/pkg/errors v0.9.1 // indirect 26 | github.com/segmentio/fasthash v1.0.3 // indirect 27 | go.opencensus.io v0.24.0 // indirect 28 | go.uber.org/multierr v1.11.0 // indirect 29 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 30 | golang.org/x/net v0.7.0 // indirect 31 | golang.org/x/sync v0.9.0 // indirect 32 | golang.org/x/sys v0.5.0 // indirect 33 | google.golang.org/protobuf v1.28.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /systems/gcounter/gcounter_test.go: -------------------------------------------------------------------------------- 1 | package gcounter 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "testing" 7 | 8 | "github.com/UBC-NSS/pgo/distsys" 9 | "github.com/UBC-NSS/pgo/distsys/tla" 10 | ) 11 | 12 | func TestGCounter_Node(t *testing.T) { 13 | numNodes := 10 14 | constants := []distsys.MPCalContextConfigFn{ 15 | distsys.DefineConstantValue("NUM_NODES", tla.MakeNumber(int32(numNodes))), 16 | distsys.DefineConstantValue("BENCH_NUM_ROUNDS", tla.MakeNumber(0)), 17 | } 18 | 19 | nodeAddrMap := make(map[tla.Value]string, numNodes+1) 20 | for i := 1; i <= numNodes; i++ { 21 | portNum := 9000 + i 22 | addr := fmt.Sprintf("localhost:%d", portNum) 23 | nodeAddrMap[tla.MakeNumber(int32(i))] = addr 24 | } 25 | 26 | var replicaCtxs []*distsys.MPCalContext 27 | errs := make(chan error, numNodes) 28 | for i := 1; i <= numNodes; i++ { 29 | ctx := getNodeMapCtx(tla.MakeNumber(int32(i)), nodeAddrMap, constants) 30 | replicaCtxs = append(replicaCtxs, ctx) 31 | go func() { 32 | errs <- ctx.Run() 33 | }() 34 | } 35 | 36 | defer func() { 37 | for _, ctx := range replicaCtxs { 38 | ctx.Stop() 39 | } 40 | }() 41 | 42 | getVal := func(ctx *distsys.MPCalContext) (tla.Value, error) { 43 | fs, err := ctx.IFace().RequireArchetypeResourceRef("ANode.cntr") 44 | if err != nil { 45 | return tla.Value{}, err 46 | } 47 | return ctx.IFace().Read(fs, []tla.Value{ctx.IFace().Self()}) 48 | } 49 | 50 | for i := 1; i <= numNodes; i++ { 51 | err := <-errs 52 | if err != nil { 53 | t.Fatalf("non-nil error from ANode archetype: %s", err) 54 | } 55 | } 56 | 57 | for _, ctx := range replicaCtxs { 58 | replicaVal, err := getVal(ctx) 59 | log.Printf("node %s's count: %s", ctx.IFace().Self(), replicaVal) 60 | if err != nil { 61 | t.Fatalf("could not read value from cntr") 62 | } 63 | if !replicaVal.Equal(tla.MakeNumber(int32(numNodes))) { 64 | t.Fatalf("expected values %v and %v to be equal", replicaVal, numNodes) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /systems/gcounter/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/gcounter 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.2.0 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 20 | github.com/golang/protobuf v1.5.4 // indirect 21 | github.com/golang/snappy v0.0.4 // indirect 22 | github.com/google/flatbuffers v24.3.25+incompatible // indirect 23 | github.com/klauspost/compress v1.17.11 // indirect 24 | github.com/pkg/errors v0.9.1 // indirect 25 | github.com/segmentio/fasthash v1.0.3 // indirect 26 | go.opencensus.io v0.24.0 // indirect 27 | go.uber.org/multierr v1.11.0 // indirect 28 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 29 | golang.org/x/net v0.31.0 // indirect 30 | golang.org/x/sync v0.9.0 // indirect 31 | golang.org/x/sys v0.27.0 // indirect 32 | google.golang.org/protobuf v1.35.2 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /systems/loadbalancer/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/loadbalancer 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.1.1 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/glog v1.0.0 // indirect 20 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 21 | github.com/golang/protobuf v1.5.2 // indirect 22 | github.com/golang/snappy v0.0.4 // indirect 23 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 24 | github.com/klauspost/compress v1.15.15 // indirect 25 | github.com/pkg/errors v0.9.1 // indirect 26 | github.com/segmentio/fasthash v1.0.3 // indirect 27 | go.opencensus.io v0.24.0 // indirect 28 | go.uber.org/multierr v1.11.0 // indirect 29 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 30 | golang.org/x/net v0.7.0 // indirect 31 | golang.org/x/sync v0.9.0 // indirect 32 | golang.org/x/sys v0.5.0 // indirect 33 | google.golang.org/protobuf v1.28.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /systems/locksvc/Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | JAVA=java 3 | JAVA_OPTS=-XX:+IgnoreUnrecognizedVMOptions -XX:+UseParallelGC 4 | TLA2TOOLS_JAR=../../tools/tla2tools.jar 5 | 6 | TLC_CMD=$(JAVA) $(JAVA_OPTS) -cp $(TLA2TOOLS_JAR) 7 | 8 | TLC=$(TLC_CMD) tlc2.TLC 9 | PCAL=$(TLC_CMD) pcal.trans 10 | SANY=$(TLC_CMD) tla2sany.SANY 11 | 12 | mc: 13 | $(TLC) -config locksvc.cfg -workers 'auto' -cleanup locksvc.tla 14 | 15 | tlaplusgen: 16 | $(PCAL) -nocfg locksvc.tla 17 | 18 | sany: 19 | $(SANY) locksvc.tla 20 | -------------------------------------------------------------------------------- /systems/locksvc/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/locksvc 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.1.1 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/glog v1.0.0 // indirect 20 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 21 | github.com/golang/protobuf v1.5.2 // indirect 22 | github.com/golang/snappy v0.0.4 // indirect 23 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 24 | github.com/klauspost/compress v1.15.15 // indirect 25 | github.com/pkg/errors v0.9.1 // indirect 26 | github.com/segmentio/fasthash v1.0.3 // indirect 27 | go.opencensus.io v0.24.0 // indirect 28 | go.uber.org/multierr v1.11.0 // indirect 29 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 30 | golang.org/x/net v0.7.0 // indirect 31 | golang.org/x/sync v0.9.0 // indirect 32 | golang.org/x/sys v0.5.0 // indirect 33 | google.golang.org/protobuf v1.28.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /systems/locksvc/locksvc.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT NumClients = 5 5 | 6 | \* SPECIFICATION definition 7 | SPECIFICATION SpecNoDeadlock 8 | 9 | \* INVARIANT definition 10 | INVARIANT Safety 11 | 12 | \* PROPERTY definition 13 | PROPERTY Liveness -------------------------------------------------------------------------------- /systems/nestedcrdtimpl/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/nestedcrdtimpl 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require ( 10 | github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 11 | github.com/benbjohnson/immutable v0.4.3 12 | ) 13 | 14 | require ( 15 | github.com/cespare/xxhash v1.1.0 // indirect 16 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 17 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 18 | github.com/dgraph-io/ristretto v0.1.1 // indirect 19 | github.com/dustin/go-humanize v1.0.1 // indirect 20 | github.com/gogo/protobuf v1.3.2 // indirect 21 | github.com/golang/glog v1.0.0 // indirect 22 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 23 | github.com/golang/protobuf v1.5.2 // indirect 24 | github.com/golang/snappy v0.0.4 // indirect 25 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 26 | github.com/klauspost/compress v1.15.15 // indirect 27 | github.com/pkg/errors v0.9.1 // indirect 28 | github.com/segmentio/fasthash v1.0.3 // indirect 29 | go.opencensus.io v0.24.0 // indirect 30 | go.uber.org/multierr v1.11.0 // indirect 31 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 32 | golang.org/x/net v0.7.0 // indirect 33 | golang.org/x/sync v0.9.0 // indirect 34 | golang.org/x/sys v0.5.0 // indirect 35 | google.golang.org/protobuf v1.28.1 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /systems/nestedcrdtimpl/timer.go: -------------------------------------------------------------------------------- 1 | package nestedcrdtimpl 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/UBC-NSS/pgo/distsys" 7 | "github.com/UBC-NSS/pgo/distsys/tla" 8 | ) 9 | 10 | func NewTimer(d time.Duration) distsys.ArchetypeResource { 11 | return &TimerResource{duration: d} 12 | } 13 | 14 | type TimerResource struct { 15 | distsys.ArchetypeResourceLeafMixin 16 | timer *time.Timer 17 | 18 | duration time.Duration 19 | } 20 | 21 | func (res *TimerResource) Abort() chan struct{} { 22 | return nil 23 | } 24 | 25 | func (res *TimerResource) PreCommit() chan error { 26 | return nil 27 | } 28 | 29 | func (res *TimerResource) Commit() chan struct{} { 30 | return nil 31 | } 32 | 33 | func (res *TimerResource) ReadValue() (tla.Value, error) { 34 | if res.timer == nil { 35 | res.timer = time.NewTimer(res.duration) 36 | return tla.ModuleFALSE, nil 37 | } 38 | select { 39 | case <-res.timer.C: 40 | res.timer.Reset(res.duration) 41 | return tla.ModuleTRUE, nil 42 | default: 43 | return tla.ModuleFALSE, nil 44 | } 45 | } 46 | 47 | func (res *TimerResource) WriteValue(value tla.Value) error { 48 | panic("write to timer resource is not allowed") 49 | } 50 | 51 | func (res *TimerResource) Close() error { 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /systems/pbkvs/Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | JAVA=java 3 | JAVA_OPTS=-XX:+IgnoreUnrecognizedVMOptions -XX:+UseParallelGC 4 | TLA2TOOLS_JAR=../../tools/tla2tools.jar 5 | 6 | TLC_CMD=$(JAVA) $(JAVA_OPTS) -cp $(TLA2TOOLS_JAR) 7 | 8 | TLC=$(TLC_CMD) tlc2.TLC 9 | PCAL=$(TLC_CMD) pcal.trans 10 | SANY=$(TLC_CMD) tla2sany.SANY 11 | 12 | .PHONY: mc tlaplusgen sany 13 | 14 | all: build 15 | 16 | mc: 17 | $(TLC) -config pbkvs.cfg -workers 'auto' -cleanup pbkvs.tla 18 | 19 | tlaplusgen: 20 | $(PCAL) -nocfg pbkvs.tla 21 | 22 | sany: 23 | $(SANY) pbkvs.tla 24 | 25 | build: 26 | $(GO) build -o server cmd/server/*.go 27 | $(GO) build -o client cmd/client/*.go 28 | -------------------------------------------------------------------------------- /systems/pbkvs/README.md: -------------------------------------------------------------------------------- 1 | # pvkvs 2 | 3 | A key-value store that uses primary-backup replication. We used the following 4 | document as the reference: 5 | http://www.sc.ehu.es/acwlaalm/sdi/replication-schemas.pdf 6 | 7 | Assumptions: 8 | - Crash-stop failure model 9 | - Having access to a perfect failure detector 10 | 11 | In each step of the execution, we consider a replica as the primary that has the 12 | lowest ID amongst all alive replicas. Primary synchronously replicates requests 13 | to the backup nodes. Clients must send write requests only to the primary 14 | replica. The primary always has the same state as backups or the primary is one 15 | step ahead. Therefore, reading from a backup might return an old value. In this 16 | spec, read requests are sent only to the primary. 17 | 18 | If the primary node fails while processing a write request, the client won't 19 | receive any response back. If the primary replicated the write request to at 20 | least one backup node, the request will be applied to the system, otherwise, it 21 | won't. Therefore, the system provides no guarantee when a client don't receive a 22 | response for its write request. However, it's fine since the client retries and 23 | all operations are idempotent. 24 | 25 | We define consistency property for this system as: when primary node sends a 26 | response back to a client, all replicas (including primary) have a same state. 27 | -------------------------------------------------------------------------------- /systems/pbkvs/bootstrap/server.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/DistCompiler/pgo/systems/pbkvs" 5 | "github.com/DistCompiler/pgo/systems/pbkvs/configs" 6 | "github.com/UBC-NSS/pgo/distsys" 7 | "github.com/UBC-NSS/pgo/distsys/resources" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | func getReplicaCtx(self tla.Value, c configs.Root) *distsys.MPCalContext { 12 | network := newNetwork(self, c) 13 | networkLen := resources.NewMailboxesLength(network) 14 | constants := makeConstants(c) 15 | 16 | ctx := distsys.NewMPCalContext(self, pbkvs.AReplica, append(constants, 17 | distsys.EnsureArchetypeRefParam("net", network), 18 | distsys.EnsureArchetypeRefParam("fs", resources.NewIncMap(func(index tla.Value) distsys.ArchetypeResource { 19 | if !index.Equal(self) { 20 | panic("wrong index") 21 | } 22 | return resources.NewIncMap(func(index tla.Value) distsys.ArchetypeResource { 23 | return distsys.NewLocalArchetypeResource(tla.MakeString("")) 24 | }) 25 | })), 26 | distsys.EnsureArchetypeRefParam("fd", resources.NewFailureDetector( 27 | func(t tla.Value) string { 28 | return fdAddrMapper(c, t) 29 | }, 30 | resources.WithFailureDetectorPullInterval(c.FD.PullInterval), 31 | resources.WithFailureDetectorTimeout(c.FD.Timeout), 32 | )), 33 | distsys.EnsureArchetypeRefParam("netEnabled", resources.NewPlaceHolder()), 34 | distsys.EnsureArchetypeRefParam("primary", pbkvs.NewLeaderElection()), 35 | distsys.EnsureArchetypeRefParam("netLen", networkLen), 36 | )...) 37 | return ctx 38 | } 39 | 40 | type Replica struct { 41 | Id int 42 | Config configs.Root 43 | 44 | ctx *distsys.MPCalContext 45 | mon *resources.Monitor 46 | } 47 | 48 | func NewReplica(srvId int, c configs.Root) *Replica { 49 | srvIdTLA := tla.MakeNumber(int32(srvId)) 50 | mon := setupMonitor(srvIdTLA, c) 51 | ctx := getReplicaCtx(srvIdTLA, c) 52 | 53 | return &Replica{ 54 | Id: srvId, 55 | Config: c, 56 | ctx: ctx, 57 | mon: mon, 58 | } 59 | } 60 | 61 | func (r *Replica) Run() error { 62 | return r.mon.RunArchetype(r.ctx) 63 | } 64 | 65 | func (r *Replica) Close() error { 66 | r.ctx.Stop() 67 | return r.mon.Close() 68 | } 69 | -------------------------------------------------------------------------------- /systems/pbkvs/cmd/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | 7 | "github.com/DistCompiler/pgo/systems/pbkvs/bootstrap" 8 | "github.com/DistCompiler/pgo/systems/pbkvs/configs" 9 | ) 10 | 11 | func main() { 12 | var clientId int 13 | var configPath string 14 | flag.IntVar(&clientId, "clientId", -1, "Client ID") 15 | flag.StringVar(&configPath, "c", "", "Config file") 16 | 17 | flag.Parse() 18 | if clientId == -1 { 19 | log.Fatal("clientId is not provided or it is invalid") 20 | } 21 | if configPath == "" { 22 | log.Fatal("config file is not provided") 23 | } 24 | 25 | c, err := configs.ReadConfig(configPath) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | client := bootstrap.NewClient(clientId, c) 31 | go func() { 32 | err := client.Run() 33 | if err != nil { 34 | log.Println(err) 35 | } 36 | }() 37 | defer func() { 38 | if err := client.Close(); err != nil { 39 | log.Println(err) 40 | } 41 | }() 42 | 43 | { 44 | resp, err := client.Put("key1", "value1") 45 | if err != nil { 46 | log.Printf("put error: %v", err) 47 | } else { 48 | log.Printf("put resp: %v", resp) 49 | } 50 | } 51 | { 52 | resp, err := client.Get("key1") 53 | if err != nil { 54 | log.Printf("get error: %v", err) 55 | } else { 56 | log.Printf("get resp: %v", resp) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /systems/pbkvs/cmd/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | 10 | "github.com/DistCompiler/pgo/systems/pbkvs/bootstrap" 11 | "github.com/DistCompiler/pgo/systems/pbkvs/configs" 12 | ) 13 | 14 | func main() { 15 | var srvId int 16 | var configPath string 17 | flag.IntVar(&srvId, "srvId", -1, "Server ID") 18 | flag.StringVar(&configPath, "c", "", "Config file") 19 | 20 | flag.Parse() 21 | if srvId == -1 { 22 | log.Fatal("srvId is not provided or it is invalid") 23 | } 24 | if configPath == "" { 25 | log.Fatal("config file is not provided") 26 | } 27 | 28 | c, err := configs.ReadConfig(configPath) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | r := bootstrap.NewReplica(srvId, c) 34 | go func() { 35 | err := r.Run() 36 | if err != nil { 37 | log.Println(err) 38 | } 39 | }() 40 | defer func() { 41 | err := r.Close() 42 | if err != nil { 43 | log.Println(err) 44 | } 45 | }() 46 | 47 | sigCh := make(chan os.Signal, 1) 48 | signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) 49 | 50 | <-sigCh 51 | log.Println("received SIGTERM") 52 | } 53 | -------------------------------------------------------------------------------- /systems/pbkvs/configs/config.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | type Root struct { 11 | NumReplicas int 12 | NumClients int 13 | 14 | Debug bool 15 | 16 | ClientRequestTimeout time.Duration 17 | 18 | FD FD 19 | Mailboxes Mailboxes 20 | 21 | InputChanReadTimeout time.Duration 22 | 23 | Replicas map[int]Replica 24 | Clients map[int]Client 25 | } 26 | 27 | type FD struct { 28 | PullInterval time.Duration 29 | Timeout time.Duration 30 | } 31 | 32 | type Mailboxes struct { 33 | ReceiveChanSize int 34 | DialTimeout time.Duration 35 | ReadTimeout time.Duration 36 | WriteTimeout time.Duration 37 | } 38 | 39 | type Replica struct { 40 | ReqMailboxAddr string 41 | RespMailboxAddr string 42 | MonitorAddr string 43 | } 44 | 45 | type Client struct { 46 | ReqMailboxAddr string 47 | RespMailboxAddr string 48 | } 49 | 50 | func (r Root) Validate() error { 51 | if r.NumReplicas != len(r.Replicas) { 52 | return errors.New("NumReplicas must be equal to the number of Replicas entries") 53 | } 54 | if r.NumClients != len(r.Clients) { 55 | return errors.New("NumClients must be equal to the number of Clients entries") 56 | } 57 | return nil 58 | } 59 | 60 | func ReadConfig(path string) (Root, error) { 61 | viper.SetConfigFile(path) 62 | if err := viper.ReadInConfig(); err != nil { 63 | return Root{}, err 64 | } 65 | var c Root 66 | err := viper.Unmarshal(&c) 67 | if err != nil { 68 | return c, err 69 | } 70 | return c, c.Validate() 71 | } 72 | -------------------------------------------------------------------------------- /systems/pbkvs/configs/local-1-1.yaml: -------------------------------------------------------------------------------- 1 | numReplicas: 1 2 | numClients: 1 3 | 4 | clientRequestTimeout: 1500ms 5 | 6 | fd: 7 | pullInterval: 200ms 8 | timeout: 100ms 9 | 10 | mailboxes: 11 | receiveChanSize: 10000 12 | dialTimeout: 50ms 13 | readTimeout: 50ms 14 | writeTimeout: 50ms 15 | 16 | inputChanReadTimeout: 5ms 17 | 18 | replicas: 19 | 1: 20 | reqMailboxAddr: "localhost:8001" 21 | respMailboxAddr: "localhost:9001" 22 | monitorAddr: "localhost:10001" 23 | 24 | clients: 25 | 1: 26 | reqMailboxAddr: "localhost:6001" 27 | respMailboxAddr: "localhost:7003" -------------------------------------------------------------------------------- /systems/pbkvs/configs/local-3-3.yaml: -------------------------------------------------------------------------------- 1 | numReplicas: 3 2 | numClients: 3 3 | 4 | debug: true 5 | 6 | clientRequestTimeout: 2s 7 | 8 | fd: 9 | pullInterval: 200ms 10 | timeout: 100ms 11 | 12 | mailboxes: 13 | receiveChanSize: 10000 14 | dialTimeout: 50ms 15 | readTimeout: 50ms 16 | writeTimeout: 50ms 17 | 18 | inputChanReadTimeout: 5ms 19 | 20 | replicas: 21 | 1: 22 | reqMailboxAddr: "localhost:8001" 23 | respMailboxAddr: "localhost:9001" 24 | monitorAddr: "localhost:10001" 25 | 26 | 2: 27 | reqMailboxAddr: "localhost:8002" 28 | respMailboxAddr: "localhost:9002" 29 | monitorAddr: "localhost:10002" 30 | 31 | 3: 32 | reqMailboxAddr: "localhost:8003" 33 | respMailboxAddr: "localhost:9003" 34 | monitorAddr: "localhost:10003" 35 | 36 | clients: 37 | 1: 38 | reqMailboxAddr: "localhost:6001" 39 | respMailboxAddr: "localhost:7001" 40 | 41 | 2: 42 | reqMailboxAddr: "localhost:6002" 43 | respMailboxAddr: "localhost:7002" 44 | 45 | 3: 46 | reqMailboxAddr: "localhost:6003" 47 | respMailboxAddr: "localhost:7003" -------------------------------------------------------------------------------- /systems/pbkvs/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/pbkvs 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require ( 10 | github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 11 | github.com/spf13/viper v1.15.0 12 | ) 13 | 14 | require ( 15 | github.com/benbjohnson/immutable v0.4.3 // indirect 16 | github.com/cespare/xxhash v1.1.0 // indirect 17 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 18 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 19 | github.com/dgraph-io/ristretto v0.1.1 // indirect 20 | github.com/dustin/go-humanize v1.0.1 // indirect 21 | github.com/fsnotify/fsnotify v1.6.0 // indirect 22 | github.com/gogo/protobuf v1.3.2 // indirect 23 | github.com/golang/glog v1.0.0 // indirect 24 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 25 | github.com/golang/protobuf v1.5.2 // indirect 26 | github.com/golang/snappy v0.0.4 // indirect 27 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 28 | github.com/hashicorp/hcl v1.0.0 // indirect 29 | github.com/klauspost/compress v1.15.15 // indirect 30 | github.com/magiconair/properties v1.8.7 // indirect 31 | github.com/mitchellh/mapstructure v1.5.0 // indirect 32 | github.com/pelletier/go-toml/v2 v2.0.6 // indirect 33 | github.com/pkg/errors v0.9.1 // indirect 34 | github.com/segmentio/fasthash v1.0.3 // indirect 35 | github.com/spf13/afero v1.9.3 // indirect 36 | github.com/spf13/cast v1.5.0 // indirect 37 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 38 | github.com/spf13/pflag v1.0.5 // indirect 39 | github.com/subosito/gotenv v1.4.2 // indirect 40 | go.opencensus.io v0.24.0 // indirect 41 | go.uber.org/multierr v1.11.0 // indirect 42 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 43 | golang.org/x/net v0.7.0 // indirect 44 | golang.org/x/sync v0.9.0 // indirect 45 | golang.org/x/sys v0.5.0 // indirect 46 | golang.org/x/text v0.7.0 // indirect 47 | google.golang.org/protobuf v1.28.1 // indirect 48 | gopkg.in/ini.v1 v1.67.0 // indirect 49 | gopkg.in/yaml.v3 v3.0.1 // indirect 50 | ) 51 | -------------------------------------------------------------------------------- /systems/pbkvs/leaderelection.go: -------------------------------------------------------------------------------- 1 | package pbkvs 2 | 3 | import ( 4 | "github.com/UBC-NSS/pgo/distsys" 5 | "github.com/UBC-NSS/pgo/distsys/tla" 6 | ) 7 | 8 | // TODO 9 | 10 | type LeaderElection struct { 11 | distsys.ArchetypeResourceLeafMixin 12 | } 13 | 14 | func NewLeaderElection() *LeaderElection { 15 | return &LeaderElection{} 16 | } 17 | 18 | func (res *LeaderElection) Abort() chan struct{} { 19 | return nil 20 | } 21 | 22 | func (res *LeaderElection) PreCommit() chan error { 23 | return nil 24 | } 25 | 26 | func (res *LeaderElection) Commit() chan struct{} { 27 | return nil 28 | } 29 | 30 | func (res *LeaderElection) ReadValue() (tla.Value, error) { 31 | return tla.MakeNumber(1), nil 32 | } 33 | 34 | func (res *LeaderElection) WriteValue(value tla.Value) error { 35 | panic("no write allowed") 36 | } 37 | 38 | func (res *LeaderElection) Close() error { 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /systems/pbkvs/pbkvs.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT NUM_REPLICAS = 3 5 | CONSTANT NUM_CLIENTS = 2 6 | 7 | CONSTANT DEBUG = FALSE 8 | 9 | CONSTANT EXPLORE_FAIL = TRUE 10 | 11 | \* CONSTRAINT definition 12 | CONSTRAINT VersionNumberCnst 13 | 14 | \* SPECIFICATION definition 15 | SPECIFICATION Spec 16 | 17 | \* INVARIANT definition 18 | INVARIANT ConsistencyOK 19 | 20 | \* PROPERTY definition 21 | \* PROPERTY ClientsOK 22 | 23 | \* CHECK_DEADLOCK setting 24 | CHECK_DEADLOCK FALSE -------------------------------------------------------------------------------- /systems/proxy/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/proxy 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.1.1 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/glog v1.0.0 // indirect 20 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 21 | github.com/golang/protobuf v1.5.2 // indirect 22 | github.com/golang/snappy v0.0.4 // indirect 23 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 24 | github.com/klauspost/compress v1.15.15 // indirect 25 | github.com/pkg/errors v0.9.1 // indirect 26 | github.com/segmentio/fasthash v1.0.3 // indirect 27 | go.opencensus.io v0.24.0 // indirect 28 | go.uber.org/multierr v1.11.0 // indirect 29 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 30 | golang.org/x/net v0.7.0 // indirect 31 | golang.org/x/sync v0.9.0 // indirect 32 | golang.org/x/sys v0.5.0 // indirect 33 | google.golang.org/protobuf v1.28.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /systems/raftkvs/Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | JAVA=java 3 | JAVA_OPTS=-XX:+IgnoreUnrecognizedVMOptions -XX:+UseParallelGC 4 | TLA2TOOLS_JAR=../../tools/tla2tools.jar 5 | 6 | TLC_CMD=$(JAVA) $(JAVA_OPTS) -cp $(TLA2TOOLS_JAR) 7 | 8 | TLC=$(TLC_CMD) tlc2.TLC 9 | PCAL=$(TLC_CMD) pcal.trans 10 | SANY=$(TLC_CMD) tla2sany.SANY 11 | 12 | .PHONY: sim mc tlaplusgen sany test racetest build 13 | 14 | all: build 15 | 16 | sim: 17 | $(TLC) -config models/SIM.cfg -depth '1000' -simulate num=100 -workers 'auto' -cleanup raftkvs.tla 18 | 19 | mc: 20 | $(TLC) -config models/MC.cfg -workers 'auto' -cleanup raftkvs.tla 21 | 22 | tlaplusgen: 23 | $(PCAL) -nocfg raftkvs.tla 24 | 25 | sany: 26 | $(SANY) raftkvs.tla 27 | 28 | test: 29 | $(GO) test -v 30 | 31 | racetest: 32 | $(GO) test -race -v 33 | 34 | build: 35 | $(GO) build -o server cmd/server/*.go 36 | $(GO) build -o client cmd/client/*.go 37 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/azure-3.yaml: -------------------------------------------------------------------------------- 1 | numServers: 3 2 | numClients: 8 3 | 4 | debug: true 5 | 6 | clientRequestTimeout: 1s 7 | 8 | fd: 9 | pullInterval: 200ms 10 | timeout: 100ms 11 | 12 | mailboxes: 13 | receiveChanSize: 10000 14 | dialTimeout: 50ms 15 | readTimeout: 50ms 16 | writeTimeout: 50ms 17 | 18 | leaderElection: 19 | timeout: 150ms 20 | timeoutOffset: 150ms 21 | 22 | appendEntriesSendInterval: 2ms 23 | 24 | sharedResourceTimeout: 3ms 25 | 26 | inputChanReadTimeout: 5ms 27 | 28 | servers: 29 | 1: 30 | mailboxAddr: "kv1:8000" 31 | monitorAddr: "kv1:9000" 32 | 33 | 2: 34 | mailboxAddr: "kv2:8000" 35 | monitorAddr: "kv2:9000" 36 | 37 | 3: 38 | mailboxAddr: "kv3:8000" 39 | monitorAddr: "kv3:9000" 40 | 41 | clients: 42 | 1: 43 | mailboxAddr: "client:8001" 44 | 45 | 2: 46 | mailboxAddr: "client:8002" 47 | 48 | 3: 49 | mailboxAddr: "client:8003" 50 | 51 | 4: 52 | mailboxAddr: "client:8004" 53 | 54 | 5: 55 | mailboxAddr: "client:8005" 56 | 57 | 6: 58 | mailboxAddr: "client:8006" 59 | 60 | 7: 61 | mailboxAddr: "client:8007" 62 | 63 | 8: 64 | mailboxAddr: "client:8008" 65 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/config.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | type Root struct { 11 | NumServers int 12 | NumClients int 13 | 14 | Debug bool 15 | 16 | Persist bool 17 | 18 | ClientRequestTimeout time.Duration 19 | 20 | FD FD 21 | Mailboxes Mailboxes 22 | LeaderElection LeaderElection 23 | 24 | AppendEntriesSendInterval time.Duration 25 | SharedResourceTimeout time.Duration 26 | InputChanReadTimeout time.Duration 27 | 28 | Servers map[int]Server 29 | Clients map[int]Client 30 | } 31 | 32 | type FD struct { 33 | PullInterval time.Duration 34 | Timeout time.Duration 35 | } 36 | 37 | type Mailboxes struct { 38 | ReceiveChanSize int 39 | DialTimeout time.Duration 40 | ReadTimeout time.Duration 41 | WriteTimeout time.Duration 42 | } 43 | 44 | type LeaderElection struct { 45 | Timeout time.Duration 46 | TimeoutOffset time.Duration 47 | } 48 | 49 | type Server struct { 50 | MailboxAddr string 51 | MonitorAddr string 52 | } 53 | 54 | type Client struct { 55 | MailboxAddr string 56 | } 57 | 58 | func (r Root) Validate() error { 59 | if r.NumServers != len(r.Servers) { 60 | return errors.New("NumServers must be equal to the number of Servers entries") 61 | } 62 | if r.NumClients != len(r.Clients) { 63 | return errors.New("NumClients must be equal to the number of Clients entries") 64 | } 65 | return nil 66 | } 67 | 68 | func ReadConfig(path string) (Root, error) { 69 | viper.SetConfigFile(path) 70 | if err := viper.ReadInConfig(); err != nil { 71 | return Root{}, err 72 | } 73 | var c Root 74 | err := viper.Unmarshal(&c) 75 | if err != nil { 76 | return c, err 77 | } 78 | return c, c.Validate() 79 | } 80 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/local-1.yaml: -------------------------------------------------------------------------------- 1 | numServers: 1 2 | numClients: 1 3 | 4 | debug: true 5 | 6 | persist: false 7 | 8 | clientRequestTimeout: 1000ms 9 | 10 | fd: 11 | pullInterval: 200ms 12 | timeout: 100ms 13 | 14 | mailboxes: 15 | dialTimeout: 50ms 16 | readTimeout: 50ms 17 | writeTimeout: 50ms 18 | 19 | leaderElection: 20 | timeout: 150ms 21 | timeoutOffset: 150ms 22 | 23 | appendEntriesSendInterval: 10ms 24 | 25 | sharedResourceTimeout: 2ms 26 | 27 | inputChanReadTimeout: 2ms 28 | 29 | servers: 30 | 1: 31 | mailboxAddr: "localhost:8001" 32 | monitorAddr: "localhost:9001" 33 | 34 | clients: 35 | 1: 36 | mailboxAddr: "localhost:8019" 37 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/local-3.yaml: -------------------------------------------------------------------------------- 1 | numServers: 3 2 | numClients: 8 3 | 4 | debug: true 5 | 6 | persist: true 7 | 8 | clientRequestTimeout: 1500ms 9 | 10 | fd: 11 | pullInterval: 200ms 12 | timeout: 100ms 13 | 14 | mailboxes: 15 | receiveChanSize: 10000 16 | dialTimeout: 50ms 17 | readTimeout: 50ms 18 | writeTimeout: 50ms 19 | 20 | leaderElection: 21 | timeout: 150ms 22 | timeoutOffset: 150ms 23 | 24 | appendEntriesSendInterval: 2ms 25 | 26 | sharedResourceTimeout: 3ms 27 | 28 | inputChanReadTimeout: 5ms 29 | 30 | servers: 31 | 1: 32 | mailboxAddr: "localhost:8001" 33 | monitorAddr: "localhost:9001" 34 | 35 | 2: 36 | mailboxAddr: "localhost:8002" 37 | monitorAddr: "localhost:9002" 38 | 39 | 3: 40 | mailboxAddr: "localhost:8003" 41 | monitorAddr: "localhost:9003" 42 | 43 | clients: 44 | 1: 45 | mailboxAddr: "localhost:8019" 46 | 47 | 2: 48 | mailboxAddr: "localhost:8020" 49 | 50 | 3: 51 | mailboxAddr: "localhost:8021" 52 | 53 | 4: 54 | mailboxAddr: "localhost:8022" 55 | 56 | 5: 57 | mailboxAddr: "localhost:8023" 58 | 59 | 6: 60 | mailboxAddr: "localhost:8024" 61 | 62 | 7: 63 | mailboxAddr: "localhost:8025" 64 | 65 | 8: 66 | mailboxAddr: "localhost:8026" 67 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/test-1-1.yaml: -------------------------------------------------------------------------------- 1 | numServers: 1 2 | numClients: 1 3 | 4 | debug: true 5 | 6 | persist: false 7 | 8 | clientRequestTimeout: 1000ms 9 | 10 | fd: 11 | pullInterval: 200ms 12 | timeout: 100ms 13 | 14 | mailboxes: 15 | dialTimeout: 50ms 16 | readTimeout: 50ms 17 | writeTimeout: 50ms 18 | 19 | leaderElection: 20 | timeout: 150ms 21 | timeoutOffset: 150ms 22 | 23 | appendEntriesSendInterval: 10ms 24 | 25 | sharedResourceTimeout: 2ms 26 | 27 | inputChanReadTimeout: 2ms 28 | 29 | servers: 30 | 1: 31 | mailboxAddr: "localhost:8001" 32 | monitorAddr: "localhost:9001" 33 | 34 | clients: 35 | 1: 36 | mailboxAddr: "localhost:8019" 37 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/test-2-1.yaml: -------------------------------------------------------------------------------- 1 | numServers: 2 2 | numClients: 1 3 | 4 | debug: true 5 | 6 | persist: false 7 | 8 | clientRequestTimeout: 1000ms 9 | 10 | fd: 11 | pullInterval: 300ms 12 | timeout: 150ms 13 | 14 | mailboxes: 15 | receiveChanSize: 10000 16 | dialTimeout: 100ms 17 | readTimeout: 100ms 18 | writeTimeout: 100ms 19 | 20 | leaderElection: 21 | timeout: 150ms 22 | timeoutOffset: 150ms 23 | 24 | appendEntriesSendInterval: 5ms 25 | 26 | sharedResourceTimeout: 2ms 27 | 28 | inputChanReadTimeout: 2ms 29 | 30 | servers: 31 | 1: 32 | mailboxAddr: "localhost:8001" 33 | monitorAddr: "localhost:9001" 34 | 35 | 2: 36 | mailboxAddr: "localhost:8002" 37 | monitorAddr: "localhost:9002" 38 | 39 | clients: 40 | 1: 41 | mailboxAddr: "localhost:8019" 42 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/test-3-1.yaml: -------------------------------------------------------------------------------- 1 | numServers: 3 2 | numClients: 1 3 | 4 | debug: true 5 | 6 | persist: false 7 | 8 | clientRequestTimeout: 2000ms 9 | 10 | fd: 11 | pullInterval: 500ms 12 | timeout: 500ms 13 | 14 | mailboxes: 15 | receiveChanSize: 10000 16 | dialTimeout: 500ms 17 | readTimeout: 500ms 18 | writeTimeout: 500ms 19 | 20 | leaderElection: 21 | timeout: 500ms 22 | timeoutOffset: 500ms 23 | 24 | appendEntriesSendInterval: 50ms 25 | 26 | sharedResourceTimeout: 2ms 27 | 28 | inputChanReadTimeout: 2ms 29 | 30 | servers: 31 | 1: 32 | mailboxAddr: "localhost:8001" 33 | monitorAddr: "localhost:9001" 34 | 35 | 2: 36 | mailboxAddr: "localhost:8002" 37 | monitorAddr: "localhost:9002" 38 | 39 | 3: 40 | mailboxAddr: "localhost:8003" 41 | monitorAddr: "localhost:9003" 42 | 43 | 44 | clients: 45 | 1: 46 | mailboxAddr: "localhost:8019" 47 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/test-3-3.yaml: -------------------------------------------------------------------------------- 1 | numServers: 3 2 | numClients: 3 3 | 4 | debug: true 5 | 6 | persist: true 7 | 8 | clientRequestTimeout: 1500ms 9 | 10 | fd: 11 | pullInterval: 300ms 12 | timeout: 150ms 13 | 14 | mailboxes: 15 | receiveChanSize: 10000 16 | dialTimeout: 100ms 17 | readTimeout: 100ms 18 | writeTimeout: 100ms 19 | 20 | leaderElection: 21 | timeout: 150ms 22 | timeoutOffset: 150ms 23 | 24 | appendEntriesSendInterval: 5ms 25 | 26 | sharedResourceTimeout: 3ms 27 | 28 | inputChanReadTimeout: 5ms 29 | 30 | servers: 31 | 1: 32 | mailboxAddr: "localhost:8001" 33 | monitorAddr: "localhost:9001" 34 | 35 | 2: 36 | mailboxAddr: "localhost:8002" 37 | monitorAddr: "localhost:9002" 38 | 39 | 3: 40 | mailboxAddr: "localhost:8003" 41 | monitorAddr: "localhost:9003" 42 | 43 | clients: 44 | 1: 45 | mailboxAddr: "localhost:8019" 46 | 47 | 2: 48 | mailboxAddr: "localhost:8020" 49 | 50 | 3: 51 | mailboxAddr: "localhost:8021" 52 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/test-3-5.yaml: -------------------------------------------------------------------------------- 1 | numServers: 3 2 | numClients: 5 3 | 4 | debug: true 5 | 6 | persist: true 7 | 8 | clientRequestTimeout: 1500ms 9 | 10 | fd: 11 | pullInterval: 300ms 12 | timeout: 150ms 13 | 14 | mailboxes: 15 | receiveChanSize: 10000 16 | dialTimeout: 100ms 17 | readTimeout: 100ms 18 | writeTimeout: 100ms 19 | 20 | leaderElection: 21 | timeout: 150ms 22 | timeoutOffset: 150ms 23 | 24 | appendEntriesSendInterval: 5ms 25 | 26 | sharedResourceTimeout: 3ms 27 | 28 | inputChanReadTimeout: 5ms 29 | 30 | servers: 31 | 1: 32 | mailboxAddr: "localhost:8001" 33 | monitorAddr: "localhost:9001" 34 | 35 | 2: 36 | mailboxAddr: "localhost:8002" 37 | monitorAddr: "localhost:9002" 38 | 39 | 3: 40 | mailboxAddr: "localhost:8003" 41 | monitorAddr: "localhost:9003" 42 | 43 | clients: 44 | 1: 45 | mailboxAddr: "localhost:8019" 46 | 47 | 2: 48 | mailboxAddr: "localhost:8020" 49 | 50 | 3: 51 | mailboxAddr: "localhost:8021" 52 | 53 | 4: 54 | mailboxAddr: "localhost:8022" 55 | 56 | 5: 57 | mailboxAddr: "localhost:8023" 58 | -------------------------------------------------------------------------------- /systems/raftkvs/configs/test-5-1.yaml: -------------------------------------------------------------------------------- 1 | numServers: 5 2 | numClients: 1 3 | 4 | debug: true 5 | 6 | persist: false 7 | 8 | clientRequestTimeout: 1000ms 9 | 10 | fd: 11 | pullInterval: 300ms 12 | timeout: 150ms 13 | 14 | mailboxes: 15 | receiveChanSize: 10000 16 | dialTimeout: 100ms 17 | readTimeout: 100ms 18 | writeTimeout: 100ms 19 | 20 | leaderElection: 21 | timeout: 150ms 22 | timeoutOffset: 150ms 23 | 24 | appendEntriesSendInterval: 5ms 25 | 26 | sharedResourceTimeout: 2ms 27 | 28 | inputChanReadTimeout: 2ms 29 | 30 | servers: 31 | 1: 32 | mailboxAddr: "localhost:8001" 33 | monitorAddr: "localhost:9001" 34 | 35 | 2: 36 | mailboxAddr: "localhost:8002" 37 | monitorAddr: "localhost:9002" 38 | 39 | 3: 40 | mailboxAddr: "localhost:8003" 41 | monitorAddr: "localhost:9003" 42 | 43 | 4: 44 | mailboxAddr: "localhost:8004" 45 | monitorAddr: "localhost:9004" 46 | 47 | 5: 48 | mailboxAddr: "localhost:8005" 49 | monitorAddr: "localhost:9005" 50 | 51 | clients: 52 | 1: 53 | mailboxAddr: "localhost:8019" 54 | -------------------------------------------------------------------------------- /systems/raftkvs/customch.go: -------------------------------------------------------------------------------- 1 | package raftkvs 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/UBC-NSS/pgo/distsys" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | // CustomInChan is similar resources.InputChannel, however, after a timeout it 12 | // returns a default value instead of aborting the critical section. It used in 13 | // implementing periodic sending of AppendEntries request. In some cases, the 14 | // request should be sent immediately, for example, when the server just becomes 15 | // a leader. In this case, the input channel signals. 16 | type CustomInChan struct { 17 | distsys.ArchetypeResourceLeafMixin 18 | channel <-chan tla.Value 19 | buffer, backlogBuffer []tla.Value 20 | timeout time.Duration 21 | } 22 | 23 | var _ distsys.ArchetypeResource = &CustomInChan{} 24 | 25 | func NewCustomInChan(ch <-chan tla.Value, timeout time.Duration) distsys.ArchetypeResource { 26 | return &CustomInChan{ 27 | channel: ch, 28 | timeout: timeout, 29 | } 30 | } 31 | 32 | func (res *CustomInChan) Abort() chan struct{} { 33 | res.buffer = append(res.backlogBuffer, res.buffer...) 34 | res.backlogBuffer = nil 35 | return nil 36 | } 37 | 38 | func (res *CustomInChan) PreCommit() chan error { 39 | return nil 40 | } 41 | 42 | func (res *CustomInChan) Commit() chan struct{} { 43 | res.backlogBuffer = nil 44 | return nil 45 | } 46 | 47 | func (res *CustomInChan) ReadValue() (tla.Value, error) { 48 | if len(res.buffer) > 0 { 49 | value := res.buffer[0] 50 | res.buffer = res.buffer[1:] 51 | res.backlogBuffer = append(res.backlogBuffer, value) 52 | return value, nil 53 | } 54 | 55 | select { 56 | case value := <-res.channel: 57 | res.backlogBuffer = append(res.backlogBuffer, value) 58 | return value, nil 59 | case <-time.After(res.timeout): 60 | return tla.ModuleTRUE, nil 61 | } 62 | } 63 | 64 | func (res *CustomInChan) WriteValue(value tla.Value) error { 65 | panic(fmt.Errorf("attempted to write %v to an input channel resource", value)) 66 | } 67 | 68 | func (res *CustomInChan) Close() error { 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /systems/raftkvs/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/raftkvs 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require ( 10 | github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 11 | github.com/benbjohnson/immutable v0.4.3 12 | github.com/dgraph-io/badger/v3 v3.2103.5 13 | github.com/pkg/profile v1.7.0 14 | github.com/spf13/viper v1.15.0 15 | go.uber.org/multierr v1.11.0 16 | ) 17 | 18 | require ( 19 | github.com/cespare/xxhash v1.1.0 // indirect 20 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 21 | github.com/dgraph-io/ristretto v0.2.0 // indirect 22 | github.com/dustin/go-humanize v1.0.1 // indirect 23 | github.com/felixge/fgprof v0.9.3 // indirect 24 | github.com/fsnotify/fsnotify v1.6.0 // indirect 25 | github.com/gogo/protobuf v1.3.2 // indirect 26 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 27 | github.com/golang/protobuf v1.5.4 // indirect 28 | github.com/golang/snappy v0.0.4 // indirect 29 | github.com/google/flatbuffers v24.3.25+incompatible // indirect 30 | github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect 31 | github.com/hashicorp/hcl v1.0.0 // indirect 32 | github.com/klauspost/compress v1.17.11 // indirect 33 | github.com/magiconair/properties v1.8.7 // indirect 34 | github.com/mitchellh/mapstructure v1.5.0 // indirect 35 | github.com/pelletier/go-toml/v2 v2.0.6 // indirect 36 | github.com/pkg/errors v0.9.1 // indirect 37 | github.com/segmentio/fasthash v1.0.3 // indirect 38 | github.com/spf13/afero v1.9.3 // indirect 39 | github.com/spf13/cast v1.5.0 // indirect 40 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 41 | github.com/spf13/pflag v1.0.5 // indirect 42 | github.com/subosito/gotenv v1.4.2 // indirect 43 | go.opencensus.io v0.24.0 // indirect 44 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 45 | golang.org/x/net v0.31.0 // indirect 46 | golang.org/x/sync v0.9.0 // indirect 47 | golang.org/x/sys v0.27.0 // indirect 48 | golang.org/x/text v0.20.0 // indirect 49 | google.golang.org/protobuf v1.35.2 // indirect 50 | gopkg.in/ini.v1 v1.67.0 // indirect 51 | gopkg.in/yaml.v3 v3.0.1 // indirect 52 | ) 53 | -------------------------------------------------------------------------------- /systems/raftkvs/models/MC.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT ExploreFail = TRUE 5 | CONSTANT Debug = FALSE 6 | 7 | CONSTANT NumServers = 3 8 | CONSTANT NumClients = 1 9 | 10 | CONSTANT BufferSize = 3 11 | 12 | CONSTANT MaxTerm = 3 13 | CONSTANT MaxCommitIndex = 2 14 | 15 | CONSTANT MaxNodeFail = 1 16 | 17 | CONSTANT LogConcat = 2 18 | CONSTANT LogPop = 1 19 | 20 | CONSTANT LeaderTimeoutReset = TRUE 21 | 22 | CONSTANT NumRequests = 1 23 | 24 | \* CONSTRAINT definition 25 | CONSTRAINT MCConstraint 26 | 27 | \* SPECIFICATION definition 28 | SPECIFICATION Spec 29 | 30 | \* INVARIANT definition 31 | INVARIANT ElectionSafety 32 | INVARIANT LogMatching 33 | INVARIANT LeaderCompleteness 34 | INVARIANT StateMachineSafety 35 | INVARIANT ApplyLogOK 36 | INVARIANT plogOK 37 | INVARIANT TermOK 38 | INVARIANT CommitIndexOK 39 | INVARIANT NodeFailOK 40 | 41 | \* PROPERTY definition 42 | PROPERTY LeaderAppendOnly 43 | -------------------------------------------------------------------------------- /systems/raftkvs/models/SIM.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT ExploreFail = TRUE 5 | CONSTANT Debug = FALSE 6 | 7 | CONSTANT NumServers = 3 8 | CONSTANT NumClients = 3 9 | 10 | CONSTANT BufferSize = 10 11 | 12 | CONSTANT MaxTerm = 2 13 | CONSTANT MaxCommitIndex = 2 14 | 15 | CONSTANT MaxNodeFail = 1 16 | 17 | CONSTANT LogConcat = 2 18 | CONSTANT LogPop = 1 19 | 20 | CONSTANT LeaderTimeoutReset = TRUE 21 | 22 | CONSTANT NumRequests = 3 23 | 24 | \* CONSTRAINT definition 25 | CONSTRAINT LimitNodeFailure 26 | 27 | \* SPECIFICATION definition 28 | SPECIFICATION Spec 29 | 30 | \* INVARIANT definition 31 | INVARIANT ElectionSafety 32 | INVARIANT LogMatching 33 | INVARIANT LeaderCompleteness 34 | INVARIANT StateMachineSafety 35 | INVARIANT ApplyLogOK 36 | INVARIANT plogOK 37 | INVARIANT NodeFailOK 38 | 39 | \* PROPERTY definition 40 | PROPERTY LeaderAppendOnly 41 | PROPERTY ElectionLiveness 42 | PROPERTY ClientsOK 43 | -------------------------------------------------------------------------------- /systems/raftres/Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | 3 | all: build 4 | 5 | test: 6 | $(GO) test -v 7 | 8 | racetest: 9 | $(GO) test -race -v 10 | 11 | build: 12 | $(GO) build -o server cmd/server/*.go 13 | -------------------------------------------------------------------------------- /systems/raftres/README.md: -------------------------------------------------------------------------------- 1 | # raftres 2 | 3 | raftres is a modular composition of the pure Raft protocol and a naive 4 | distributed key-value store. This system demonstrates the modular verification 5 | and composition of PGo-based systems. 6 | 7 | ## Model 8 | 9 | raftres is composed of two models: `raft` and `kv`. `raft` is a pure Raft 10 | protocol without any client interaction semantics. `kv` is a distributed 11 | key-value store that assumes it has access on an abstract consensus layer. 12 | 13 | In the composition, `raft` and `kv` communicate using accept and propose 14 | channels. Each `raft` server accepts new request through its accept channel and 15 | broadcasts committed log entries through its propose channel. `kv` leverages these 16 | channels and implements a key-value store using them. 17 | 18 | ### Assumptions 19 | 20 | Both `raft` and `kv` make the same assumptions as the [raftkvs](/systems/raftkvs/) systems. 21 | 22 | ### Properties 23 | 24 | `raft` only has the five properties from the Raft protocol. -------------------------------------------------------------------------------- /systems/raftres/cmd/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | 10 | "github.com/DistCompiler/pgo/systems/raftres" 11 | "github.com/DistCompiler/pgo/systems/raftres/configs" 12 | ) 13 | 14 | func main() { 15 | var srvId int 16 | var configPath string 17 | flag.IntVar(&srvId, "srvId", -1, "Server ID") 18 | flag.StringVar(&configPath, "c", "", "Config file") 19 | 20 | flag.Parse() 21 | 22 | c, err := configs.ReadConfig(configPath) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | s := raftres.NewServer(srvId, c) 28 | 29 | go func() { 30 | err := s.Run() 31 | if err != nil { 32 | log.Println(err) 33 | } 34 | }() 35 | 36 | defer func() { 37 | err := s.Close() 38 | if err != nil { 39 | log.Println(err) 40 | } 41 | }() 42 | 43 | sigCh := make(chan os.Signal) 44 | signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) 45 | select { 46 | case <-sigCh: 47 | log.Println("received SIGTERM") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /systems/raftres/configs/config.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | type Root struct { 11 | NumServers int 12 | NumClients int 13 | 14 | Debug bool 15 | 16 | ClientRequestTimeout time.Duration 17 | 18 | FD FD 19 | Mailboxes Mailboxes 20 | LeaderElection LeaderElection 21 | 22 | AppendEntriesSendInterval time.Duration 23 | SharedResourceTimeout time.Duration 24 | InputChanReadTimeout time.Duration 25 | 26 | Servers map[int]Server 27 | Clients map[int]Client 28 | } 29 | 30 | type FD struct { 31 | PullInterval time.Duration 32 | Timeout time.Duration 33 | } 34 | 35 | type Mailboxes struct { 36 | ReceiveChanSize int 37 | DialTimeout time.Duration 38 | ReadTimeout time.Duration 39 | WriteTimeout time.Duration 40 | } 41 | 42 | type LeaderElection struct { 43 | Timeout time.Duration 44 | TimeoutOffset time.Duration 45 | } 46 | 47 | type Server struct { 48 | RaftAddr string 49 | KVAddr string 50 | MonitorAddr string 51 | } 52 | 53 | type Client struct { 54 | MailboxAddr string 55 | } 56 | 57 | func (r Root) Validate() error { 58 | if r.NumServers != len(r.Servers) { 59 | return errors.New("NumServers must be equal to the number of Servers entries") 60 | } 61 | if r.NumClients != len(r.Clients) { 62 | return errors.New("NumClients must be equal to the number of Clients entries") 63 | } 64 | return nil 65 | } 66 | 67 | func ReadConfig(path string) (Root, error) { 68 | viper.SetConfigFile(path) 69 | if err := viper.ReadInConfig(); err != nil { 70 | return Root{}, err 71 | } 72 | var c Root 73 | err := viper.Unmarshal(&c) 74 | if err != nil { 75 | return c, err 76 | } 77 | return c, c.Validate() 78 | } 79 | -------------------------------------------------------------------------------- /systems/raftres/configs/local-3.yaml: -------------------------------------------------------------------------------- 1 | numServers: 3 2 | numClients: 4 3 | 4 | debug: true 5 | 6 | clientRequestTimeout: 5s 7 | 8 | fd: 9 | pullInterval: 200ms 10 | timeout: 100ms 11 | 12 | mailboxes: 13 | receiveChanSize: 10000 14 | dialTimeout: 50ms 15 | readTimeout: 50ms 16 | writeTimeout: 50ms 17 | 18 | leaderElection: 19 | timeout: 150ms 20 | timeoutOffset: 150ms 21 | 22 | appendEntriesSendInterval: 2ms 23 | 24 | sharedResourceTimeout: 1ms 25 | 26 | inputChanReadTimeout: 10ms 27 | 28 | servers: 29 | 1: 30 | raftAddr: "localhost:10001" 31 | kvAddr: "localhost:8001" 32 | monitorAddr: "localhost:9001" 33 | 34 | 2: 35 | raftAddr: "localhost:10002" 36 | kvAddr: "localhost:8002" 37 | monitorAddr: "localhost:9002" 38 | 39 | 3: 40 | raftAddr: "localhost:10003" 41 | kvAddr: "localhost:8003" 42 | monitorAddr: "localhost:9003" 43 | 44 | clients: 45 | 1: 46 | mailboxAddr: "localhost:8019" 47 | 48 | 2: 49 | mailboxAddr: "localhost:8020" 50 | 51 | 3: 52 | mailboxAddr: "localhost:8021" 53 | 54 | 4: 55 | mailboxAddr: "localhost:8022" 56 | 57 | # 5: 58 | # mailboxAddr: "localhost:8023" 59 | 60 | # 6: 61 | # mailboxAddr: "localhost:8024" 62 | 63 | # 7: 64 | # mailboxAddr: "localhost:8025" 65 | 66 | # 8: 67 | # mailboxAddr: "localhost:8026" 68 | -------------------------------------------------------------------------------- /systems/raftres/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/raftres 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require ( 10 | github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 11 | github.com/benbjohnson/immutable v0.4.3 12 | github.com/dgraph-io/badger/v3 v3.2103.5 13 | github.com/spf13/viper v1.19.0 14 | go.uber.org/multierr v1.11.0 15 | ) 16 | 17 | require ( 18 | github.com/cespare/xxhash v1.1.0 // indirect 19 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 20 | github.com/dgraph-io/ristretto v0.2.0 // indirect 21 | github.com/dustin/go-humanize v1.0.1 // indirect 22 | github.com/fsnotify/fsnotify v1.8.0 // indirect 23 | github.com/gogo/protobuf v1.3.2 // indirect 24 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 25 | github.com/golang/protobuf v1.5.4 // indirect 26 | github.com/golang/snappy v0.0.4 // indirect 27 | github.com/google/flatbuffers v24.3.25+incompatible // indirect 28 | github.com/hashicorp/hcl v1.0.0 // indirect 29 | github.com/klauspost/compress v1.17.11 // indirect 30 | github.com/magiconair/properties v1.8.7 // indirect 31 | github.com/mitchellh/mapstructure v1.5.0 // indirect 32 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 33 | github.com/pkg/errors v0.9.1 // indirect 34 | github.com/sagikazarmark/locafero v0.6.0 // indirect 35 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 36 | github.com/segmentio/fasthash v1.0.3 // indirect 37 | github.com/sourcegraph/conc v0.3.0 // indirect 38 | github.com/spf13/afero v1.11.0 // indirect 39 | github.com/spf13/cast v1.7.0 // indirect 40 | github.com/spf13/pflag v1.0.5 // indirect 41 | github.com/subosito/gotenv v1.6.0 // indirect 42 | go.opencensus.io v0.24.0 // indirect 43 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 44 | golang.org/x/net v0.31.0 // indirect 45 | golang.org/x/sync v0.9.0 // indirect 46 | golang.org/x/sys v0.27.0 // indirect 47 | golang.org/x/text v0.20.0 // indirect 48 | google.golang.org/protobuf v1.35.2 // indirect 49 | gopkg.in/ini.v1 v1.67.0 // indirect 50 | gopkg.in/yaml.v3 v3.0.1 // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /systems/raftres/kv/Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | JAVA=java 3 | JAVA_OPTS=-XX:+IgnoreUnrecognizedVMOptions -XX:+UseParallelGC 4 | TLA2TOOLS_JAR=../../../tools/tla2tools.jar 5 | 6 | TLC_CMD=$(JAVA) $(JAVA_OPTS) -cp $(TLA2TOOLS_JAR) 7 | 8 | TLC=$(TLC_CMD) tlc2.TLC 9 | PCAL=$(TLC_CMD) pcal.trans 10 | SANY=$(TLC_CMD) tla2sany.SANY 11 | 12 | mc: 13 | $(TLC) -config kv.cfg -workers 'auto' -cleanup kv.tla 14 | 15 | tlaplusgen: 16 | $(PCAL) -nocfg kv.tla 17 | 18 | sany: 19 | $(SANY) kv.tla 20 | 21 | test: 22 | $(GO) test -v 23 | 24 | racetest: 25 | $(GO) test -race -v 26 | -------------------------------------------------------------------------------- /systems/raftres/kv/kv.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT Debug = FALSE 5 | 6 | CONSTANT NumServers = 3 7 | CONSTANT NumClients = 2 8 | 9 | CONSTANT BufferSize = 3 10 | 11 | CONSTANT NumRequests = 2 12 | 13 | \* SPECIFICATION definition 14 | SPECIFICATION Spec 15 | 16 | \* CHECK_DEADLOCK setting 17 | CHECK_DEADLOCK FALSE -------------------------------------------------------------------------------- /systems/raftres/raft/Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | JAVA=java 3 | JAVA_OPTS=-XX:+IgnoreUnrecognizedVMOptions -XX:+UseParallelGC 4 | TLA2TOOLS_JAR=../../../tools/tla2tools.jar 5 | 6 | TLC_CMD=$(JAVA) $(JAVA_OPTS) -cp $(TLA2TOOLS_JAR) 7 | 8 | TLC=$(TLC_CMD) tlc2.TLC 9 | PCAL=$(TLC_CMD) pcal.trans 10 | SANY=$(TLC_CMD) tla2sany.SANY 11 | 12 | .PHONY: sim mc tlaplusgen sany test racetest build 13 | 14 | sim: 15 | $(TLC) -config models/SIM.cfg -depth '1000' -simulate num=100 -workers 'auto' -cleanup raft.tla 16 | 17 | mc: 18 | $(TLC) -config models/MC.cfg -workers 'auto' -cleanup raft.tla 19 | 20 | tlaplusgen: 21 | $(PCAL) -nocfg raft.tla 22 | 23 | sany: 24 | $(SANY) raft.tla 25 | 26 | test: 27 | $(GO) test -v 28 | 29 | racetest: 30 | $(GO) test -race -v 31 | -------------------------------------------------------------------------------- /systems/raftres/raft/customch.go: -------------------------------------------------------------------------------- 1 | package raft 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/UBC-NSS/pgo/distsys" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | // CustomInChan is similar resources.InputChannel, however, after a timeout it 12 | // returns a default value instead of aborting the critical section. It used in 13 | // implementing periodic sending of AppendEntries request. In some cases, the 14 | // request should be sent immediately, for example, when the server just becomes 15 | // a leader. In this case, the input channel signals. 16 | type CustomInChan struct { 17 | distsys.ArchetypeResourceLeafMixin 18 | channel <-chan tla.Value 19 | buffer, backlogBuffer []tla.Value 20 | timeout time.Duration 21 | } 22 | 23 | var _ distsys.ArchetypeResource = &CustomInChan{} 24 | 25 | func NewCustomInChan(ch <-chan tla.Value, timeout time.Duration) distsys.ArchetypeResource { 26 | return &CustomInChan{ 27 | channel: ch, 28 | timeout: timeout, 29 | } 30 | } 31 | 32 | func (res *CustomInChan) Abort() chan struct{} { 33 | res.buffer = append(res.backlogBuffer, res.buffer...) 34 | res.backlogBuffer = nil 35 | return nil 36 | } 37 | 38 | func (res *CustomInChan) PreCommit() chan error { 39 | return nil 40 | } 41 | 42 | func (res *CustomInChan) Commit() chan struct{} { 43 | res.backlogBuffer = nil 44 | return nil 45 | } 46 | 47 | func (res *CustomInChan) ReadValue() (tla.Value, error) { 48 | if len(res.buffer) > 0 { 49 | value := res.buffer[0] 50 | res.buffer = res.buffer[1:] 51 | res.backlogBuffer = append(res.backlogBuffer, value) 52 | return value, nil 53 | } 54 | 55 | select { 56 | case value := <-res.channel: 57 | res.backlogBuffer = append(res.backlogBuffer, value) 58 | return value, nil 59 | case <-time.After(res.timeout): 60 | return tla.ModuleTRUE, nil 61 | } 62 | } 63 | 64 | func (res *CustomInChan) WriteValue(value tla.Value) error { 65 | panic(fmt.Errorf("attempted to write %v to an input channel resource", value)) 66 | } 67 | 68 | func (res *CustomInChan) Close() error { 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /systems/raftres/raft/models/MC.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT ExploreFail = TRUE 5 | CONSTANT Debug = FALSE 6 | 7 | CONSTANT NumServers = 3 8 | 9 | CONSTANT BufferSize = 3 10 | 11 | CONSTANT MaxTerm = 3 12 | CONSTANT MaxCommitIndex = 2 13 | 14 | CONSTANT MaxNodeFail = 1 15 | 16 | CONSTANT LogConcat = 2 17 | CONSTANT LogPop = 1 18 | 19 | CONSTANT LeaderTimeoutReset = TRUE 20 | 21 | \* CONSTRAINT definition 22 | CONSTRAINT MCConstraint 23 | 24 | \* SPECIFICATION definition 25 | SPECIFICATION Spec 26 | 27 | \* INVARIANT definition 28 | INVARIANT ElectionSafety 29 | INVARIANT LogMatching 30 | INVARIANT LeaderCompleteness 31 | INVARIANT StateMachineSafety 32 | INVARIANT plogOK 33 | INVARIANT TermOK 34 | INVARIANT CommitIndexOK 35 | INVARIANT NodeFailOK 36 | 37 | \* PROPERTY definition 38 | PROPERTY LeaderAppendOnly 39 | -------------------------------------------------------------------------------- /systems/raftres/raft/models/SIM.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT ExploreFail = FALSE 5 | CONSTANT Debug = FALSE 6 | 7 | CONSTANT NumServers = 3 8 | 9 | CONSTANT BufferSize = 10 10 | 11 | CONSTANT MaxTerm = 2 12 | CONSTANT MaxCommitIndex = 2 13 | 14 | CONSTANT MaxNodeFail = 1 15 | 16 | CONSTANT LogConcat = 2 17 | CONSTANT LogPop = 1 18 | 19 | CONSTANT LeaderTimeoutReset = TRUE 20 | 21 | \* CONSTRAINT definition 22 | CONSTRAINT LimitNodeFailure 23 | 24 | \* SPECIFICATION definition 25 | SPECIFICATION Spec 26 | 27 | \* INVARIANT definition 28 | INVARIANT ElectionSafety 29 | INVARIANT LogMatching 30 | INVARIANT LeaderCompleteness 31 | INVARIANT StateMachineSafety 32 | INVARIANT plogOK 33 | INVARIANT NodeFailOK 34 | 35 | \* PROPERTY definition 36 | PROPERTY LeaderAppendOnly 37 | PROPERTY ElectionLiveness 38 | PROPERTY AcceptLiveness -------------------------------------------------------------------------------- /systems/raftres/raft/raft_test.go: -------------------------------------------------------------------------------- 1 | package raft_test 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "testing" 7 | "time" 8 | 9 | "github.com/DistCompiler/pgo/systems/raftres/configs" 10 | "github.com/DistCompiler/pgo/systems/raftres/raft/bootstrap" 11 | "github.com/UBC-NSS/pgo/distsys/resources" 12 | "github.com/UBC-NSS/pgo/distsys/tla" 13 | "github.com/dgraph-io/badger/v3" 14 | ) 15 | 16 | func setupMonitor(c configs.Root, srvId int) *resources.Monitor { 17 | addr := c.Servers[srvId].MonitorAddr 18 | mon := resources.NewMonitor(addr) 19 | go func() { 20 | if err := mon.ListenAndServe(); err != nil { 21 | log.Fatal(err) 22 | } 23 | }() 24 | return mon 25 | } 26 | 27 | func TestRaftLeaderElection(t *testing.T) { 28 | c, err := configs.ReadConfig("../configs/local-3.yaml") 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | var monitors []*resources.Monitor 34 | var servers []*bootstrap.Server 35 | 36 | for id := range c.Servers { 37 | dbPath := fmt.Sprintf("/tmp/server%d/badger", id) 38 | db, err := badger.Open(badger.DefaultOptions(dbPath)) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | defer func() { 43 | if err := db.Close(); err != nil { 44 | log.Println(err) 45 | } 46 | }() 47 | 48 | mon := setupMonitor(c, id) 49 | monitors = append(monitors, mon) 50 | 51 | propCh := make(chan tla.Value, 10) 52 | acctCh := make(chan tla.Value, 10) 53 | 54 | s := bootstrap.NewServer(id, c, db, mon, propCh, acctCh) 55 | servers = append(servers, s) 56 | go func() { 57 | err := s.Run() 58 | if err != nil { 59 | log.Println(err) 60 | } 61 | }() 62 | } 63 | 64 | time.Sleep(2 * time.Second) 65 | 66 | for _, s := range servers { 67 | err := s.Close() 68 | if err != nil { 69 | t.Error(err) 70 | } 71 | } 72 | for _, mon := range monitors { 73 | err := mon.Close() 74 | if err != nil { 75 | t.Error(err) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /systems/raftres/server.go: -------------------------------------------------------------------------------- 1 | package raftres 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/DistCompiler/pgo/systems/raftres/configs" 9 | "github.com/DistCompiler/pgo/systems/raftres/kv" 10 | "github.com/DistCompiler/pgo/systems/raftres/raft/bootstrap" 11 | "github.com/UBC-NSS/pgo/distsys/resources" 12 | "github.com/UBC-NSS/pgo/distsys/tla" 13 | "github.com/dgraph-io/badger/v3" 14 | "go.uber.org/multierr" 15 | ) 16 | 17 | type Server struct { 18 | Id int 19 | Config configs.Root 20 | 21 | db *badger.DB 22 | mon *resources.Monitor 23 | 24 | raft *bootstrap.Server 25 | kv *kv.Server 26 | } 27 | 28 | func NewServer(srvId int, c configs.Root) *Server { 29 | acctCh := make(chan tla.Value, 100) 30 | propCh := make(chan tla.Value, 100) 31 | 32 | dbPath := fmt.Sprintf("/tmp/raftres/server%d/badger", srvId) 33 | 34 | // temp: removing existing badger files 35 | log.Println("removing badger files") 36 | if err := os.RemoveAll(dbPath); err != nil { 37 | log.Println(err) 38 | } 39 | 40 | db, err := badger.Open(badger.DefaultOptions(dbPath)) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | mon := resources.NewMonitor(c.Servers[srvId].MonitorAddr) 46 | 47 | raft := bootstrap.NewServer(srvId, c, db, mon, propCh, acctCh) 48 | kvServer := kv.NewServer(srvId, c, mon, propCh, acctCh) 49 | 50 | return &Server{ 51 | Id: srvId, 52 | Config: c, 53 | db: db, 54 | mon: mon, 55 | raft: raft, 56 | kv: kvServer, 57 | } 58 | } 59 | 60 | func (s *Server) Run() error { 61 | cnt := 3 62 | 63 | errCh := make(chan error) 64 | go func() { 65 | err := s.mon.ListenAndServe() 66 | errCh <- err 67 | }() 68 | go func() { 69 | err := s.raft.Run() 70 | errCh <- err 71 | }() 72 | go func() { 73 | err := s.kv.Run() 74 | errCh <- err 75 | }() 76 | 77 | for i := 0; i < cnt; i++ { 78 | err := <-errCh 79 | if err != nil { 80 | return err 81 | } 82 | } 83 | return nil 84 | } 85 | 86 | func (s *Server) Close() error { 87 | var err error 88 | 89 | err = multierr.Append(err, s.raft.Close()) 90 | err = multierr.Append(err, s.kv.Close()) 91 | err = multierr.Append(err, s.mon.Close()) 92 | err = multierr.Append(err, s.db.Close()) 93 | return err 94 | } 95 | -------------------------------------------------------------------------------- /systems/replicatedkv/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/replicatedkv 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.0 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /systems/replicatedkv/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= 2 | github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= 9 | github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 13 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 16 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 17 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= 18 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= 19 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 20 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 21 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 22 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | -------------------------------------------------------------------------------- /systems/shcounter/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/shcounter 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.2.0 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 20 | github.com/golang/protobuf v1.5.4 // indirect 21 | github.com/golang/snappy v0.0.4 // indirect 22 | github.com/google/flatbuffers v24.3.25+incompatible // indirect 23 | github.com/klauspost/compress v1.17.11 // indirect 24 | github.com/pkg/errors v0.9.1 // indirect 25 | github.com/segmentio/fasthash v1.0.3 // indirect 26 | go.opencensus.io v0.24.0 // indirect 27 | go.uber.org/multierr v1.11.0 // indirect 28 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 29 | golang.org/x/net v0.31.0 // indirect 30 | golang.org/x/sync v0.9.0 // indirect 31 | golang.org/x/sys v0.27.0 // indirect 32 | google.golang.org/protobuf v1.35.2 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /systems/shcounter/shcounter_test.go: -------------------------------------------------------------------------------- 1 | package shcounter 2 | 3 | // The main test logic has been moved to shcounter_util, in order to also 4 | // use the same tests for benchmarking. 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | func TestShCounter(t *testing.T) { 11 | RunTest(t, 20) 12 | } 13 | -------------------------------------------------------------------------------- /systems/shopcart/Makefile: -------------------------------------------------------------------------------- 1 | GO=go 2 | JAVA=java 3 | JAVA_OPTS=-XX:+IgnoreUnrecognizedVMOptions -XX:+UseParallelGC 4 | TLA2TOOLS_JAR=../../tools/tla2tools.jar 5 | 6 | TLC_CMD=$(JAVA) $(JAVA_OPTS) -cp $(TLA2TOOLS_JAR) 7 | 8 | TLC=$(TLC_CMD) tlc2.TLC 9 | PCAL=$(TLC_CMD) pcal.trans 10 | SANY=$(TLC_CMD) tla2sany.SANY 11 | 12 | all: build 13 | 14 | mc: 15 | $(TLC) -config shopcart.cfg -workers 'auto' -cleanup shopcart.tla 16 | 17 | tlaplusgen: 18 | $(PCAL) -nocfg shopcart.tla 19 | 20 | sany: 21 | $(SANY) shopcart.tla 22 | 23 | build: 24 | $(GO) build -o node cmd/*.go 25 | 26 | test: 27 | $(GO) test -v 28 | 29 | racetest: 30 | $(GO) test -v -race -------------------------------------------------------------------------------- /systems/shopcart/README.md: -------------------------------------------------------------------------------- 1 | # shopcart 2 | 3 | shopcart models an AWORSET set CRDT. 4 | 5 | For more information check out this report: 6 | [Distributed State in PGo](https://shayanh.com/files/diststate.pdf). 7 | 8 | 9 | ## Model 10 | 11 | We express query and update methods of CRDTs using mapping macros in MPCal. For 12 | each CRDT, we define a mapping macro that its read section implements the query 13 | method and its write section implements the update method. In this case, the 14 | mapping macro is `AWORSet`. 15 | 16 | According to the CRDT paper [Shapiro et al. 2011], every node infinitely often 17 | sends its state to every other node. Each node after receiving another node’s 18 | state, merges the received state into its local state. So there is no 19 | restriction on the execution order of merge operations. To simulate this 20 | behavior, we created a new process in the specification that runs concurrently 21 | with node archetypes. This process, in each of its execution step, merges states 22 | of two nodes that do not have equal states (merging states of two nodes with the 23 | same state has no effect). The model checker will explore every possible 24 | execution orders of running this process along with that of the node archetypes. 25 | This allows us to make sure that a system works correctly in every possible way 26 | that merge operations can happen. Note that for this, we do not use an 27 | `archetype`, and we use a `process` because we wish to model-check this behavior 28 | without compiling it into Go. The actual broadcasting in the Go implementation 29 | is included in the CRDT resources. 30 | 31 | ### Properties 32 | 33 | - Eventual Delivery: an update delivered at some node is eventually delivered to 34 | all nodes. 35 | - Strong Convergence: Correct replicas that have delivered the same updates have 36 | equivalent state. 37 | - Termination: All method executions terminate. -------------------------------------------------------------------------------- /systems/shopcart/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/DistCompiler/pgo/systems/shopcart" 10 | "github.com/DistCompiler/pgo/systems/shopcart/configs" 11 | ) 12 | 13 | func main() { 14 | var nid int 15 | var configPath string 16 | flag.IntVar(&nid, "nid", -1, "Server ID") 17 | flag.StringVar(&configPath, "c", "", "Config file") 18 | 19 | flag.Parse() 20 | 21 | if nid == -1 { 22 | log.Fatal("id is not provided or it is invalid") 23 | } 24 | if configPath == "" { 25 | log.Fatal("config file is not provided") 26 | } 27 | 28 | c, err := configs.ReadConfig(configPath) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | ch := make(chan shopcart.Event, 10) 34 | node := shopcart.NewNode(nid, c, ch) 35 | go func() { 36 | if err := node.Run(); err != nil { 37 | log.Println(err) 38 | } 39 | }() 40 | 41 | var start time.Time 42 | roundIdx := 0 43 | numEvents := 2 * (c.NumRounds - 1) 44 | for i := 0; i < numEvents; i++ { 45 | e := <-ch 46 | 47 | // log.Println(nid, e) 48 | 49 | if e == shopcart.AddStartEvent { 50 | start = time.Now() 51 | } else if e == shopcart.AddFinishEvent { 52 | elapsed := time.Since(start) 53 | fmt.Println("RESULT", roundIdx, nid, elapsed) 54 | roundIdx += 1 55 | } 56 | } 57 | 58 | if err := node.Close(); err != nil { 59 | log.Println(err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /systems/shopcart/configs/config.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | type Root struct { 10 | NumRounds int 11 | 12 | BroadcastInterval time.Duration 13 | SendTimeout time.Duration 14 | DialTimeout time.Duration 15 | 16 | Peers map[int]string 17 | } 18 | 19 | func ReadConfig(path string) (Root, error) { 20 | viper.SetConfigFile(path) 21 | if err := viper.ReadInConfig(); err != nil { 22 | return Root{}, err 23 | } 24 | var c Root 25 | err := viper.Unmarshal(&c) 26 | return c, err 27 | } 28 | -------------------------------------------------------------------------------- /systems/shopcart/configs/local-4.yaml: -------------------------------------------------------------------------------- 1 | numRounds: 10 2 | 3 | broadcastInterval: 50ms 4 | sendTimeout: 200ms 5 | dialTimeout: 50ms 6 | 7 | peers: 8 | 1: "localhost:8001" 9 | 2: "localhost:8002" 10 | 3: "localhost:8003" 11 | 4: "localhost:8004" 12 | -------------------------------------------------------------------------------- /systems/shopcart/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/DistCompiler/pgo/systems/shopcart 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../distsys 8 | 9 | require ( 10 | github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 11 | github.com/spf13/viper v1.19.0 12 | ) 13 | 14 | require ( 15 | github.com/benbjohnson/immutable v0.4.3 // indirect 16 | github.com/cespare/xxhash v1.1.0 // indirect 17 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 18 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 19 | github.com/dgraph-io/ristretto v0.2.0 // indirect 20 | github.com/dustin/go-humanize v1.0.1 // indirect 21 | github.com/fsnotify/fsnotify v1.8.0 // indirect 22 | github.com/gogo/protobuf v1.3.2 // indirect 23 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 24 | github.com/golang/protobuf v1.5.4 // indirect 25 | github.com/golang/snappy v0.0.4 // indirect 26 | github.com/google/flatbuffers v24.3.25+incompatible // indirect 27 | github.com/hashicorp/hcl v1.0.0 // indirect 28 | github.com/klauspost/compress v1.17.11 // indirect 29 | github.com/magiconair/properties v1.8.7 // indirect 30 | github.com/mitchellh/mapstructure v1.5.0 // indirect 31 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 32 | github.com/pkg/errors v0.9.1 // indirect 33 | github.com/sagikazarmark/locafero v0.6.0 // indirect 34 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 35 | github.com/segmentio/fasthash v1.0.3 // indirect 36 | github.com/sourcegraph/conc v0.3.0 // indirect 37 | github.com/spf13/afero v1.11.0 // indirect 38 | github.com/spf13/cast v1.7.0 // indirect 39 | github.com/spf13/pflag v1.0.5 // indirect 40 | github.com/subosito/gotenv v1.6.0 // indirect 41 | go.opencensus.io v0.24.0 // indirect 42 | go.uber.org/multierr v1.11.0 // indirect 43 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 44 | golang.org/x/net v0.31.0 // indirect 45 | golang.org/x/sync v0.9.0 // indirect 46 | golang.org/x/sys v0.27.0 // indirect 47 | golang.org/x/text v0.20.0 // indirect 48 | google.golang.org/protobuf v1.35.2 // indirect 49 | gopkg.in/ini.v1 v1.67.0 // indirect 50 | gopkg.in/yaml.v3 v3.0.1 // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /systems/shopcart/shopcart.cfg: -------------------------------------------------------------------------------- 1 | \* CONSTANT declaration 2 | CONSTANT defaultInitValue = defaultInitValue 3 | 4 | CONSTANT NumNodes = 3 5 | CONSTANT BenchNumRounds = 2 6 | CONSTANT ElemSet <- BenchElemSet 7 | 8 | \* SPECIFICATION definition 9 | SPECIFICATION Spec 10 | 11 | \* INVARIANT definition 12 | INVARIANT QueryOK 13 | 14 | \* PROPERTY definition 15 | PROPERTY EventualStateConvergence 16 | PROPERTY EventualValueConvergence 17 | 18 | PROPERTY EventualDelivery 19 | 20 | PROPERTY StrongConvergence 21 | 22 | PROPERTY NodeTermination 23 | 24 | \* CHECK_DEADLOCK setting 25 | CHECK_DEADLOCK FALSE -------------------------------------------------------------------------------- /systems/shopcart/shopcart_test.go: -------------------------------------------------------------------------------- 1 | package shopcart_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/UBC-NSS/pgo/distsys/hashmap" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | // func TestShopCart(t *testing.T) { 12 | // c, err := configs.ReadConfig("configs/local-4.yaml") 13 | // if err != nil { 14 | // t.Fatal(err) 15 | // } 16 | 17 | // var wg sync.WaitGroup 18 | // wg.Add(len(c.Peers)) 19 | 20 | // var nodes []*shopcart.Node 21 | // for id := range c.Peers { 22 | // nid := id 23 | // ch := make(chan shopcart.Event, 10) 24 | 25 | // node := shopcart.NewNode(nid, c, ch) 26 | // nodes = append(nodes, node) 27 | 28 | // go func() { 29 | // if err := node.Run(); err != nil { 30 | // log.Println(err) 31 | // } 32 | // }() 33 | 34 | // go func() { 35 | // var start time.Time 36 | // roundIdx := 0 37 | // numEvents := 2 * (c.NumRounds - 1) 38 | // for i := 0; i < numEvents; i++ { 39 | // e := <-ch 40 | // // log.Println(nid, e) 41 | // if e == shopcart.AddStartEvent { 42 | // start = time.Now() 43 | // } else if e == shopcart.AddFinishEvent { 44 | // elapsed := time.Since(start) 45 | // log.Println("RESULT", roundIdx, nid, elapsed) 46 | // roundIdx += 1 47 | // } 48 | // } 49 | // wg.Done() 50 | // }() 51 | // } 52 | // defer func() { 53 | // for _, node := range nodes { 54 | // err := node.Close() 55 | // if err != nil { 56 | // log.Println(err) 57 | // } 58 | // } 59 | // }() 60 | 61 | // wg.Wait() 62 | // } 63 | 64 | func TestHashMap(t *testing.T) { 65 | t1 := tla.MakeTuple(tla.MakeNumber(1), tla.MakeNumber(0)) 66 | t2 := tla.MakeTuple(tla.MakeNumber(2), tla.MakeNumber(0)) 67 | 68 | fmt.Println(t1, t1.Hash()) 69 | fmt.Println(t2, t2.Hash()) 70 | 71 | h := hashmap.New[bool]() 72 | h.Set(t1, true) 73 | 74 | val, ok := h.Get(t2) 75 | fmt.Println(val, ok) 76 | } 77 | -------------------------------------------------------------------------------- /test/files/general/AssignmentRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE AssignmentRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal AssignmentRules { 5 | macro MyMacro(x) { 6 | x := 3; 7 | \*:: expectedError: MultipleAssignmentError 8 | y := 4; 9 | } 10 | 11 | procedure Proc2() 12 | variables x, y; 13 | { 14 | l1: MyMacro(x); 15 | l2: MyMacro(x); MyMacro( (*:: expectedError: MultipleAssignmentError *) x); 16 | l3: MyMacro(x); (*:: expectedError: MultipleAssignmentError *) y := y + 1; 17 | l4: MyMacro(x); (*:: expectedError: MultipleAssignmentError *) x := -1; 18 | } 19 | 20 | procedure MyProcedure(x, y) 21 | variables x, y; 22 | { 23 | p: either { y := 10 } or { skip }; 24 | \*:: expectedError: MultipleAssignmentError 25 | y := 11; (* missing label *) 26 | p2: y := 20; 27 | x := y || (*:: expectedError: MultipleAssignmentError *) y := x; (* swap x and y: invalid *) 28 | } 29 | 30 | archetype MyArchetype(ref x) 31 | variables x; 32 | { 33 | a1: x := 10; 34 | \*:: expectedError: MultipleAssignmentError 35 | x := 11; (* missing label *) 36 | } 37 | 38 | process (MyProcess = 23) 39 | variables n; 40 | { 41 | l1: n := 2; 42 | l2: while (n < 10) { 43 | n := 12; 44 | if (n = 20) { 45 | \*:: expectedError: MultipleAssignmentError 46 | n := 100; (* missing label *) 47 | } 48 | }; 49 | \*:: expectedError: MultipleAssignmentError 50 | n := 32; (* label not missing *) 51 | 52 | l3: if (n = 32) { 53 | n := 0; 54 | }; 55 | \*:: expectedError: MultipleAssignmentError 56 | n := 12; (* missing label *) 57 | } 58 | } 59 | *) 60 | \* BEGIN TRANSLATION 61 | ==== -------------------------------------------------------------------------------- /test/files/general/AssignmentRules.tla.noMultipleWrites: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistCompiler/pgo/0008579c554dd5d66a343ea4c727b75ad68d4ee5/test/files/general/AssignmentRules.tla.noMultipleWrites -------------------------------------------------------------------------------- /test/files/general/CallLabelingRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE CallLabelingRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal CallLabelingRules { 5 | procedure SomeProcedure() { 6 | l99: skip; 7 | } 8 | 9 | procedure MyProcedure() { 10 | l2: print "procedure"; 11 | call SomeProcedure(); 12 | return; (* no label required *) 13 | } 14 | 15 | archetype MyArchetype() { 16 | l1: print "first label"; 17 | call MyProcedure(); 18 | \*:: expectedError: LabelRequiredError 19 | call MyProcedure(); 20 | } 21 | 22 | process (MyProcess = 32) 23 | variables x; 24 | { 25 | l3: print "process"; 26 | call MyProcedure(); 27 | goto l3; (* no label required *) 28 | l4: print "next label"; 29 | call MyProcedure(); 30 | \*:: expectedError: LabelRequiredError 31 | x := 10; (* missing label *) 32 | } 33 | } 34 | *) 35 | \* BEGIN TRANSLATION 36 | ==== -------------------------------------------------------------------------------- /test/files/general/ExprTests.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/exprtests 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/general/ExprTests.tla.gotests/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= 2 | github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= 9 | github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 16 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 17 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 18 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 19 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= 20 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /test/files/general/IfEitherLabelingRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE IfEitherLabelingRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal IfEitherLabelingRules { 5 | 6 | procedure MyProcedure() 7 | variables v; 8 | { 9 | l2: print "procedure"; 10 | either { v := 10 } or { return }; 11 | \*:: expectedError: LabelRequiredError 12 | goto l2; (* missing label *) 13 | } 14 | 15 | archetype MyArchetype() { 16 | l1: print "first label"; 17 | if (TRUE) { 18 | print "true"; 19 | } else if (TRUE) { 20 | call MyProcedure(); 21 | }; 22 | 23 | \*:: expectedError: LabelRequiredError 24 | print "needs label"; (* missing label *) 25 | } 26 | 27 | process (MyProcess = 32) 28 | variables x, y; 29 | { 30 | l3: print "process"; 31 | either { x := 0 } or { goto l3 }; 32 | l4: print "all good"; 33 | 34 | either { goto l4 } or { skip }; 35 | \*:: expectedError: LabelRequiredError 36 | x := 50; (* missing label *) 37 | 38 | l5: if (TRUE) { 39 | l6: print "label"; 40 | }; 41 | \*:: expectedError: LabelRequiredError 42 | y := 20; (* missing label *) 43 | 44 | l6: while(TRUE) { 45 | l7: skip; 46 | }; 47 | skip; \* ok, no label needed (this says something about the correct desugaring for while) 48 | } 49 | } 50 | *) 51 | \* BEGIN TRANSLATION 52 | ==== -------------------------------------------------------------------------------- /test/files/general/IndexingLocals.tla.gotests/IndexingLocals_test.go: -------------------------------------------------------------------------------- 1 | package indexinglocals 2 | 3 | import ( 4 | "github.com/UBC-NSS/pgo/distsys" 5 | "github.com/UBC-NSS/pgo/distsys/tla" 6 | "github.com/UBC-NSS/pgo/distsys/trace" 7 | "testing" 8 | ) 9 | 10 | func TestANode(t *testing.T) { 11 | traceRecorder := trace.MakeLocalFileRecorder("IndexingLocals_trace.txt") 12 | ctx := distsys.NewMPCalContext(tla.MakeString("self"), ANode, distsys.SetTraceRecorder(traceRecorder)) 13 | err := ctx.Run() 14 | if err != nil { 15 | panic(err) 16 | } 17 | 18 | logVal := ctx.IFace().ReadArchetypeResourceLocal("ANode.log") 19 | pVal := ctx.IFace().ReadArchetypeResourceLocal("ANode.p") 20 | 21 | if pVal.AsNumber() != 3 { 22 | t.Fatalf("%v did now equal 3", pVal) 23 | } 24 | 25 | expectedLogVal := tla.MakeTuple( 26 | tla.MakeNumber(3), 27 | tla.MakeNumber(21), 28 | tla.MakeNumber(999), 29 | tla.MakeRecord([]tla.RecordField{ 30 | {tla.MakeString("foo"), tla.MakeNumber(43)}, 31 | })) 32 | if !logVal.Equal(expectedLogVal) { 33 | t.Fatalf("%v did not equal %v", logVal, expectedLogVal) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/files/general/IndexingLocals.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/exprtests 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/general/IndexingLocals.tla.gotests/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= 2 | github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= 9 | github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 16 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 17 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 18 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 19 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= 20 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /test/files/general/InvalidAssignment.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE InvalidAssignment ---- 2 | EXTENDS Integers, Sequences, TLC, FiniteSets, Peano 3 | 4 | (* --mpcal InvalidAssignment { 5 | archetype Ping() { 6 | lbl: (*:: expectedError: PCalInvalidAssignment *) FALSE := TRUE; 7 | } 8 | 9 | procedure Bar() { 10 | lbl: (*:: expectedError: PCalInvalidAssignment *) TRUE := FALSE; 11 | } 12 | 13 | process (Foo = 0) { 14 | lbl: (*:: expectedError: PCalInvalidAssignment *) BOOLEAN := TRUE; 15 | } 16 | } 17 | *) 18 | 19 | \* BEGIN TRANSLATION 20 | ==== -------------------------------------------------------------------------------- /test/files/general/LabelBeforeWhile.tla: -------------------------------------------------------------------------------- 1 | |---- MODULE WhileLabels ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal WhileLabels { 5 | procedure CorrectProcedure() { 6 | l2: print "procedure"; 7 | l3: while (FALSE) { print(3 - 3) }; (* all good *) 8 | } 9 | 10 | archetype IncorrectArchetype() { 11 | l1: print "first label"; 12 | \*:: expectedError: LabelRequiredError 13 | while (TRUE) { print "hello" }; 14 | } 15 | 16 | process (IncorrectProcess = 32) { 17 | \*:: expectedError: LabelRequiredError 18 | while (10 < 20) { print(2 * 2) }; 19 | } 20 | 21 | process (NestedMissingWhileLabels = 33) { 22 | \*:: expectedError: LabelRequiredError 23 | while (10 < 20) { 24 | \*:: expectedError: LabelRequiredError 25 | while(20 < 30) { print(3 * 3) }; 26 | }; 27 | } 28 | } 29 | *) 30 | \* BEGIN TRANSLATION 31 | ==== -------------------------------------------------------------------------------- /test/files/general/LabelNotDefined.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE LabelNotDefined ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal LabelNotDefined { 5 | procedure Proc() { 6 | l1: 7 | \*:: expectedError: LabelNotDefinedError 8 | goto l2; 9 | } 10 | 11 | process (P = 1) { 12 | pl1: (*:: expectedError: LabelNotDefinedError *) goto pl2; 13 | } 14 | process (P = 2) { 15 | pl2: (*:: expectedError: LabelNotDefinedError *) goto pl1; 16 | } 17 | } 18 | *) 19 | \* BEGIN TRANSLATION 20 | ==== -------------------------------------------------------------------------------- /test/files/general/MPCalKindMatching.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MPCalKindMatching ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal MPCalKindMatching { 5 | procedure Proc(ref a[_]) 6 | variables x; 7 | { 8 | l2: call Proc((*:: expectedError: MPCalKindMismatchError *) a); 9 | l4: call Proc2(ref a[_][_]); 10 | l5: a[2] := 3; 11 | l3: (*:: expectedError: MPCalKindMismatchError *) a := 3; 12 | l6: (*:: expectedError: MPCalReadWriteAssignmentForbidden *) a[5][6] := 3; 13 | l7: x := (*:: expectedError: MPCalKindMismatchError *) a; 14 | l8: x := a[3]; 15 | l9: x := a[(*:: expectedError: MPCalKindMismatchError *) a]; 16 | l10: x := a[3][4]; 17 | l11: call Proc((*:: expectedError: MPCalKindMismatchError *) ref a); 18 | l12: call Proc(ref a[_]); 19 | l13: call Proc((*:: expectedError: MPCalKindMismatchError *) ref a[_][_]); 20 | } 21 | 22 | procedure Proc2(ref b[_][_]) { 23 | l3: skip; 24 | } 25 | 26 | mapping macro M { 27 | read { 28 | yield $variable; 29 | } 30 | write { 31 | yield $value; 32 | } 33 | } 34 | 35 | archetype Arch(ref a[_]) { 36 | l1: skip; 37 | } 38 | 39 | archetype Arch2(ref a) { 40 | l1: skip; 41 | } 42 | 43 | variables myVar; 44 | 45 | process (A = 42) == instance Arch(ref myVar[_]); 46 | 47 | process (B = 43) == instance Arch((*:: expectedError: MPCalKindMismatchError *) ref myVar); 48 | 49 | process (C = 44) == instance Arch(myVar); \* will generate a synthetic local var 50 | 51 | process (D = 45) == instance Arch((*:: expectedError: MPCalKindMismatchError *) ref myVar[_][_]); 52 | 53 | process (E = 46) == instance Arch(ref myVar[_]) 54 | mapping (*:: expectedError: MPCalKindMismatchError *) myVar via M; 55 | 56 | process (F = 47) == instance Arch2(ref myVar) 57 | mapping (*:: expectedError: MPCalKindMismatchError *) myVar[_] via M; 58 | } 59 | *) 60 | \* BEGIN TRANSLATION 61 | ==== -------------------------------------------------------------------------------- /test/files/general/MacroRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MacroRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal MacroRules { 5 | macro ValidMacro() { 6 | print(1 + 1); 7 | x := 10; 8 | } 9 | 10 | macro InvalidMacro() { 11 | either { skip } or { (*:: expectedError: LabelForbiddenError *) l1: print(10) }; (* invalid *) 12 | \*:: expectedError: LabelForbiddenError 13 | l2: print(20); (* invalid *) 14 | } 15 | } 16 | *) 17 | \* BEGIN TRANSLATION 18 | ==== -------------------------------------------------------------------------------- /test/files/general/MappingMacroRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MappingMacroRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | 4 | CONSTANTS NODE_SET, SERVER_SET, BUFFER_SIZE 5 | 6 | (* 7 | --mpcal MappingMacroRules { 8 | mapping macro TCPChannel { 9 | read { 10 | await Len($variable.queue) > 0 /\ $variable.enabled; 11 | with (msg = Head($variable.queue)) { 12 | $variable.queue := Tail($variable.queue); 13 | yield msg; 14 | }; 15 | } 16 | 17 | write { 18 | await Len($variable.queue) < BUFFER_SIZE /\ $variable.enabled; 19 | yield [queue |-> Append($variable.queue, $value), enabled |-> $variable.enabled]; 20 | } 21 | } 22 | 23 | mapping macro NetworkToggle { 24 | read { 25 | yield $variable.enabled; 26 | } 27 | 28 | write { 29 | yield [queue |-> $variable.queue, enabled |-> $value]; 30 | } 31 | } 32 | 33 | archetype AServer(ref net[_], ref netToggle[_]) { 34 | lbl: skip; 35 | } 36 | 37 | archetype OtherArchetype(notRef) { 38 | lbl: skip; 39 | } 40 | 41 | variables 42 | network = [id \in NODE_SET |-> [queue |-> <<>>, enabled |-> TRUE]]; 43 | 44 | fair process (Server \in SERVER_SET) == instance AServer(ref network[_], ref network[_]) 45 | mapping network[_] via TCPChannel 46 | (*:: expectedError: MPCalMultipleMapping *) mapping network[_] via NetworkToggle; 47 | 48 | fair process (Server2 \in SERVER_SET) == instance AServer(ref network[_], ref network[_]) 49 | mapping @1[_] via TCPChannel 50 | (*:: expectedError: MPCalMultipleMapping *) mapping @1[_] via NetworkToggle; 51 | 52 | fair process (Server3 \in SERVER_SET) == instance AServer(ref network[_], ref network[_]) 53 | mapping @2[_] via TCPChannel 54 | (*:: expectedError: MPCalMultipleMapping *) mapping @2[_] via NetworkToggle; 55 | 56 | fair process (Server4 \in SERVER_SET) == instance AServer(ref network[_], ref network[_]) 57 | mapping @1[_] via TCPChannel 58 | mapping @2[_] via NetworkToggle; 59 | 60 | fair process (Server5 \in SERVER_SET) == instance OtherArchetype("foo") 61 | mapping (*:: expectedError: MPCalMappingValueRefMismatchError *) @1[_] via TCPChannel; 62 | } 63 | 64 | *) 65 | 66 | \* BEGIN TRANSLATION 67 | ==== -------------------------------------------------------------------------------- /test/files/general/MappingMacroWithCallGoto.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MappingMacroWithCallGoto ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal MappingMacroWithCallGoto { 5 | procedure YesProcedure() { 6 | l1: skip; 7 | } 8 | procedure NoProcedure() { 9 | l2: skip; 10 | } 11 | 12 | mapping macro InvalidStatements { 13 | read { 14 | await Len($variable) = 0; 15 | if (TRUE) { 16 | call YesProcedure(); 17 | }; 18 | \*:: expectedError: LabelRequiredError 19 | call NoProcedure(); 20 | \*:: expectedError: LabelRequiredError 21 | yield 0; 22 | } 23 | write { 24 | either { yield $value } 25 | or { (*:: expectedError: LabelForbiddenError *) l1: goto l1 }; 26 | } 27 | } 28 | } 29 | *) 30 | \* BEGIN TRANSLATION 31 | ==== -------------------------------------------------------------------------------- /test/files/general/MappingWithLabels.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MappingWithLabels ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal MappingWithLabels { 5 | mapping macro ContainsLabels { 6 | read { 7 | \*:: expectedError: LabelForbiddenError 8 | r: yield $variable 9 | } 10 | write { 11 | if ($variable > 10) { 12 | \*:: expectedError: LabelForbiddenError 13 | w: yield $value; 14 | } 15 | } 16 | } 17 | } 18 | *) 19 | \* BEGIN TRANSLATION 20 | ==== -------------------------------------------------------------------------------- /test/files/general/NoFirstLabel.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE NoFirstLabel ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal NoFirstLabel { 5 | procedure MPCalProc(a) { 6 | \*:: expectedError: LabelRequiredError 7 | print(2 + 2); 8 | } 9 | archetype MyArchetype() { 10 | (*:: expectedError: LabelRequiredError *) 11 | print(1 + 1); 12 | } 13 | procedure PCalProc(a) { 14 | \*:: expectedError: LabelRequiredError 15 | print(3 + 3); 16 | } 17 | 18 | process (Proc = 1) { 19 | \*:: expectedError: LabelRequiredError 20 | print(4 + 4); 21 | } 22 | } 23 | *) 24 | \* BEGIN TRANSLATION 25 | ==== -------------------------------------------------------------------------------- /test/files/general/NonDetExploration.tla.gotests/NonDetExploration_test.go: -------------------------------------------------------------------------------- 1 | package nondetexploration 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/UBC-NSS/pgo/distsys" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | func TestCoverage(t *testing.T) { 12 | errCh := make(chan error, 1) 13 | ctx := distsys.NewMPCalContext(tla.MakeString("self"), ACoverage) 14 | go func() { 15 | errCh <- ctx.Run() 16 | }() 17 | 18 | select { 19 | case err := <-errCh: 20 | if err != nil { 21 | panic(err) 22 | } 23 | case <-time.After(5 * time.Second): 24 | t.Fatalf("timeout: ACoverage should eventually (within 5 seconds) terminate") 25 | } 26 | } 27 | 28 | func TestCoincidence(t *testing.T) { 29 | errCh := make(chan error, 1) 30 | ctx := distsys.NewMPCalContext(tla.MakeString("self"), ACoincidence) 31 | go func() { 32 | errCh <- ctx.Run() 33 | }() 34 | 35 | select { 36 | case err := <-errCh: 37 | if err != nil { 38 | panic(err) 39 | } 40 | case <-time.After(5 * time.Second): 41 | t.Fatalf("timeout: ACoincidence should eventually (within 5 seconds) terminate") 42 | } 43 | } 44 | 45 | func TestComplex(t *testing.T) { 46 | errCh := make(chan error, 1) 47 | ctx := distsys.NewMPCalContext(tla.MakeString("self"), AComplex) 48 | go func() { 49 | errCh <- ctx.Run() 50 | }() 51 | 52 | select { 53 | case err := <-errCh: 54 | if err != nil { 55 | panic(err) 56 | } 57 | case <-time.After(5 * time.Second): 58 | t.Fatalf("timeout: AComplex should eventually (within 5 seconds) terminate") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test/files/general/NonDetExploration.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/nondet 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/general/PBFail4_bug125.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/pbfail 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/general/PBFail4_bug125.tla.gotests/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= 2 | github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= 9 | github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 16 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 17 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 18 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 19 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= 20 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /test/files/general/ProcedureSpaghetti.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ProcedureSpaghetti ---- 2 | EXTENDS TLC, Sequences, Integers 3 | 4 | (* --mpcal ProcedureSpaghetti { 5 | 6 | procedure Proc1(ref a, b) 7 | variables c; 8 | { 9 | Proc1lbl1: 10 | call Proc2(ref a); 11 | Proc1lbl2: 12 | a := a + b; 13 | return; 14 | } 15 | 16 | procedure Proc2(ref a_) { 17 | Proc2lbl1: 18 | a_ := a_ + 1; 19 | return; 20 | } 21 | 22 | procedure RecursiveProcRef(ref X) { 23 | RecursiveProclbl1: 24 | print X; 25 | call RecursiveProcRef(ref X); 26 | return; 27 | } 28 | 29 | mapping macro M { 30 | read { 31 | yield $variable + 1; 32 | } 33 | write { 34 | yield $value - 1; 35 | } 36 | } 37 | 38 | archetype Arch1(ref e, f) { 39 | Arch1lbl: 40 | call Proc1(ref e, f); 41 | } 42 | 43 | variables V1, V2; 44 | 45 | process (Pross1 = 1) == instance Arch1(ref V1, 30) 46 | mapping V1 via M; 47 | 48 | process (Pross2 = 2) == instance Arch1(ref V1, 40); 49 | 50 | process (Pross3 = 3) == instance Arch1(ref V2, 50); 51 | 52 | process (Pross3Bis = 33) == instance Arch1(ref V2, 60); 53 | 54 | process (Pross4 = 4) 55 | variables c; 56 | { 57 | Prosslbl1: 58 | call Proc1(ref c, 10); 59 | Prosslbl2: 60 | call Proc1(ref V1, 20); 61 | } 62 | 63 | process (Pross5 = 5) { 64 | Pross5lbl1: 65 | call RecursiveProcRef(ref V1); 66 | } 67 | 68 | } *) 69 | 70 | (* 71 | 72 | \* BEGIN PLUSCAL TRANSLATION 73 | 74 | \* END PLUSCAL TRANSLATION 75 | 76 | *) 77 | 78 | \* BEGIN TRANSLATION 79 | ==== -------------------------------------------------------------------------------- /test/files/general/ProcedureSpaghetti.tla.gotests/ProcedureSpaghetti_test.go: -------------------------------------------------------------------------------- 1 | package procedurespaghetti 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/UBC-NSS/pgo/distsys" 7 | "github.com/UBC-NSS/pgo/distsys/tla" 8 | ) 9 | 10 | func TestArch1(t *testing.T) { 11 | ctx := distsys.NewMPCalContext(tla.MakeString("self"), Arch1, 12 | distsys.EnsureArchetypeRefParam("e", distsys.NewLocalArchetypeResource(tla.MakeNumber(13))), 13 | distsys.EnsureArchetypeValueParam("f", tla.MakeNumber(21))) 14 | 15 | err := ctx.Run() 16 | if err != nil { 17 | panic(err) 18 | } 19 | 20 | eVal := ctx.IFace().ReadArchetypeResourceLocal("&Arch1.e") 21 | fVal := ctx.IFace().ReadArchetypeResourceLocal("Arch1.f") 22 | 23 | if !eVal.Equal(tla.MakeNumber(35)) { 24 | t.Errorf("eVal did not equal expected value 35, was %v instead", eVal) 25 | } 26 | if !fVal.Equal(tla.MakeNumber(21)) { 27 | t.Errorf("fVal did not equal expected value 21 (same as start), was %v instead", fVal) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/files/general/ProcedureSpaghetti.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/test 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/general/RecursiveMacroRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE RecursiveMacroRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal RecursiveMacroRules { 5 | macro RecursiveMacro(x) { 6 | \*:: expectedError: RecursiveMacroError 7 | RecursiveMacro(x) 8 | } 9 | 10 | macro Indirect1(x) { 11 | \*:: expectedError: RecursiveMacroError 12 | Indirect2(x) 13 | } 14 | macro Indirect2(x) { 15 | \*:: expectedError: RecursiveMacroError 16 | Indirect1(x) 17 | } 18 | 19 | procedure CallsMacro() { 20 | label: 21 | \*:: expectedError: RecursiveMacroError 22 | Indirect1(5) 23 | } 24 | } 25 | *) 26 | \* BEGIN TRANSLATION 27 | ==== -------------------------------------------------------------------------------- /test/files/general/ReservedLabels.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ReservedLabels ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal ReservedLabels { 5 | procedure MyProcedure(y) { 6 | p: either { p1: y := 20 } or { (*:: expectedError: ReservedLabelError *) Error: skip }; (* reserved *) 7 | } 8 | 9 | archetype MyArchetype(ref x) { 10 | \*:: expectedError: ReservedLabelError 11 | Done: x := 10; (* reserved *) 12 | done: x := 30; (* no problem *) 13 | } 14 | } 15 | *) 16 | \* BEGIN TRANSLATION 17 | ==== -------------------------------------------------------------------------------- /test/files/general/ReturnGotoLabelingRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ReturnGotoLabelingRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal ReturnGotoLabelingRules { 5 | 6 | procedure MyProcedure() { 7 | l2: print "procedure"; 8 | return; 9 | \*:: expectedError: LabelRequiredError 10 | goto l2; (* missing label *) 11 | } 12 | 13 | archetype MyArchetype() { 14 | l1: print "first label"; 15 | goto l1; 16 | \*:: expectedError: LabelRequiredError 17 | print "needs label"; (* missing label *) 18 | } 19 | 20 | process (MyProcess = 32) 21 | variables x; 22 | { 23 | l3: print "process"; 24 | goto l3; 25 | \*:: expectedError: LabelRequiredError 26 | x := 10; (* missing label *) 27 | } 28 | } 29 | *) 30 | \* BEGIN TRANSLATION 31 | ==== -------------------------------------------------------------------------------- /test/files/general/WithRules.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE WithRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal WithRules { 5 | macro MacroWith() { 6 | print(1 + 1); 7 | with (x = 10) { 8 | print x; 9 | \*:: expectedError: LabelForbiddenError 10 | m1: x := 20; (* invalid *) 11 | }; 12 | \*:: expectedError: LabelForbiddenError 13 | m2: print(20); (* invalid *) 14 | } 15 | 16 | procedure ProcedureWith() { 17 | l1: with (x = 10) { 18 | \*:: expectedError: LabelForbiddenError 19 | l2: print x; (* invalid *) 20 | } 21 | } 22 | } 23 | *) 24 | \* BEGIN TRANSLATION 25 | ==== -------------------------------------------------------------------------------- /test/files/general/bug2_124.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE bug2 ---- 2 | EXTENDS Naturals, Sequences, TLC 3 | 4 | CONSTANTS NUM_NODES, BUFFER_SIZE 5 | 6 | (* --mpcal bug2 { 7 | mapping macro TCPChannel { 8 | read { 9 | await Len($variable) > 0; 10 | with (msg = Head($variable)) { 11 | $variable := Tail($variable); 12 | yield msg; 13 | }; 14 | } 15 | 16 | write { 17 | await Len($variable) < BUFFER_SIZE; 18 | yield Append($variable, $value); 19 | } 20 | } 21 | 22 | archetype AEchoServer(ref net[_]) 23 | variables 24 | msg; 25 | { 26 | serverLoop: 27 | while (TRUE) { 28 | rcvMsg: 29 | msg := net[self, 1]; 30 | sndMsg: 31 | net[msg.from, msg.typ] := [from |-> self, to |-> msg.from, 32 | body |-> msg.body, typ |-> msg.typ]; 33 | }; 34 | } 35 | 36 | variables 37 | network = [id \in 1..NUM_NODES, typ \in 1..4 |-> <<>>]; 38 | 39 | fair process (EchoServer \in 1..NUM_NODES) == instance AEchoServer(ref network[_]) 40 | mapping network[_] via TCPChannel; 41 | } 42 | 43 | \* BEGIN PLUSCAL TRANSLATION 44 | 45 | \* END PLUSCAL TRANSLATION 46 | *) 47 | 48 | \* BEGIN TRANSLATION 49 | ==== -------------------------------------------------------------------------------- /test/files/general/bug2_124.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/bug2 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/general/bug2_124.tla.gotests/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= 2 | github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= 9 | github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 16 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 17 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 18 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 19 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= 20 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /test/files/general/bug3_127.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE bug3_127 ---- 2 | EXTENDS TLC, Naturals 3 | 4 | CONSTANT NUM_NODES 5 | 6 | (* --mpcal bug3 { 7 | macro mayFail() { 8 | if ((*:: expectedError: UnboundFreeVariableError *) EXPLORE_FAIL) { 9 | either { 10 | skip; 11 | } or { 12 | goto fail; 13 | }; 14 | }; 15 | }; 16 | 17 | archetype AFoo() { 18 | start: 19 | mayFail(); 20 | 21 | program: 22 | print("program"); 23 | goto start; 24 | 25 | fail: 26 | print("fail"); 27 | } 28 | 29 | fair process (Foo \in 1..NUM_NODES) == instance AFoo(); 30 | } *) 31 | 32 | \* BEGIN TRANSLATION 33 | 34 | ==== -------------------------------------------------------------------------------- /test/files/general/bug_119.tla.gotests/bug_119_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/UBC-NSS/pgo/distsys" 7 | "github.com/UBC-NSS/pgo/distsys/resources" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | func TestCounter(t *testing.T) { 12 | outChan := make(chan tla.Value, 1) 13 | 14 | ctx := distsys.NewMPCalContext(tla.MakeString("self"), Counter, 15 | distsys.EnsureArchetypeRefParam("out", resources.NewOutputChan(outChan))) 16 | 17 | err := ctx.Run() 18 | if err != nil { 19 | panic(err) 20 | } 21 | 22 | outVal := <-outChan 23 | close(outChan) // everything is sync in this test, but close the channel anyway to catch anything weird 24 | if !outVal.Equal(tla.MakeNumber(1)) { 25 | t.Errorf("incrementation result %v was not equal to expected value 1", outVal) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/files/general/bug_119.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/test 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.1.1 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/glog v1.0.0 // indirect 20 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 21 | github.com/golang/protobuf v1.5.2 // indirect 22 | github.com/golang/snappy v0.0.4 // indirect 23 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 24 | github.com/klauspost/compress v1.15.15 // indirect 25 | github.com/pkg/errors v0.9.1 // indirect 26 | github.com/segmentio/fasthash v1.0.3 // indirect 27 | go.opencensus.io v0.24.0 // indirect 28 | go.uber.org/multierr v1.11.0 // indirect 29 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 30 | golang.org/x/net v0.31.0 // indirect 31 | golang.org/x/sync v0.9.0 // indirect 32 | golang.org/x/sys v0.27.0 // indirect 33 | google.golang.org/protobuf v1.28.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /test/files/general/bug_123.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE bug_123 ---- 2 | EXTENDS Naturals, Sequences, TLC 3 | 4 | CONSTANT NUM_NODES 5 | 6 | (***************** 7 | --mpcal bug_123 { 8 | mapping macro MyMappingMacro { 9 | read { 10 | yield $variable; 11 | } 12 | 13 | write { 14 | yield $value; 15 | } 16 | } 17 | 18 | archetype AServer(ref mp) { 19 | op: 20 | mp[self] := 1; 21 | } 22 | 23 | variables 24 | mymp = [id \in 1..NUM_NODES |-> 0]; 25 | 26 | fair process (Server \in 1..NUM_NODES) == instance AServer() 27 | mapping (*:: expectedError: MappingLookupError *) mymp[_] via MyMappingMacro; 28 | } *) 29 | 30 | \* BEGIN TRANSLATION 31 | ==== -------------------------------------------------------------------------------- /test/files/general/hello.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE hello ------------------------------- 2 | 3 | EXTENDS Naturals, Sequences, TLC, FiniteSets 4 | 5 | \* test higher-order constants 6 | CONSTANT MK_HELLO(_,_) 7 | 8 | (******************** 9 | --mpcal hello { 10 | define { 11 | HELLO == MK_HELLO("hell", "o") 12 | } 13 | 14 | archetype AHello(ref out) { 15 | lbl: 16 | out := HELLO; 17 | } 18 | 19 | variables out; 20 | 21 | fair process (Hello = 1) == instance AHello(ref out); 22 | } 23 | 24 | \* BEGIN PLUSCAL TRANSLATION 25 | --algorithm hello { 26 | variables out; 27 | define{ 28 | HELLO == "hello" 29 | } 30 | 31 | fair process (Hello = 1) 32 | { 33 | lbl: 34 | out := HELLO; 35 | goto Done; 36 | } 37 | } 38 | 39 | \* END PLUSCAL TRANSLATION 40 | 41 | ********************) 42 | \* BEGIN TRANSLATION (chksum(pcal) = "cb2aaacf" /\ chksum(tla) = "78d542e9") 43 | CONSTANT defaultInitValue 44 | VARIABLES out, pc 45 | 46 | (* define statement *) 47 | HELLO == "hello" 48 | 49 | 50 | vars == << out, pc >> 51 | 52 | ProcSet == {1} 53 | 54 | Init == (* Global variables *) 55 | /\ out = defaultInitValue 56 | /\ pc = [self \in ProcSet |-> "lbl"] 57 | 58 | lbl == /\ pc[1] = "lbl" 59 | /\ out' = HELLO 60 | /\ pc' = [pc EXCEPT ![1] = "Done"] 61 | 62 | Hello == lbl 63 | 64 | (* Allow infinite stuttering to prevent deadlock on termination. *) 65 | Terminating == /\ \A self \in ProcSet: pc[self] = "Done" 66 | /\ UNCHANGED vars 67 | 68 | Next == Hello 69 | \/ Terminating 70 | 71 | Spec == /\ Init /\ [][Next]_vars 72 | /\ WF_vars(Hello) 73 | 74 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 75 | 76 | \* END TRANSLATION 77 | 78 | \* Properties 79 | 80 | OutOK == <>(out = HELLO) 81 | 82 | ============================================================================= 83 | \* Modification History 84 | \* Last modified Thu Aug 26 14:12:33 PDT 2021 by shayan 85 | \* Created Thu Aug 26 13:10:19 PDT 2021 by shayan 86 | -------------------------------------------------------------------------------- /test/files/general/hello.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE hello ------------------------------- 2 | 3 | EXTENDS Naturals, Sequences, TLC, FiniteSets 4 | 5 | \* test higher-order constants 6 | CONSTANT MK_HELLO(_,_) 7 | 8 | (******************** 9 | --mpcal hello { 10 | define { 11 | HELLO == MK_HELLO("hell", "o") 12 | } 13 | 14 | archetype AHello(ref out) { 15 | lbl: 16 | out := HELLO; 17 | } 18 | 19 | variables out; 20 | 21 | fair process (Hello = 1) == instance AHello(ref out); 22 | } 23 | 24 | \* BEGIN PLUSCAL TRANSLATION 25 | --algorithm hello { 26 | variables out; 27 | define{ 28 | HELLO == MK_HELLO("hell", "o") 29 | } 30 | 31 | fair process (Hello = 1) 32 | { 33 | lbl: 34 | out := HELLO; 35 | goto Done; 36 | } 37 | } 38 | 39 | \* END PLUSCAL TRANSLATION 40 | 41 | ********************) 42 | \* BEGIN TRANSLATION (chksum(pcal) = "cb2aaacf" /\ chksum(tla) = "78d542e9") 43 | CONSTANT defaultInitValue 44 | VARIABLES out, pc 45 | 46 | (* define statement *) 47 | HELLO == "hello" 48 | 49 | 50 | vars == << out, pc >> 51 | 52 | ProcSet == {1} 53 | 54 | Init == (* Global variables *) 55 | /\ out = defaultInitValue 56 | /\ pc = [self \in ProcSet |-> "lbl"] 57 | 58 | lbl == /\ pc[1] = "lbl" 59 | /\ out' = HELLO 60 | /\ pc' = [pc EXCEPT ![1] = "Done"] 61 | 62 | Hello == lbl 63 | 64 | (* Allow infinite stuttering to prevent deadlock on termination. *) 65 | Terminating == /\ \A self \in ProcSet: pc[self] = "Done" 66 | /\ UNCHANGED vars 67 | 68 | Next == Hello 69 | \/ Terminating 70 | 71 | Spec == /\ Init /\ [][Next]_vars 72 | /\ WF_vars(Hello) 73 | 74 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 75 | 76 | \* END TRANSLATION 77 | 78 | \* Properties 79 | 80 | OutOK == <>(out = HELLO) 81 | 82 | ============================================================================= 83 | \* Modification History 84 | \* Last modified Thu Aug 26 14:12:33 PDT 2021 by shayan 85 | \* Created Thu Aug 26 13:10:19 PDT 2021 by shayan 86 | -------------------------------------------------------------------------------- /test/files/general/hello.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/hello 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require ( 10 | github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 11 | github.com/dgraph-io/badger/v3 v3.2103.5 12 | ) 13 | 14 | require ( 15 | github.com/benbjohnson/immutable v0.4.3 // indirect 16 | github.com/cespare/xxhash v1.1.0 // indirect 17 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 18 | github.com/dgraph-io/ristretto v0.1.1 // indirect 19 | github.com/dustin/go-humanize v1.0.1 // indirect 20 | github.com/gogo/protobuf v1.3.2 // indirect 21 | github.com/golang/glog v1.0.0 // indirect 22 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 23 | github.com/golang/protobuf v1.5.2 // indirect 24 | github.com/golang/snappy v0.0.4 // indirect 25 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 26 | github.com/klauspost/compress v1.15.15 // indirect 27 | github.com/pkg/errors v0.9.1 // indirect 28 | github.com/segmentio/fasthash v1.0.3 // indirect 29 | go.opencensus.io v0.24.0 // indirect 30 | go.uber.org/multierr v1.11.0 // indirect 31 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 32 | golang.org/x/net v0.31.0 // indirect 33 | golang.org/x/sync v0.9.0 // indirect 34 | golang.org/x/sys v0.27.0 // indirect 35 | google.golang.org/protobuf v1.28.1 // indirect 36 | ) 37 | -------------------------------------------------------------------------------- /test/files/general/hello.tla.gotests/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "fmt" 5 | "github.com/UBC-NSS/pgo/distsys" 6 | "github.com/UBC-NSS/pgo/distsys/tla" 7 | ) 8 | 9 | var _ = new(fmt.Stringer) // unconditionally prevent go compiler from reporting unused fmt import 10 | var _ = distsys.ErrDone 11 | var _ = tla.Value{} // same, for tla 12 | 13 | func HELLO(iface distsys.ArchetypeInterface) tla.Value { 14 | return iface.GetConstant("MK_HELLO")(tla.MakeString("hell"), tla.MakeString("o")) 15 | } 16 | 17 | var procTable = distsys.MakeMPCalProcTable() 18 | 19 | var jumpTable = distsys.MakeMPCalJumpTable( 20 | distsys.MPCalCriticalSection{ 21 | Name: "AHello.lbl", 22 | Body: func(iface distsys.ArchetypeInterface) error { 23 | var err error 24 | _ = err 25 | out, err := iface.RequireArchetypeResourceRef("AHello.out") 26 | if err != nil { 27 | return err 28 | } 29 | err = iface.Write(out, nil, HELLO(iface)) 30 | if err != nil { 31 | return err 32 | } 33 | return iface.Goto("AHello.Done") 34 | }, 35 | }, 36 | distsys.MPCalCriticalSection{ 37 | Name: "AHello.Done", 38 | Body: func(distsys.ArchetypeInterface) error { 39 | return distsys.ErrDone 40 | }, 41 | }, 42 | ) 43 | 44 | var AHello = distsys.MPCalArchetype{ 45 | Name: "AHello", 46 | Label: "AHello.lbl", 47 | RequiredRefParams: []string{"AHello.out"}, 48 | RequiredValParams: []string{}, 49 | JumpTable: jumpTable, 50 | ProcTable: procTable, 51 | PreAmble: func(iface distsys.ArchetypeInterface) { 52 | }, 53 | } 54 | -------------------------------------------------------------------------------- /test/files/general/raft.tla.gotests.ignore/customch.go: -------------------------------------------------------------------------------- 1 | package raft 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/UBC-NSS/pgo/distsys" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | // CustomInChan is similar resources.InputChannel, however, after a timeout it 12 | // returns a default value instead of aborting the critical section. It used in 13 | // implementing periodic sending of AppendEntries request. In some cases, the 14 | // request should be sent immediately, for example, when the server just becomes 15 | // a leader. In this case, the input channel signals. 16 | type CustomInChan struct { 17 | distsys.ArchetypeResourceLeafMixin 18 | channel <-chan tla.TLAValue 19 | buffer, backlogBuffer []tla.TLAValue 20 | timeout time.Duration 21 | } 22 | 23 | var _ distsys.ArchetypeResource = &CustomInChan{} 24 | 25 | func NewCustomInChan(ch <-chan tla.TLAValue, timeout time.Duration) distsys.ArchetypeResource { 26 | return &CustomInChan{ 27 | channel: ch, 28 | timeout: timeout, 29 | } 30 | } 31 | 32 | func (res *CustomInChan) Abort() chan struct{} { 33 | res.buffer = append(res.backlogBuffer, res.buffer...) 34 | res.backlogBuffer = nil 35 | return nil 36 | } 37 | 38 | func (res *CustomInChan) PreCommit() chan error { 39 | return nil 40 | } 41 | 42 | func (res *CustomInChan) Commit() chan struct{} { 43 | res.backlogBuffer = nil 44 | return nil 45 | } 46 | 47 | func (res *CustomInChan) ReadValue() (tla.TLAValue, error) { 48 | if len(res.buffer) > 0 { 49 | value := res.buffer[0] 50 | res.buffer = res.buffer[1:] 51 | res.backlogBuffer = append(res.backlogBuffer, value) 52 | return value, nil 53 | } 54 | 55 | select { 56 | case value := <-res.channel: 57 | res.backlogBuffer = append(res.backlogBuffer, value) 58 | return value, nil 59 | case <-time.After(res.timeout): 60 | return tla.TLA_TRUE, nil 61 | } 62 | } 63 | 64 | func (res *CustomInChan) WriteValue(value tla.TLAValue) error { 65 | panic(fmt.Errorf("attempted to write %v to an input channel resource", value)) 66 | } 67 | 68 | func (res *CustomInChan) Close() error { 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /test/files/general/raft.tla.gotests.ignore/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/raft 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/cespare/xxhash v1.1.0 // indirect 14 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 15 | github.com/dgraph-io/badger/v3 v3.2103.5 // indirect 16 | github.com/dgraph-io/ristretto v0.1.1 // indirect 17 | github.com/dustin/go-humanize v1.0.1 // indirect 18 | github.com/gogo/protobuf v1.3.2 // indirect 19 | github.com/golang/glog v1.0.0 // indirect 20 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 21 | github.com/golang/protobuf v1.5.2 // indirect 22 | github.com/golang/snappy v0.0.4 // indirect 23 | github.com/google/flatbuffers v23.1.21+incompatible // indirect 24 | github.com/klauspost/compress v1.15.15 // indirect 25 | github.com/pkg/errors v0.9.1 // indirect 26 | github.com/segmentio/fasthash v1.0.3 // indirect 27 | go.opencensus.io v0.24.0 // indirect 28 | go.uber.org/multierr v1.11.0 // indirect 29 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 30 | golang.org/x/net v0.31.0 // indirect 31 | golang.org/x/sync v0.9.0 // indirect 32 | golang.org/x/sys v0.27.0 // indirect 33 | google.golang.org/protobuf v1.28.1 // indirect 34 | ) 35 | -------------------------------------------------------------------------------- /test/files/general/raft.tla.gotests.ignore/timer.go: -------------------------------------------------------------------------------- 1 | package nestedcrdtimpl 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/UBC-NSS/pgo/distsys" 7 | "github.com/UBC-NSS/pgo/distsys/tla" 8 | ) 9 | 10 | func NewTimer(d time.Duration) distsys.ArchetypeResource { 11 | return &TimerResource{duration: d} 12 | } 13 | 14 | type TimerResource struct { 15 | distsys.ArchetypeResourceLeafMixin 16 | timer *time.Timer 17 | 18 | duration time.Duration 19 | } 20 | 21 | func (res *TimerResource) Abort() chan struct{} { 22 | return nil 23 | } 24 | 25 | func (res *TimerResource) PreCommit() chan error { 26 | return nil 27 | } 28 | 29 | func (res *TimerResource) Commit() chan struct{} { 30 | return nil 31 | } 32 | 33 | func (res *TimerResource) ReadValue() (tla.TLAValue, error) { 34 | if res.timer == nil { 35 | res.timer = time.NewTimer(res.duration) 36 | return tla.TLA_FALSE, nil 37 | } 38 | select { 39 | case <-res.timer.C: 40 | res.timer.Reset(res.duration) 41 | return tla.TLA_TRUE, nil 42 | default: 43 | return tla.TLA_FALSE, nil 44 | } 45 | } 46 | 47 | func (res *TimerResource) WriteValue(value tla.TLAValue) error { 48 | panic("write to timer resource is not allowed") 49 | } 50 | 51 | func (res *TimerResource) Close() error { 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /test/files/general/raftkvs.tla.gotests.ignore/customch.go: -------------------------------------------------------------------------------- 1 | package raftkvs 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/UBC-NSS/pgo/distsys" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | // CustomInChan is similar resources.InputChannel, however, after a timeout it 12 | // returns a default value instead of aborting the critical section. It used in 13 | // implementing periodic sending of AppendEntries request. In some cases, the 14 | // request should be sent immediately, for example, when the server just becomes 15 | // a leader. In this case, the input channel signals. 16 | type CustomInChan struct { 17 | distsys.ArchetypeResourceLeafMixin 18 | channel <-chan tla.TLAValue 19 | buffer, backlogBuffer []tla.TLAValue 20 | timeout time.Duration 21 | } 22 | 23 | var _ distsys.ArchetypeResource = &CustomInChan{} 24 | 25 | func NewCustomInChan(ch <-chan tla.TLAValue, timeout time.Duration) distsys.ArchetypeResource { 26 | return &CustomInChan{ 27 | channel: ch, 28 | timeout: timeout, 29 | } 30 | } 31 | 32 | func (res *CustomInChan) Abort() chan struct{} { 33 | res.buffer = append(res.backlogBuffer, res.buffer...) 34 | res.backlogBuffer = nil 35 | return nil 36 | } 37 | 38 | func (res *CustomInChan) PreCommit() chan error { 39 | return nil 40 | } 41 | 42 | func (res *CustomInChan) Commit() chan struct{} { 43 | res.backlogBuffer = nil 44 | return nil 45 | } 46 | 47 | func (res *CustomInChan) ReadValue() (tla.TLAValue, error) { 48 | if len(res.buffer) > 0 { 49 | value := res.buffer[0] 50 | res.buffer = res.buffer[1:] 51 | res.backlogBuffer = append(res.backlogBuffer, value) 52 | return value, nil 53 | } 54 | 55 | select { 56 | case value := <-res.channel: 57 | res.backlogBuffer = append(res.backlogBuffer, value) 58 | return value, nil 59 | case <-time.After(res.timeout): 60 | return tla.TLA_TRUE, nil 61 | } 62 | } 63 | 64 | func (res *CustomInChan) WriteValue(value tla.TLAValue) error { 65 | panic(fmt.Errorf("attempted to write %v to an input channel resource", value)) 66 | } 67 | 68 | func (res *CustomInChan) Close() error { 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /test/files/general/raftkvs.tla.gotests.ignore/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/raftkvs 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require ( 10 | github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 11 | github.com/benbjohnson/immutable v0.4.3 12 | github.com/dgraph-io/badger/v3 v3.2103.5 13 | ) 14 | 15 | require ( 16 | github.com/cespare/xxhash v1.1.0 // indirect 17 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 18 | github.com/dgraph-io/ristretto v0.2.0 // indirect 19 | github.com/dustin/go-humanize v1.0.1 // indirect 20 | github.com/gogo/protobuf v1.3.2 // indirect 21 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 22 | github.com/golang/protobuf v1.5.4 // indirect 23 | github.com/golang/snappy v0.0.4 // indirect 24 | github.com/google/flatbuffers v24.3.25+incompatible // indirect 25 | github.com/klauspost/compress v1.17.11 // indirect 26 | github.com/pkg/errors v0.9.1 // indirect 27 | github.com/segmentio/fasthash v1.0.3 // indirect 28 | go.opencensus.io v0.24.0 // indirect 29 | go.uber.org/multierr v1.11.0 // indirect 30 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 31 | golang.org/x/net v0.31.0 // indirect 32 | golang.org/x/sync v0.9.0 // indirect 33 | golang.org/x/sys v0.27.0 // indirect 34 | google.golang.org/protobuf v1.35.2 // indirect 35 | ) 36 | -------------------------------------------------------------------------------- /test/files/general/raftkvs.tla.gotests.ignore/timer.go: -------------------------------------------------------------------------------- 1 | package raftkvs 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | 7 | "github.com/UBC-NSS/pgo/distsys" 8 | "github.com/UBC-NSS/pgo/distsys/tla" 9 | ) 10 | 11 | func TimerResourceMaker() distsys.ArchetypeResourceMaker { 12 | return distsys.ArchetypeResourceMakerFn(func() distsys.ArchetypeResource { 13 | return &TimerResource{} 14 | }) 15 | } 16 | 17 | func getTimeout() time.Duration { 18 | n := rand.Intn(150) + 150 19 | return time.Duration(n) * time.Millisecond 20 | } 21 | 22 | // TimerResource is used to implement randomized timeout in the Raft leader 23 | // election. It measures the time since the last call to Read and if it's 24 | // greater than some random timeout, then it returns true; otherwise, returns 25 | // false. 26 | type TimerResource struct { 27 | distsys.ArchetypeResourceLeafMixin 28 | timer *time.Timer 29 | } 30 | 31 | func (res *TimerResource) Abort() chan struct{} { 32 | return nil 33 | } 34 | 35 | func (res *TimerResource) PreCommit() chan error { 36 | return nil 37 | } 38 | 39 | func (res *TimerResource) Commit() chan struct{} { 40 | return nil 41 | } 42 | 43 | func (res *TimerResource) ReadValue() (tla.TLAValue, error) { 44 | if res.timer == nil { 45 | res.timer = time.NewTimer(getTimeout()) 46 | return tla.TLA_FALSE, nil 47 | } 48 | select { 49 | case <-res.timer.C: 50 | res.timer.Reset(getTimeout()) 51 | return tla.TLA_TRUE, nil 52 | default: 53 | return tla.TLA_FALSE, nil 54 | } 55 | } 56 | 57 | func (res *TimerResource) WriteValue(value tla.TLAValue) error { 58 | panic("write to timer resource is not allowed") 59 | } 60 | 61 | func (res *TimerResource) Close() error { 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /test/files/gogen/EmptyBlock.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE EmptyBlock ---- 2 | EXTENDS TLC 3 | 4 | (* --mpcal EmptyBlock { } *) 5 | 6 | \* BEGIN TRANSLATION 7 | ==== -------------------------------------------------------------------------------- /test/files/gogen/EmptyBlock.tla.gotests/EmptyBlock.go: -------------------------------------------------------------------------------- 1 | package emptyblock 2 | 3 | import ( 4 | "fmt" 5 | "github.com/UBC-NSS/pgo/distsys" 6 | "github.com/UBC-NSS/pgo/distsys/tla" 7 | ) 8 | 9 | var _ = new(fmt.Stringer) // unconditionally prevent go compiler from reporting unused fmt import 10 | var _ = distsys.ErrDone 11 | var _ = tla.Value{} // same, for tla 12 | 13 | var procTable = distsys.MakeMPCalProcTable() 14 | 15 | var jumpTable = distsys.MakeMPCalJumpTable() 16 | -------------------------------------------------------------------------------- /test/files/gogen/EmptyBlock.tla.gotests/emptyblock_test.go: -------------------------------------------------------------------------------- 1 | package emptyblock 2 | 3 | import "testing" 4 | 5 | func TestNothing(t *testing.T) { 6 | t.Skipf("Nothing to test. This just has to compile; the generated code should be fundamentally empty.") 7 | } 8 | -------------------------------------------------------------------------------- /test/files/gogen/EmptyBlock.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/emptyblock 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/gogen/EmptyBlock.tla.gotests/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= 2 | github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= 9 | github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 16 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 17 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 18 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 19 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= 20 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /test/files/gogen/UnsupportedOps.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE UnsupportedOps ---- 2 | EXTENDS Sequences, FiniteSets, Integers, Bags 3 | 4 | MyOperator == (*:: expectedError: UnsupportedOperationError *) SetToBag({}) 5 | 6 | (* 7 | --mpcal NoFirstLabel { 8 | define { 9 | MyOperator2 == (*:: expectedError: UnsupportedOperationError *) SetToBag({}) 10 | } 11 | 12 | procedure MPCalProc(a) { 13 | lbl: a := (*:: expectedError: UnsupportedOperationError *) SetToBag({}); 14 | } 15 | archetype MyArchetype(ref a) { 16 | lbl: a := (*:: expectedError: UnsupportedOperationError *) SetToBag({}); 17 | } 18 | procedure PCalProc(a) { 19 | lbl: a := SetToBag({}); 20 | } 21 | 22 | process (Proc = 1) 23 | variable a; 24 | { 25 | lbl: a := SetToBag({}); 26 | } 27 | } 28 | *) 29 | \* BEGIN TRANSLATION 30 | ==== -------------------------------------------------------------------------------- /test/files/gogen/bug_167.tla.gotests/go.mod: -------------------------------------------------------------------------------- 1 | module example.org/pbkvs 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.23.2 6 | 7 | replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys 8 | 9 | require github.com/UBC-NSS/pgo/distsys v0.0.0-20241115155132-ac5da2a2c7c6 10 | 11 | require ( 12 | github.com/benbjohnson/immutable v0.4.3 // indirect 13 | github.com/segmentio/fasthash v1.0.3 // indirect 14 | github.com/stretchr/testify v1.8.1 // indirect 15 | go.uber.org/multierr v1.11.0 // indirect 16 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /test/files/gogen/bug_167.tla.gotests/go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/immutable v0.4.3 h1:GYHcksoJ9K6HyAUpGxwZURrbTkXA0Dh4otXGqbhdrjA= 2 | github.com/benbjohnson/immutable v0.4.3/go.mod h1:qJIKKSmdqz1tVzNtst1DZzvaqOU1onk1rc03IeM3Owk= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= 9 | github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 16 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 17 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 18 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 19 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= 20 | golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /test/files/pcalgen/AssignmentRulesNoMultipleWrites.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE AssignmentRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal AssignmentRules { 5 | macro MyMacro(x) { 6 | x := 3; 7 | y := 4; 8 | } 9 | 10 | procedure Proc2() 11 | variables x, y; 12 | { 13 | l1: MyMacro(x); 14 | l2: MyMacro(x); MyMacro(x); 15 | l3: MyMacro(x); y := y + 1; 16 | l4: MyMacro(x); x := -1; 17 | } 18 | 19 | procedure MyProcedure(x, y) 20 | variables x, y; 21 | { 22 | p: either { y := 10 } or { skip }; 23 | y := 11; (* missing label *) 24 | p2: y := 20; 25 | x := y || y := x; (* swap x and y: invalid *) 26 | } 27 | 28 | archetype MyArchetype(ref x) 29 | variables x; 30 | { 31 | a1: x := 10; 32 | x := 11; (* missing label *) 33 | } 34 | 35 | process (MyProcess = 23) 36 | variables n; 37 | { 38 | l1: n := 2; 39 | l2: while (n < 10) { 40 | n := 12; 41 | if (n = 20) { 42 | n := 100; (* missing label *) 43 | } 44 | }; 45 | n := 32; (* label not missing *) 46 | 47 | l3: if (n = 32) { 48 | n := 0; 49 | }; 50 | n := 12; (* missing label *) 51 | } 52 | } 53 | 54 | \* BEGIN PLUSCAL TRANSLATION 55 | 56 | \* END PLUSCAL TRANSLATION 57 | 58 | *) 59 | \* BEGIN TRANSLATION 60 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/AssignmentRulesNoMultipleWrites.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE AssignmentRules ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | (* 4 | --mpcal AssignmentRules { 5 | macro MyMacro(x) { 6 | x := 3; 7 | y := 4; 8 | } 9 | 10 | procedure Proc2() 11 | variables x, y; 12 | { 13 | l1: MyMacro(x); 14 | l2: MyMacro(x); MyMacro(x); 15 | l3: MyMacro(x); y := y + 1; 16 | l4: MyMacro(x); x := -1; 17 | } 18 | 19 | procedure MyProcedure(x, y) 20 | variables x, y; 21 | { 22 | p: either { y := 10 } or { skip }; 23 | y := 11; (* missing label *) 24 | p2: y := 20; 25 | x := y || y := x; (* swap x and y: invalid *) 26 | } 27 | 28 | archetype MyArchetype(ref x) 29 | variables x; 30 | { 31 | a1: x := 10; 32 | x := 11; (* missing label *) 33 | } 34 | 35 | process (MyProcess = 23) 36 | variables n; 37 | { 38 | l1: n := 2; 39 | l2: while (n < 10) { 40 | n := 12; 41 | if (n = 20) { 42 | n := 100; (* missing label *) 43 | } 44 | }; 45 | n := 32; (* label not missing *) 46 | 47 | l3: if (n = 32) { 48 | n := 0; 49 | }; 50 | n := 12; (* missing label *) 51 | } 52 | } 53 | 54 | \* BEGIN PLUSCAL TRANSLATION 55 | --algorithm AssignmentRules { 56 | 57 | process (MyProcess = 23) 58 | variables n; 59 | { 60 | l1: 61 | n := 2; 62 | goto l2; 63 | l2: 64 | if ((n) < (10)) { 65 | with (n0 = 12) { 66 | if ((n0) = (20)) { 67 | n := 100; 68 | goto l2; 69 | } else { 70 | n := n0; 71 | goto l2; 72 | }; 73 | }; 74 | } else { 75 | n := 32; 76 | goto l3; 77 | }; 78 | l3: 79 | if ((n) = (32)) { 80 | with (n1 = 0) { 81 | n := 12; 82 | goto Done; 83 | }; 84 | } else { 85 | n := 12; 86 | goto Done; 87 | }; 88 | } 89 | } 90 | 91 | \* END PLUSCAL TRANSLATION 92 | 93 | *) 94 | \* BEGIN TRANSLATION 95 | ==== 96 | -------------------------------------------------------------------------------- /test/files/pcalgen/BadNonRecursiveScoping1.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE BadNonRecursiveScoping1 ---- 2 | 3 | Foo == (*:: expectedError+3: ParseFailureError *) Foo 4 | 5 | \* BEGIN TRANSLATION 6 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/BadNonRecursiveScoping2.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE BadNonRecursiveScoping2 ---- 2 | 3 | Bar == (*:: expectedError+3: ParseFailureError *) Foo 4 | 5 | Foo == 12 6 | 7 | \* BEGIN TRANSLATION 8 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/BadPCalMarkerPlacementBeginMissing.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE BadPCalMarkerPlacementBeginMissing ---- 2 | EXTENDS TLC 3 | 4 | (* --mpcal BadPCalMarkerPlacementBeginMissing { 5 | process (Nothing = 0) { 6 | lbl: skip; 7 | } 8 | } *) 9 | 10 | \* +3 because it's not worth the pain of trying to get the error to _not_ point to END vs. the \* part of the comment 11 | \*:: expectedError+3: PCalTagsError 12 | \* END PLUSCAL TRANSLATION 13 | 14 | \* BEGIN TRANSLATION 15 | 16 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/BadPCalMarkerPlacementBothMissing.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE BadPCalMarkerPlacementBothMissing ---- 2 | EXTENDS TLC 3 | 4 | (* --mpcal BadPCalMarkerPlacementBothMissing { 5 | process (Nothing = 0) { 6 | lbl: skip; 7 | } 8 | } *) 9 | 10 | \* BEGIN TRANSLATION 11 | 12 | ==== \*:: expectedError: PCalTagsError -------------------------------------------------------------------------------- /test/files/pcalgen/BadPCalMarkerPlacementEndMissing.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE BadPCalMarkerPlacementEndMissing ---- 2 | EXTENDS TLC 3 | 4 | (* --mpcal BadPCalMarkerPlacementEndMissing { 5 | process (Nothing = 0) { 6 | lbl: skip; 7 | } 8 | } *) 9 | 10 | \* BEGIN PLUSCAL TRANSLATION 11 | 12 | \* BEGIN TRANSLATION 13 | 14 | ==== \*:: expectedError: PCalTagsError -------------------------------------------------------------------------------- /test/files/pcalgen/BadPCalMarkerPlacementSwapped.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE BadPCalMarkerPlacementSwapped ---- 2 | EXTENDS TLC 3 | 4 | (* --mpcal BadPCalMarkerPlacementSwapped { 5 | process (Nothing = 0) { 6 | lbl: skip; 7 | } 8 | } *) 9 | 10 | \* +3 because it's not worth the pain of trying to get the error to _not_ point to END vs. the \* part of the comment 11 | \*:: expectedError+3: PCalTagsError 12 | \* END PLUSCAL TRANSLATION 13 | \* BEGIN PLUSCAL TRANSLATION 14 | 15 | \* BEGIN TRANSLATION 16 | 17 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/EmptyBlock.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE EmptyBlock ---- 2 | EXTENDS TLC 3 | 4 | \* the only pcalgen-specific error! gogen can handle this, 5 | \* but it's fundamentally invalid for a PCal algorithm to have no processes 6 | 7 | (* (*:: expectedError: MPCalEffectivelyNoProcesses *) --mpcal EmptyBlock { } *) 8 | 9 | \* BEGIN TRANSLATION 10 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/MPCalInstanceClauseSelf.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MPCalInstanceClauseSelf ---- 2 | EXTENDS Sequences, TLC, FiniteSets 3 | 4 | (* 5 | --mpcal MPCalInstanceClauseSelf { 6 | archetype Foo(x) { 7 | lbl: skip; 8 | } 9 | 10 | process (Bar = 42) == instance Foo(Bar); 11 | } 12 | 13 | \* BEGIN PLUSCAL TRANSLATION 14 | 15 | \* END PLUSCAL TRANSLATION 16 | 17 | *) 18 | 19 | \* BEGIN TRANSLATION 20 | 21 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/MPCalInstanceClauseSelf.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE MPCalInstanceClauseSelf ---- 2 | EXTENDS Sequences, TLC, FiniteSets 3 | 4 | (* 5 | --mpcal MPCalInstanceClauseSelf { 6 | archetype Foo(x) { 7 | lbl: skip; 8 | } 9 | 10 | process (Bar = 42) == instance Foo(Bar); 11 | } 12 | 13 | \* BEGIN PLUSCAL TRANSLATION 14 | --algorithm MPCalInstanceClauseSelf { 15 | 16 | process (Bar = 42) 17 | variables x = self; 18 | { 19 | lbl: 20 | skip; 21 | goto Done; 22 | } 23 | } 24 | 25 | \* END PLUSCAL TRANSLATION 26 | 27 | *) 28 | 29 | \* BEGIN TRANSLATION 30 | 31 | ==== 32 | -------------------------------------------------------------------------------- /test/files/pcalgen/MPCalScopeBleed.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MPCalScopeBleed ---- 2 | EXTENDS TLC, Integers 3 | 4 | (* --mpcal MPCalScopeBleed { 5 | 6 | archetype Arch() { 7 | lbl3: skip; 8 | } 9 | 10 | procedure P1(x) 11 | variables y; 12 | { 13 | lbl1: 14 | x := y + z; 15 | } 16 | 17 | process (P2 = 1) 18 | variables z; 19 | { 20 | lbl2: 21 | x := y + z; 22 | } 23 | 24 | } *) 25 | 26 | (* 27 | 28 | \* BEGIN PLUSCAL TRANSLATION 29 | 30 | \* END PLUSCAL TRANSLATION 31 | 32 | *) 33 | 34 | 35 | \* BEGIN TRANSLATION 36 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/MPCalScopeBleed.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE MPCalScopeBleed ---- 2 | EXTENDS TLC, Integers 3 | 4 | (* --mpcal MPCalScopeBleed { 5 | 6 | archetype Arch() { 7 | lbl3: skip; 8 | } 9 | 10 | procedure P1(x) 11 | variables y; 12 | { 13 | lbl1: 14 | x := y + z; 15 | } 16 | 17 | process (P2 = 1) 18 | variables z; 19 | { 20 | lbl2: 21 | x := y + z; 22 | } 23 | 24 | } *) 25 | 26 | (* 27 | 28 | \* BEGIN PLUSCAL TRANSLATION 29 | --algorithm MPCalScopeBleed { 30 | 31 | procedure P1(x) 32 | variables y; 33 | { 34 | lbl1: 35 | x := (y) + (z); 36 | goto Error; 37 | } 38 | 39 | process (P2 = 1) 40 | variables z; 41 | { 42 | lbl2: 43 | x := (y) + (z); 44 | goto Done; 45 | } 46 | } 47 | 48 | \* END PLUSCAL TRANSLATION 49 | 50 | *) 51 | 52 | 53 | \* BEGIN TRANSLATION 54 | ==== 55 | -------------------------------------------------------------------------------- /test/files/pcalgen/MappingMacroDeferredScoping.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MappingMacroDeferredScoping ---- 2 | EXTENDS TLC, Integers 3 | 4 | (* --mpcal MappingMacroDeferredScoping { 5 | 6 | mapping macro M { 7 | read { 8 | yield $variable + (*:: expectedError: DefinitionLookupError *) N; 9 | } 10 | write { 11 | yield $value - N; 12 | } 13 | } 14 | 15 | archetype Arch(ref p) { 16 | lbl: p := p + 1; 17 | } 18 | 19 | variables pp = 1; 20 | 21 | process (Proc = 1) == instance Arch(ref pp) 22 | mapping pp via M; 23 | } 24 | 25 | \* BEGIN PLUSCAL TRANSLATION 26 | 27 | \* END PLUSCAL TRANSLATION 28 | 29 | *) 30 | 31 | \* BEGIN TRANSLATION 32 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/MappingMacroNestedWithExpansion.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MappingMacroNestedWithExpansion ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | 4 | (* --mpcal MappingMacroNestedWithExpansion { 5 | 6 | mapping macro IdentityWith { 7 | read { 8 | with (x = $variable) { 9 | yield x; 10 | } 11 | } 12 | write { 13 | with (y = $value) { 14 | yield y; 15 | } 16 | } 17 | } 18 | 19 | archetype Arch(ref foo) { 20 | lbl: 21 | foo := foo + 1; 22 | foo := 2 + foo; 23 | } 24 | 25 | variable bar; 26 | 27 | process (Proc = 1) == instance Arch(ref bar) 28 | mapping bar via IdentityWith; 29 | 30 | } *) 31 | 32 | (* 33 | \* BEGIN PLUSCAL TRANSLATION 34 | 35 | \* END PLUSCAL TRANSLATION 36 | *) 37 | 38 | \* BEGIN TRANSLATION 39 | 40 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/MappingMacroNestedWithExpansion.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE MappingMacroNestedWithExpansion ---- 2 | EXTENDS Sequences, FiniteSets, Integers 3 | 4 | (* --mpcal MappingMacroNestedWithExpansion { 5 | 6 | mapping macro IdentityWith { 7 | read { 8 | with (x = $variable) { 9 | yield x; 10 | } 11 | } 12 | write { 13 | with (y = $value) { 14 | yield y; 15 | } 16 | } 17 | } 18 | 19 | archetype Arch(ref foo) { 20 | lbl: 21 | foo := foo + 1; 22 | foo := 2 + foo; 23 | } 24 | 25 | variable bar; 26 | 27 | process (Proc = 1) == instance Arch(ref bar) 28 | mapping bar via IdentityWith; 29 | 30 | } *) 31 | 32 | (* 33 | \* BEGIN PLUSCAL TRANSLATION 34 | --algorithm MappingMacroNestedWithExpansion { 35 | variables bar; 36 | 37 | process (Proc = 1) 38 | { 39 | lbl: 40 | with ( 41 | x00 = bar, 42 | yielded_bar1 = x00, 43 | value1 = (yielded_bar1) + (1), 44 | y00 = value1, 45 | bar0 = y00, 46 | x10 = bar0, 47 | yielded_bar00 = x10, 48 | value00 = (2) + (yielded_bar00), 49 | y10 = value00 50 | ) { 51 | bar := y10; 52 | goto Done; 53 | }; 54 | } 55 | } 56 | 57 | \* END PLUSCAL TRANSLATION 58 | *) 59 | 60 | \* BEGIN TRANSLATION 61 | 62 | ==== 63 | -------------------------------------------------------------------------------- /test/files/pcalgen/MappingMacroRWExpansion.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE func ------------------------------- 2 | 3 | EXTENDS Naturals, Sequences, TLC, FiniteSets 4 | 5 | (******************** 6 | 7 | --mpcal func { 8 | 9 | mapping macro MP { 10 | read { 11 | yield $variable; 12 | } 13 | 14 | write { 15 | yield $value; 16 | } 17 | } 18 | 19 | archetype ANode(ref arr[_]) { 20 | lbl1: 21 | (* this has the "wrong" number of indices, and implies: 22 | - read arr[1] (call it x) 23 | - write [x EXCEPT ![2] = "a"] to arr[1] 24 | 25 | this is hard to keep track of, so it's forbidden instead *) 26 | (*:: expectedError: MPCalReadWriteAssignmentForbidden *) arr[1][2] := "a"; 27 | 28 | lbl2: 29 | print arr[1][2]; 30 | } 31 | 32 | variable 33 | arr = [d1 \in {1} |-> [d2 \in {2} |-> ""]]; 34 | 35 | fair process (Node \in {1}) == instance ANode(ref arr[_]) 36 | mapping arr[_] via MP; 37 | } 38 | 39 | ********************) 40 | 41 | \* BEGIN TRANSLATION 42 | 43 | ============================================================================= 44 | -------------------------------------------------------------------------------- /test/files/pcalgen/ModuleWithoutExtends.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ModuleWithoutExtends ---- 2 | 3 | Foo == 1 4 | 5 | (* --mpcal ModuleWithoutExtends { 6 | 7 | process (Nil = 0) { 8 | lbl: skip; 9 | } 10 | } 11 | 12 | \* BEGIN PLUSCAL TRANSLATION 13 | 14 | \* END PLUSCAL TRANSLATION 15 | 16 | *) 17 | 18 | \* BEGIN TRANSLATION 19 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/ModuleWithoutExtends.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE ModuleWithoutExtends ---- 2 | 3 | Foo == 1 4 | 5 | (* --mpcal ModuleWithoutExtends { 6 | 7 | process (Nil = 0) { 8 | lbl: skip; 9 | } 10 | } 11 | 12 | \* BEGIN PLUSCAL TRANSLATION 13 | --algorithm ModuleWithoutExtends { 14 | 15 | process (Nil = 0) 16 | { 17 | lbl: 18 | skip; 19 | goto Done; 20 | } 21 | } 22 | 23 | \* END PLUSCAL TRANSLATION 24 | 25 | *) 26 | 27 | \* BEGIN TRANSLATION 28 | ==== 29 | -------------------------------------------------------------------------------- /test/files/pcalgen/MultiWithSemantics.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE MultiWithSemantics ---- 2 | EXTENDS TLC, Integers, Sequences 3 | 4 | (* --mpcal MultiWithSemantics { 5 | 6 | mapping macro Inc { 7 | read { 8 | with(v = $variable) { 9 | $variable := $variable + 1; 10 | yield v; 11 | }; 12 | } 13 | write { 14 | assert FALSE; 15 | } 16 | } 17 | 18 | archetype AnExample(ref foo) { 19 | lbl: 20 | with(x = foo, y = foo + x) { 21 | print y; 22 | }; 23 | } 24 | 25 | variables gFoo = 0; 26 | 27 | fair process (Example = 1) == instance AnExample(ref gFoo) 28 | mapping gFoo via Inc; 29 | 30 | } *) 31 | 32 | (* 33 | 34 | \* BEGIN PLUSCAL TRANSLATION 35 | 36 | \* END PLUSCAL TRANSLATION 37 | 38 | *) 39 | 40 | \* BEGIN TRANSLATION 41 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/MultiWithSemantics.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE MultiWithSemantics ---- 2 | EXTENDS TLC, Integers, Sequences 3 | 4 | (* --mpcal MultiWithSemantics { 5 | 6 | mapping macro Inc { 7 | read { 8 | with(v = $variable) { 9 | $variable := $variable + 1; 10 | yield v; 11 | }; 12 | } 13 | write { 14 | assert FALSE; 15 | } 16 | } 17 | 18 | archetype AnExample(ref foo) { 19 | lbl: 20 | with(x = foo, y = foo + x) { 21 | print y; 22 | }; 23 | } 24 | 25 | variables gFoo = 0; 26 | 27 | fair process (Example = 1) == instance AnExample(ref gFoo) 28 | mapping gFoo via Inc; 29 | 30 | } *) 31 | 32 | (* 33 | 34 | \* BEGIN PLUSCAL TRANSLATION 35 | --algorithm MultiWithSemantics { 36 | variables gFoo = 0; 37 | 38 | fair process (Example = 1) 39 | { 40 | lbl: 41 | with ( 42 | v1 = gFoo, 43 | gFoo0 = (gFoo) + (1), 44 | yielded_gFoo0 = v1, 45 | x = yielded_gFoo0, 46 | v0 = gFoo0 47 | ) { 48 | gFoo := (gFoo0) + (1); 49 | with ( 50 | yielded_gFoo = v0, 51 | y = (yielded_gFoo) + (x) 52 | ) { 53 | print y; 54 | goto Done; 55 | }; 56 | }; 57 | } 58 | } 59 | 60 | \* END PLUSCAL TRANSLATION 61 | 62 | *) 63 | 64 | \* BEGIN TRANSLATION 65 | ==== 66 | -------------------------------------------------------------------------------- /test/files/pcalgen/ResourceAccessBoundByWith.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE ResourceAccessBoundByWith ---- 2 | EXTENDS TLC, Integers 3 | 4 | (* 5 | --mpcal ResourceAccessBoundByWith { 6 | 7 | mapping macro MM { 8 | read { 9 | with (foo = $variable + 1) { 10 | yield foo; 11 | }; 12 | } 13 | write { 14 | yield $value; 15 | } 16 | } 17 | 18 | archetype AFoo(ref x) 19 | variables z; 20 | { 21 | lbl: 22 | either { 23 | with(y = x) { 24 | print y; 25 | }; 26 | } or { 27 | skip; 28 | }; 29 | goto lbl; 30 | } 31 | 32 | variables x; 33 | 34 | process (Foo = 1) == instance AFoo(ref x) 35 | mapping x via MM; 36 | } 37 | 38 | \* BEGIN PLUSCAL TRANSLATION 39 | 40 | \* END PLUSCAL TRANSLATION 41 | *) 42 | 43 | \* BEGIN TRANSLATION 44 | ==== 45 | -------------------------------------------------------------------------------- /test/files/pcalgen/ResourceAccessBoundByWith.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE ResourceAccessBoundByWith ---- 2 | EXTENDS TLC, Integers 3 | 4 | (* 5 | --mpcal ResourceAccessBoundByWith { 6 | 7 | mapping macro MM { 8 | read { 9 | with (foo = $variable + 1) { 10 | yield foo; 11 | }; 12 | } 13 | write { 14 | yield $value; 15 | } 16 | } 17 | 18 | archetype AFoo(ref x) 19 | variables z; 20 | { 21 | lbl: 22 | either { 23 | with(y = x) { 24 | print y; 25 | }; 26 | } or { 27 | skip; 28 | }; 29 | goto lbl; 30 | } 31 | 32 | variables x; 33 | 34 | process (Foo = 1) == instance AFoo(ref x) 35 | mapping x via MM; 36 | } 37 | 38 | \* BEGIN PLUSCAL TRANSLATION 39 | --algorithm ResourceAccessBoundByWith { 40 | variables x; 41 | 42 | process (Foo = 1) 43 | variables z; 44 | { 45 | lbl: 46 | either { 47 | with ( 48 | foo00 = (x) + (1), 49 | yielded_x0 = foo00, 50 | y1 = yielded_x0 51 | ) { 52 | print y1; 53 | goto lbl; 54 | }; 55 | } or { 56 | skip; 57 | goto lbl; 58 | }; 59 | } 60 | } 61 | 62 | \* END PLUSCAL TRANSLATION 63 | *) 64 | 65 | \* BEGIN TRANSLATION 66 | ==== 67 | -------------------------------------------------------------------------------- /test/files/pcalgen/StringBracketBug.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE StringBracketBug ---- 2 | 3 | (* 4 | 5 | --mpcal StringBracketBug { 6 | archetype Foo() { 7 | lbl: print "a string (with brackets)"; 8 | } 9 | 10 | process (Bar = 1) == instance Foo(); 11 | } 12 | 13 | \* BEGIN PLUSCAL TRANSLATION 14 | 15 | \* END PLUSCAL TRANSLATION 16 | 17 | *) 18 | 19 | \* BEGIN TRANSLATION 20 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/StringBracketBug.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE StringBracketBug ---- 2 | 3 | (* 4 | 5 | --mpcal StringBracketBug { 6 | archetype Foo() { 7 | lbl: print "a string (with brackets)"; 8 | } 9 | 10 | process (Bar = 1) == instance Foo(); 11 | } 12 | 13 | \* BEGIN PLUSCAL TRANSLATION 14 | --algorithm StringBracketBug { 15 | 16 | process (Bar = 1) 17 | { 18 | lbl: 19 | print "a string (with brackets)"; 20 | goto Done; 21 | } 22 | } 23 | 24 | \* END PLUSCAL TRANSLATION 25 | 26 | *) 27 | 28 | \* BEGIN TRANSLATION 29 | ==== 30 | -------------------------------------------------------------------------------- /test/files/pcalgen/TLAUnitsHorizontalLine.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE TLAUnitsHorizontalLine ---- 2 | EXTENDS TLC 3 | 4 | Unit1 == 1 5 | ---- 6 | Unit2 == 2 7 | 8 | (* --mpcal TLAUnitsHorizontalLine { 9 | 10 | process (Pass = 1) { 11 | lbl: skip; 12 | } 13 | } 14 | 15 | \* BEGIN PLUSCAL TRANSLATION 16 | 17 | \* END PLUSCAL TRANSLATION 18 | *) 19 | 20 | \* BEGIN TRANSLATION 21 | 22 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/TLAUnitsHorizontalLine.tla.expectpcal: -------------------------------------------------------------------------------- 1 | ---- MODULE TLAUnitsHorizontalLine ---- 2 | EXTENDS TLC 3 | 4 | Unit1 == 1 5 | ---- 6 | Unit2 == 2 7 | 8 | (* --mpcal TLAUnitsHorizontalLine { 9 | 10 | process (Pass = 1) { 11 | lbl: skip; 12 | } 13 | } 14 | 15 | \* BEGIN PLUSCAL TRANSLATION 16 | --algorithm TLAUnitsHorizontalLine { 17 | 18 | process (Pass = 1) 19 | { 20 | lbl: 21 | skip; 22 | goto Done; 23 | } 24 | } 25 | 26 | \* END PLUSCAL TRANSLATION 27 | *) 28 | 29 | \* BEGIN TRANSLATION 30 | 31 | ==== 32 | -------------------------------------------------------------------------------- /test/files/pcalgen/UnboundRecursive.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE UnboundRecursive ---- 2 | EXTENDS TLC, Integers 3 | 4 | RECURSIVE (*:: expectedError: UnboundRecursiveDeclError *) Foo(_, _) 5 | 6 | \* BEGIN TRANSLATION 7 | ==== -------------------------------------------------------------------------------- /test/files/pcalgen/bug_195_simple.tla: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE aworset -------------------------------- 2 | 3 | EXTENDS Naturals, Sequences, TLC, FiniteSets 4 | 5 | (* 6 | 7 | --mpcal aworset { 8 | 9 | variables v; 10 | 11 | \* this is a test of multi-assignment, specifically compound expressions 12 | 13 | process (P = 1) { 14 | lbl: 15 | v[0] := 1; 16 | if(TRUE) { 17 | v[1] := 2; 18 | v[2] := 3; 19 | }; 20 | if(TRUE) { 21 | v[3] := 4; 22 | v[4] := 5; 23 | v[5] := 6; 24 | }; 25 | } 26 | 27 | } 28 | 29 | \* BEGIN PLUSCAL TRANSLATION 30 | 31 | \* END PLUSCAL TRANSLATION 32 | 33 | *) 34 | 35 | \* BEGIN TRANSLATION 36 | 37 | ==== 38 | -------------------------------------------------------------------------------- /test/files/pcalgen/bug_195_simple.tla.expectpcal: -------------------------------------------------------------------------------- 1 | -------------------------------- MODULE aworset -------------------------------- 2 | 3 | EXTENDS Naturals, Sequences, TLC, FiniteSets 4 | 5 | (* 6 | 7 | --mpcal aworset { 8 | 9 | variables v; 10 | 11 | \* this is a test of multi-assignment, specifically compound expressions 12 | 13 | process (P = 1) { 14 | lbl: 15 | v[0] := 1; 16 | if(TRUE) { 17 | v[1] := 2; 18 | v[2] := 3; 19 | }; 20 | if(TRUE) { 21 | v[3] := 4; 22 | v[4] := 5; 23 | v[5] := 6; 24 | }; 25 | } 26 | 27 | } 28 | 29 | \* BEGIN PLUSCAL TRANSLATION 30 | --algorithm aworset { 31 | variables v; 32 | 33 | process (P = 1) 34 | { 35 | lbl: 36 | with (v0 = [v EXCEPT ![0] = 1]) { 37 | if (TRUE) { 38 | with ( 39 | v1 = [v0 EXCEPT ![1] = 2], 40 | v2 = [v1 EXCEPT ![2] = 3] 41 | ) { 42 | if (TRUE) { 43 | with ( 44 | v3 = [v2 EXCEPT ![3] = 4], 45 | v4 = [v3 EXCEPT ![4] = 5] 46 | ) { 47 | v := [v4 EXCEPT ![5] = 6]; 48 | goto Done; 49 | }; 50 | } else { 51 | v := v2; 52 | goto Done; 53 | }; 54 | }; 55 | } else { 56 | if (TRUE) { 57 | with ( 58 | v5 = [v0 EXCEPT ![3] = 4], 59 | v6 = [v5 EXCEPT ![4] = 5] 60 | ) { 61 | v := [v6 EXCEPT ![5] = 6]; 62 | goto Done; 63 | }; 64 | } else { 65 | v := v0; 66 | goto Done; 67 | }; 68 | }; 69 | }; 70 | } 71 | } 72 | 73 | \* END PLUSCAL TRANSLATION 74 | 75 | *) 76 | 77 | \* BEGIN TRANSLATION 78 | 79 | ==== 80 | -------------------------------------------------------------------------------- /test/files/tla/Euclid.tla: -------------------------------------------------------------------------------- 1 | ------------------------ MODULE Euclid ---------------------------- 2 | EXTENDS Naturals, TLC 3 | CONSTANT N 4 | 5 | (* 6 | --algorithm Euclid { 7 | variables u = 24; 8 | v \in 1 .. N; 9 | v_init = v; 10 | { 11 | a: while (u # 0) { 12 | if (u < v) { 13 | u := v || v := u; 14 | }; 15 | b: u := u - v; 16 | }; 17 | print <<24, v_init, "have gcd", v>> 18 | } 19 | } 20 | *) 21 | \* BEGIN TRANSLATION 22 | VARIABLES u, v, v_init, pc 23 | 24 | vars == << u, v, v_init, pc >> 25 | 26 | Init == (* Global variables *) 27 | /\ u = 24 28 | /\ v \in 1 .. N 29 | /\ v_init = v 30 | /\ pc = "a" 31 | 32 | a == /\ pc = "a" 33 | /\ IF u # 0 34 | THEN /\ IF u < v 35 | THEN /\ /\ u' = v 36 | /\ v' = u 37 | ELSE /\ TRUE 38 | /\ UNCHANGED << u, v >> 39 | /\ pc' = "b" 40 | ELSE /\ PrintT(<<24, v_init, "have gcd", v>>) 41 | /\ pc' = "Done" 42 | /\ UNCHANGED << u, v >> 43 | /\ UNCHANGED v_init 44 | 45 | b == /\ pc = "b" 46 | /\ u' = u - v 47 | /\ pc' = "a" 48 | /\ UNCHANGED << v, v_init >> 49 | 50 | Next == a \/ b 51 | \/ (* Disjunct to prevent deadlock on termination *) 52 | (pc = "Done" /\ UNCHANGED vars) 53 | 54 | Spec == Init /\ [][Next]_vars 55 | 56 | Termination == <>(pc = "Done") 57 | 58 | \* END TRANSLATION 59 | =================================================================== 60 | -------------------------------------------------------------------------------- /test/pgo/DistsysTests.scala: -------------------------------------------------------------------------------- 1 | package pgo 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | 5 | class DistsysTests extends AnyFunSuite { 6 | test("distsys module tests") { 7 | os.proc("go", "test").call(cwd = os.pwd / "distsys") 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/pgo/PCalReadWriteTests.scala: -------------------------------------------------------------------------------- 1 | package pgo 2 | 3 | import org.scalactic.source.Position 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import pgo.util.Description 6 | import pgo.model.SourceLocation 7 | import pgo.parser.{PCalParser, TLAParser} 8 | import Description._ 9 | import pgo.trans.PCalRenderPass 10 | 11 | class PCalReadWriteTests extends AnyFunSuite { 12 | def check(path: os.Path)(implicit pos: Position): Unit = { 13 | test(path.relativeTo(os.pwd).toString()) { 14 | val underlying = new SourceLocation.UnderlyingFile(path) 15 | val fileContents = os.read(path) 16 | withClue(s"original file:\n$fileContents") { 17 | val tlaModule = TLAParser.readModuleBeforeTranslation( 18 | underlying = underlying, seq = fileContents) 19 | val pcalAlgorithm = PCalParser.readAlgorithm( 20 | underlying = underlying, contents = fileContents, tlaModule = tlaModule) 21 | 22 | val renderedContentsLines = d"(*\n${PCalRenderPass(pcalAlgorithm)}\n*)" 23 | .linesIterator.toBuffer 24 | val renderedContents = renderedContentsLines.mkString("\n") 25 | 26 | withClue(d"rendered file:\n$renderedContents") { 27 | val reparsedAlgorithm = PCalParser.readAlgorithm( 28 | new SourceLocation.UnderlyingString(renderedContents), renderedContents, tlaModule) 29 | 30 | assert(pcalAlgorithm == reparsedAlgorithm) 31 | } 32 | } 33 | } 34 | } 35 | 36 | locally { 37 | val tlaDir = os.pwd / "test" / "files" / "tla" 38 | check(tlaDir / "Queens.tla") 39 | check(tlaDir / "Euclid.tla") 40 | check(tlaDir / "pgo2pc.tla") 41 | check(tlaDir / "2pc.tla") 42 | check(tlaDir / "round_robin.tla") 43 | check(tlaDir / "counter.tla") 44 | check(tlaDir / "DijkstraMutex.tla") 45 | } 46 | 47 | def checkWholeFolder(folder: os.Path): Unit = { 48 | os.list.stream(folder) 49 | .filter(_.last.endsWith(".tla.expectpcal")) 50 | .foreach(check) 51 | } 52 | 53 | checkWholeFolder(os.pwd / "test" / "files" / "general") 54 | checkWholeFolder(os.pwd / "test" / "files" / "pcalgen") 55 | } 56 | -------------------------------------------------------------------------------- /test/pgo/TLAExpressionFuzzTests.scala: -------------------------------------------------------------------------------- 1 | package pgo 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | import pgo.util.TLAExpressionFuzzTestUtils 5 | 6 | class TLAExpressionFuzzTests extends AnyFunSuite with TLAExpressionFuzzTestUtils { 7 | test("TLA+ expr eval (true random ASTs)") { 8 | val result = runExpressionFuzzTesting() 9 | withClue(pprint.apply(result)) { 10 | assert(result.success) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/pgo/TLCTests.scala: -------------------------------------------------------------------------------- 1 | package pgo 2 | 3 | import org.scalatest.tagobjects.Slow 4 | 5 | class TLCTests extends FileTestSuite { 6 | private val systemFiles = os.list.stream(os.pwd / "systems") 7 | .filter(os.isDir) 8 | .map(folder => os.list.stream(folder)) 9 | .flatMap(_.find(_.last.endsWith(".tla"))) 10 | .toList 11 | 12 | override val testFiles: List[os.Path] = systemFiles 13 | 14 | private val javaBin = os.Path(System.getProperty("java.home"), os.pwd) / "bin" / "java" 15 | private val tla2Tools = os.pwd / "tools" / "tla2tools.jar" 16 | 17 | def runTLAMake(testFile: os.Path, args: os.Shellable*): Unit = { 18 | os.proc("make", s"JAVA=$javaBin", s"TLA2TOOLS_JAR=$tla2Tools", args) 19 | .call(cwd = testFile / os.up) 20 | } 21 | 22 | def setupForTLC(testFile: os.Path): Unit = { 23 | val goTestsDir = testFile / os.up 24 | val outFile = goTestsDir / s"${testFile.last}.outpcal" 25 | 26 | os.copy.over(from = testFile, to = outFile) 27 | 28 | val errors = PGo.run(Seq("pcalgen", "-s", outFile.toString())) 29 | checkErrors(errors, testFile) 30 | assert(errors.isEmpty) 31 | os.move.over(from = outFile, to = testFile) 32 | 33 | runTLAMake(testFile, "tlaplusgen") 34 | runTLAMake(testFile, "sany") 35 | } 36 | 37 | // testFiles.foreach { testFile => 38 | // test(s"tlc sim ${testFile.relativeTo(os.pwd)}", Slow) { 39 | // setupForTLC(testFile) 40 | // runTLAMake(testFile, "sim") 41 | // } 42 | 43 | // test(s"tlc mc ${testFile.relativeTo(os.pwd)}", Slow) { 44 | // setupForTLC(testFile) 45 | // runTLAMake(testFile, "mc") 46 | // } 47 | // } 48 | } 49 | -------------------------------------------------------------------------------- /test/traces/hello_config.sc: -------------------------------------------------------------------------------- 1 | import pgo.util.TLAExprInterpreter.{TLAValueString,TLAValueLambda} 2 | 3 | pgo.PGo.TraceCheckerUserConfig.build { builder => 4 | builder.constant("MK_HELLO")(TLAValueLambda { 5 | case List(TLAValueString(left), TLAValueString(right)) => 6 | TLAValueString(left ++ right) 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /test/traces/hello_persistent_trace.txt: -------------------------------------------------------------------------------- 1 | {"archetypeName":"AHello","clock":[[["AHello","\"self\""],1]],"csElements":[{"indices":[],"name":{"name":".pc","prefix":"","self":"\"self\""},"tag":"read","value":"\"AHello.lbl\""},{"indices":[],"name":{"name":"out","prefix":"AHello","self":"\"self\""},"tag":"write","value":"\"hello\""},{"indices":[],"name":{"name":".pc","prefix":"","self":"\"self\""},"tag":"write","value":"\"AHello.Done\""}],"self":"\"self\""} 2 | -------------------------------------------------------------------------------- /test/traces/hello_shared_trace.txt: -------------------------------------------------------------------------------- 1 | {"archetypeName":"AHello","clock":[[["AHello","\"self\""],1]],"csElements":[{"indices":[],"name":{"name":".pc","prefix":"","self":"\"self\""},"tag":"read","value":"\"AHello.lbl\""},{"indices":[],"name":{"name":"out","prefix":"AHello","self":"\"self\""},"tag":"write","value":"\"hello\""},{"indices":[],"name":{"name":".pc","prefix":"","self":"\"self\""},"tag":"write","value":"\"AHello.Done\""}],"self":"\"self\""} 2 | -------------------------------------------------------------------------------- /test/traces/hello_simple_trace.txt: -------------------------------------------------------------------------------- 1 | {"archetypeName":"AHello","clock":[[["AHello","\"self\""],1]],"csElements":[{"indices":[],"name":{"name":".pc","prefix":"","self":"\"self\""},"tag":"read","value":"\"AHello.lbl\""},{"indices":[],"name":{"name":"out","prefix":"AHello","self":"\"self\""},"tag":"write","value":"\"hello\""},{"indices":[],"name":{"name":".pc","prefix":"","self":"\"self\""},"tag":"write","value":"\"AHello.Done\""}],"self":"\"self\""} 2 | -------------------------------------------------------------------------------- /tools/tla2tools.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DistCompiler/pgo/0008579c554dd5d66a343ea4c727b75ad68d4ee5/tools/tla2tools.jar --------------------------------------------------------------------------------