From aac1361f2527ec5440ff9b913a5a9eaea4b03f56 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Thu, 24 Feb 2022 17:14:34 -0800 Subject: [PATCH 01/16] Added initial forkState endpoint to resource API --- distsys/archetyperesource.go | 21 +++++++++++++++++++ distsys/resources/channels.go | 15 ++++++++++++++ distsys/resources/crdt.go | 5 +++++ distsys/resources/dummy.go | 5 +++++ distsys/resources/fd.go | 5 +++++ distsys/resources/filesystem.go | 5 +++++ distsys/resources/incmap.go | 5 +++++ distsys/resources/localshared.go | 5 +++++ distsys/resources/mailboxes.go | 5 +++++ distsys/resources/nestedarch.go | 5 +++++ distsys/resources/persistent.go | 5 +++++ distsys/resources/placeholder.go | 5 +++++ distsys/resources/relaxedmailboxes.go | 10 +++++++++ distsys/resources/tcpmailboxes.go | 10 +++++++++ distsys/resources/twopc.go | 29 +++++++++++++++------------ 15 files changed, 122 insertions(+), 13 deletions(-) diff --git a/distsys/archetyperesource.go b/distsys/archetyperesource.go index 41677844e..7e02dc6ef 100644 --- a/distsys/archetyperesource.go +++ b/distsys/archetyperesource.go @@ -49,6 +49,11 @@ type ArchetypeResource interface { // archetype is not running. Close will be called at most once by the MPCal // Context. Close() error + // ForkState must clone all sub-resources whose properties are NOT idempotent. ForkState returns + // a copy of this current resource along with any other properties required so that it can be run independently. + // For now this is a BLOCKING CALL (though perhaps can be converted to NON-BLOCKING by returning a channel) + // This is meant to be called before doing concurrent operations with the same resource for critical sections + ForkState() (ArchetypeResource, error) } type ArchetypeResourceLeafMixin struct{} @@ -137,6 +142,15 @@ func (res *LocalArchetypeResource) Close() error { return nil } +// TODO: Implement this +func (res *LocalArchetypeResource) ForkState() (ArchetypeResource, error) { + return &LocalArchetypeResource{ + value: res.value, + oldValue: res.oldValue, + hasOldValue: res.hasOldValue, + }, nil +} + func (res *LocalArchetypeResource) GetState() ([]byte, error) { var writer bytes.Buffer encoder := gob.NewEncoder(&writer) @@ -207,3 +221,10 @@ func (res localArchetypeSubResource) Index(index tla.TLAValue) (ArchetypeResourc func (res localArchetypeSubResource) Close() error { return nil } + +func (res localArchetypeSubResource) ForkState() (ArchetypeResource, error) { + return localArchetypeSubResource{ + indices: res.indices, + parent: res.parent, + }, nil +} diff --git a/distsys/resources/channels.go b/distsys/resources/channels.go index 384c6f7c8..078bd8fcf 100644 --- a/distsys/resources/channels.go +++ b/distsys/resources/channels.go @@ -73,6 +73,11 @@ func (res *InputChannel) Close() error { return nil } +func (res *InputChannel) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} + // OutputChannel wraps a native Go channel, such that an MPCal model may write to that channel. type OutputChannel struct { distsys.ArchetypeResourceLeafMixin @@ -128,6 +133,11 @@ func (res *OutputChannel) Close() error { return nil } +func (res *OutputChannel) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} + const singleOutputChannelWriteTimeout = 20 * time.Millisecond type SingleOutputChannel struct { @@ -173,3 +183,8 @@ func (res *SingleOutputChannel) WriteValue(value tla.TLAValue) error { func (res *SingleOutputChannel) Close() error { return nil } + +func (res *SingleOutputChannel) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/crdt.go b/distsys/resources/crdt.go index e1a327b24..260e06f03 100644 --- a/distsys/resources/crdt.go +++ b/distsys/resources/crdt.go @@ -187,6 +187,11 @@ func (res *crdt) Close() error { return nil } +func (res *crdt) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} + // tryConnectPeers tries to connect to peer nodes with timeout. If dialing // succeeds, retains the client for later RPC. func (res *crdt) tryConnectPeers(selected *immutable.Map) { diff --git a/distsys/resources/dummy.go b/distsys/resources/dummy.go index 70f32fcb1..6938cb832 100644 --- a/distsys/resources/dummy.go +++ b/distsys/resources/dummy.go @@ -40,3 +40,8 @@ func (res *Dummy) Index(index tla.TLAValue) (distsys.ArchetypeResource, error) { func (res *Dummy) Close() error { return nil } + +func (res *Dummy) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/fd.go b/distsys/resources/fd.go index 9eadf7349..9e2879342 100644 --- a/distsys/resources/fd.go +++ b/distsys/resources/fd.go @@ -398,3 +398,8 @@ func (res *singleFailureDetector) Close() error { } return err } + +func (res *singleFailureDetector) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/filesystem.go b/distsys/resources/filesystem.go index c3937c895..6509641e1 100644 --- a/distsys/resources/filesystem.go +++ b/distsys/resources/filesystem.go @@ -91,3 +91,8 @@ func (res *file) WriteValue(value tla.TLAValue) error { func (res *file) Close() error { return nil } + +func (res *file) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/incmap.go b/distsys/resources/incmap.go index 0836fe34f..ef1a91f49 100644 --- a/distsys/resources/incmap.go +++ b/distsys/resources/incmap.go @@ -153,3 +153,8 @@ func (res *IncrementalMap) Close() error { } return err } + +func (res *IncrementalMap) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/localshared.go b/distsys/resources/localshared.go index a811848d9..cac6d6081 100644 --- a/distsys/resources/localshared.go +++ b/distsys/resources/localshared.go @@ -129,6 +129,11 @@ func (res *LocalShared) Close() error { return nil } +func (res *LocalShared) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} + func (res *LocalShared) GetState() ([]byte, error) { if res.acquired == 0 { err := res.sharedRes.acquire(1) diff --git a/distsys/resources/mailboxes.go b/distsys/resources/mailboxes.go index 3fb477a55..8fd48fc19 100644 --- a/distsys/resources/mailboxes.go +++ b/distsys/resources/mailboxes.go @@ -96,3 +96,8 @@ func (res *mailboxesLocalLength) WriteValue(value tla.TLAValue) error { func (res *mailboxesLocalLength) Close() error { return nil } + +func (res *mailboxesLocalLength) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/nestedarch.go b/distsys/resources/nestedarch.go index eee0c479b..422ffe855 100644 --- a/distsys/resources/nestedarch.go +++ b/distsys/resources/nestedarch.go @@ -397,3 +397,8 @@ func (res *nestedArchetype) Close() (err error) { close(res.ctxErrCh) return } + +func (res *nestedArchetype) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/persistent.go b/distsys/resources/persistent.go index 329fd96dd..57d3f9cf6 100644 --- a/distsys/resources/persistent.go +++ b/distsys/resources/persistent.go @@ -118,3 +118,8 @@ func (res *PersistentResource) Index(index tla.TLAValue) (distsys.ArchetypeResou func (res *PersistentResource) Close() error { return res.wrappedRes.Close() } + +func (res *PersistentResource) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/placeholder.go b/distsys/resources/placeholder.go index f2fdbf0e1..980952d17 100644 --- a/distsys/resources/placeholder.go +++ b/distsys/resources/placeholder.go @@ -50,3 +50,8 @@ func (res *PlaceHolder) Index(index tla.TLAValue) (distsys.ArchetypeResource, er func (res *PlaceHolder) Close() error { return nil } + +func (res *PlaceHolder) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/relaxedmailboxes.go b/distsys/resources/relaxedmailboxes.go index 2a501c3bf..b2ac2eb3f 100644 --- a/distsys/resources/relaxedmailboxes.go +++ b/distsys/resources/relaxedmailboxes.go @@ -187,6 +187,11 @@ func (res *relaxedMailboxesLocal) Close() error { return err } +func (res *relaxedMailboxesLocal) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} + func (res *relaxedMailboxesLocal) length() int { return len(res.readBacklog) + len(res.msgChannel) } @@ -279,3 +284,8 @@ func (res *relaxedMailboxesRemote) Close() error { } return err } + +func (res *relaxedMailboxesRemote) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/tcpmailboxes.go b/distsys/resources/tcpmailboxes.go index ada9f6ff1..1c8caa4b4 100644 --- a/distsys/resources/tcpmailboxes.go +++ b/distsys/resources/tcpmailboxes.go @@ -286,6 +286,11 @@ func (res *tcpMailboxesLocal) Close() error { return err } +func (res *tcpMailboxesLocal) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} + func (res *tcpMailboxesLocal) length() int { return len(res.readBacklog) + len(res.msgChannel) } @@ -497,3 +502,8 @@ func (res *tcpMailboxesRemote) Close() error { } return err } + +func (res *tcpMailboxesRemote) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/twopc.go b/distsys/resources/twopc.go index c923d9738..832f798bf 100644 --- a/distsys/resources/twopc.go +++ b/distsys/resources/twopc.go @@ -207,17 +207,17 @@ func (state CriticalSectionState) String() string { // TwoPCReceiver defines the RPC receiver for 2PC communication. The associated // functions for this struct are exposed via the RPC interface. type TwoPCReceiver struct { - ListenAddr string - twopc *TwoPCArchetypeResource - listener net.Listener - closed bool + ListenAddr string + twopc *TwoPCArchetypeResource + listener net.Listener + closed bool activeConns []net.Conn } func makeTwoPCReceiver(twopc *TwoPCArchetypeResource, ListenAddr string) TwoPCReceiver { return TwoPCReceiver{ - ListenAddr: ListenAddr, - twopc: twopc, + ListenAddr: ListenAddr, + twopc: twopc, activeConns: []net.Conn{}, } } @@ -843,6 +843,11 @@ func (res *TwoPCArchetypeResource) Close() error { return nil } +func (res *TwoPCArchetypeResource) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + panic("implement me") +} + func (res *TwoPCArchetypeResource) inCriticalSection() bool { return res.criticalSectionState != notInCriticalSection } @@ -910,7 +915,7 @@ func (twopc *TwoPCArchetypeResource) receiveInternal(arg TwoPCRequest, reply *Tw return nil } - if arg.Version > twopc.version + 1 { + if arg.Version > twopc.version+1 { twopc.log( infoLevel, "%s message (version %d) from %s is higher than expected %d", @@ -933,11 +938,10 @@ func (twopc *TwoPCArchetypeResource) receiveInternal(arg TwoPCRequest, reply *Tw twopc.acceptedPreCommit.Sender == arg.Sender && twopc.acceptedPreCommit.Value == arg.Value { *reply = makeAccept() - } else if twopc.criticalSectionState.canAcceptPreCommit() && ( - twopc.twoPCState == initial || + } else if twopc.criticalSectionState.canAcceptPreCommit() && (twopc.twoPCState == initial || twopc.acceptedPreCommit.Version < arg.Version || - (twopc.acceptedPreCommit.Version == arg.Version && - twopc.acceptedPreCommit.Sender == arg.Sender)) { + (twopc.acceptedPreCommit.Version == arg.Version && + twopc.acceptedPreCommit.Sender == arg.Sender)) { twopc.setTwoPCState(acceptedPreCommit) twopc.log(debugLevel, "Accepted PreCommit message %s.", arg.Value) *reply = makeAccept() @@ -978,7 +982,6 @@ func (twopc *TwoPCArchetypeResource) receiveInternal(arg TwoPCRequest, reply *Tw func (res *TwoPCArchetypeResource) broadcastAbortOrCommit(request TwoPCRequest) { originalVersion := res.version - res.broadcast(request.RequestType.String(), func(i int, r ReplicaHandle, isDone func() bool) bool { shouldRetry := func() bool { @@ -995,7 +998,7 @@ func (res *TwoPCArchetypeResource) broadcastAbortOrCommit(request TwoPCRequest) res.log(warnLevel, "Error during %s RPC to %i: %s (will retry)", request.RequestType, i, err) time.Sleep(1 * time.Second) } else { - if !reply.Accept{ + if !reply.Accept { assert( reply.Version > originalVersion, fmt.Sprintf( From 41fb5424694c9be2e9be827435cbac8b96298ea4 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Mon, 14 Mar 2022 23:41:35 -0700 Subject: [PATCH 02/16] Added LinkState and RunBranchConcurrently API --- distsys/archetypeinterface.go | 122 ++++++++++ distsys/archetyperesource.go | 31 ++- distsys/mpcalctx.go | 5 + distsys/resources/channels.go | 15 ++ distsys/resources/crdt.go | 5 + distsys/resources/dummy.go | 5 + distsys/resources/fd.go | 5 + distsys/resources/filesystem.go | 5 + distsys/resources/incmap.go | 5 + distsys/resources/localshared.go | 5 + distsys/resources/mailboxes.go | 5 + distsys/resources/nestedarch.go | 5 + distsys/resources/persistent.go | 5 + distsys/resources/placeholder.go | 5 + distsys/resources/relaxedmailboxes.go | 10 + distsys/resources/tcpmailboxes.go | 27 +++ distsys/resources/twopc.go | 5 + test/files/general/BranchScheduling.tla | 79 +++++++ .../BranchScheduling.go | 223 ++++++++++++++++++ .../BranchScheduling_test.go | 28 +++ .../BranchScheduling.tla.gotests/go.mod | 7 + .../BranchScheduling.tla.gotests/go.sum | 126 ++++++++++ .../branchtest.tla.gotests/forklinkstate.go | 9 + .../forklinkstate_test.go | 105 +++++++++ .../general/branchtest.tla.gotests/go.mod | 7 + .../general/branchtest.tla.gotests/go.sum | 153 ++++++++++++ 26 files changed, 997 insertions(+), 5 deletions(-) create mode 100644 test/files/general/BranchScheduling.tla create mode 100644 test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go create mode 100644 test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go create mode 100644 test/files/general/BranchScheduling.tla.gotests/go.mod create mode 100644 test/files/general/BranchScheduling.tla.gotests/go.sum create mode 100644 test/files/general/branchtest.tla.gotests/forklinkstate.go create mode 100644 test/files/general/branchtest.tla.gotests/forklinkstate_test.go create mode 100644 test/files/general/branchtest.tla.gotests/go.mod create mode 100644 test/files/general/branchtest.tla.gotests/go.sum diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index db81a192c..aed5c3a75 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/UBC-NSS/pgo/distsys/tla" "github.com/benbjohnson/immutable" + "sync" ) // ArchetypeInterface provides an archetype-centric interface to an MPCalContext. @@ -54,6 +55,75 @@ func (iface ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []t return } +func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue, branchResources BranchResourceMap) (err error) { + iface.ensureCriticalSectionWith(handle) + + res, ok := branchResources[handle] + if ok { + // Handle was in the branch resources + for _, index := range indices { + res, err = res.Index(index) + if err != nil { + return + } + } + err = res.WriteValue(value) + return + } else { + // Handle wasn't in the branch resources + resource := iface.ctx.getResourceByHandle(handle) + res, err = resource.ForkState() + + // Put the new forked resource in the branch resources + branchResources[handle] = res + + for _, index := range indices { + res, err = res.Index(index) + if err != nil { + return + } + } + err = res.WriteValue(value) + return + } +} + +func (iface ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue, branchResources BranchResourceMap) (value tla.TLAValue, err error) { + iface.ensureCriticalSectionWith(handle) + + res, ok := branchResources[handle] + if ok { + // Handle was in the branch resources + for _, index := range indices { + res, err = res.Index(index) + if err != nil { + return + } + } + value, err = res.ReadValue() + return + } else { + // Handle wasn't in the branch resources + resource := iface.ctx.getResourceByHandle(handle) + res, err = resource.ForkState() + + // Put the new forked resource in the branch resources + branchResources[handle] = res + + if err != nil { + return + } + for _, index := range indices { + res, err = res.Index(index) + if err != nil { + return + } + } + value, err = res.ReadValue() + return + } +} + // NextFairnessCounter returns number [0,ceiling) indicating a non-deterministic branching decision which, // given an MPCal critical section being retried indefinitely with no other changes, will try to guarantee that all // possible non-deterministic branch choices will be attempted. @@ -61,6 +131,58 @@ func (iface ArchetypeInterface) NextFairnessCounter(id string, ceiling uint) uin return iface.ctx.fairnessCounter.NextFairnessCounter(id, ceiling) } +type BranchResourceMap map[ArchetypeResourceHandle]ArchetypeResource + +type branch func(branchResources BranchResourceMap) error + +func (iface ArchetypeInterface) RunBranchConcurrently(branches ...branch) error { + //return iface.ctx.branchScheduler.RunCriticalSection(branches...) + fmt.Println("Running critical section") + + var wg sync.WaitGroup + + // Make map of resource handle to actual resource for each branch + branchesResourceMap := make(map[int]BranchResourceMap) + for i, _ := range branches { + branchesResourceMap[i] = make(BranchResourceMap) + } + + ch := make(chan int, 1) + + for i, _ := range branches { + wg.Add(1) + index := i + branch := branches[index] + go func() { + defer wg.Done() + // Run branch + branch(branchesResourceMap[index]) + + // Write to channel to see which branch finished first + select { + case ch <- index: + fmt.Printf("keep branch %d's work \n", index) + default: + fmt.Printf("discard branch %d's work \n", index) + } + }() + } + + wg.Wait() + + index := <-ch + branchResourceMap := branchesResourceMap[index] + for _, forkedResource := range branchResourceMap { + forkedResource.LinkState() + } + + // Do work to maintain the work done by branch (take their branchResourceMap and write it in) + + fmt.Println("Finished critical section") + + return nil +} + // GetConstant returns the constant operator bound to the given name as a variadic Go function. // The function is generated in DefineConstantOperator, and is expected to check its own arguments. func (iface ArchetypeInterface) GetConstant(name string) func(args ...tla.TLAValue) tla.TLAValue { diff --git a/distsys/archetyperesource.go b/distsys/archetyperesource.go index 7e02dc6ef..4f1f9678b 100644 --- a/distsys/archetyperesource.go +++ b/distsys/archetyperesource.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/gob" "errors" - "github.com/UBC-NSS/pgo/distsys/tla" ) @@ -54,6 +53,9 @@ type ArchetypeResource interface { // For now this is a BLOCKING CALL (though perhaps can be converted to NON-BLOCKING by returning a channel) // This is meant to be called before doing concurrent operations with the same resource for critical sections ForkState() (ArchetypeResource, error) + // LinkState syncs the data from the caller resource into the forkParent resource. It is intended to be called on a + // resource who was forked by a call to ForkState. + LinkState() error } type ArchetypeResourceLeafMixin struct{} @@ -84,6 +86,7 @@ type LocalArchetypeResource struct { // if this resource is already written in this critical section, oldValue contains prev value // value always contains the "current" value value, oldValue tla.TLAValue + forkParent *LocalArchetypeResource } var _ ArchetypeResource = &LocalArchetypeResource{} @@ -142,15 +145,23 @@ func (res *LocalArchetypeResource) Close() error { return nil } -// TODO: Implement this func (res *LocalArchetypeResource) ForkState() (ArchetypeResource, error) { return &LocalArchetypeResource{ value: res.value, oldValue: res.oldValue, hasOldValue: res.hasOldValue, + forkParent: res, }, nil } +func (res *LocalArchetypeResource) LinkState() error { + res.forkParent.value = res.value + res.forkParent.oldValue = res.oldValue + res.forkParent.hasOldValue = res.hasOldValue + + return nil +} + func (res *LocalArchetypeResource) GetState() ([]byte, error) { var writer bytes.Buffer encoder := gob.NewEncoder(&writer) @@ -166,7 +177,8 @@ type localArchetypeSubResource struct { // indices gives the total path from root value, as accumulated from calls to Index, e.g with `i[a][b] := ...` you get []{a, b} indices []tla.TLAValue // the parent local resource. it does everything important, which is why most methods here just return nil; they shouldn't even be called - parent *LocalArchetypeResource + parent *LocalArchetypeResource + forkParent *localArchetypeSubResource } var _ ArchetypeResource = &localArchetypeSubResource{} @@ -224,7 +236,16 @@ func (res localArchetypeSubResource) Close() error { func (res localArchetypeSubResource) ForkState() (ArchetypeResource, error) { return localArchetypeSubResource{ - indices: res.indices, - parent: res.parent, + indices: res.indices, + parent: res.parent, + forkParent: &res, }, nil } + +func (res localArchetypeSubResource) LinkState() error { + + res.forkParent.indices = res.indices + res.forkParent.parent = res.parent + + return nil +} diff --git a/distsys/mpcalctx.go b/distsys/mpcalctx.go index c78f443c0..d7399a79d 100644 --- a/distsys/mpcalctx.go +++ b/distsys/mpcalctx.go @@ -132,6 +132,7 @@ type MPCalContext struct { // state for ArchetypeInterface.NextFairnessCounter fairnessCounter FairnessCounter + //branchScheduler BranchScheduler jumpTable MPCalJumpTable procTable MPCalProcTable @@ -179,6 +180,7 @@ func NewMPCalContext(self tla.TLAValue, archetype MPCalArchetype, configFns ...M self: self, resources: make(map[ArchetypeResourceHandle]ArchetypeResource), fairnessCounter: RoundRobinFairnessCounterMaker()(), + //branchScheduler: BranchSchedulerMaker(), jumpTable: archetype.JumpTable, procTable: archetype.ProcTable, @@ -597,7 +599,10 @@ func (ctx *MPCalContext) Run() (err error) { pcValStr := pcVal.AsString() ctx.fairnessCounter.BeginCriticalSection(pcValStr) + //ctx.branchScheduler.BeginCriticalSection(pcValStr) + criticalSection := ctx.iface.getCriticalSection(pcValStr) + //fmt.Println(criticalSection) err = criticalSection.Body(ctx.iface) if err != nil { continue diff --git a/distsys/resources/channels.go b/distsys/resources/channels.go index 078bd8fcf..31300ff66 100644 --- a/distsys/resources/channels.go +++ b/distsys/resources/channels.go @@ -78,6 +78,11 @@ func (res *InputChannel) ForkState() (distsys.ArchetypeResource, error) { panic("implement me") } +func (res *InputChannel) LinkState() error { + //TODO implement me + panic("implement me") +} + // OutputChannel wraps a native Go channel, such that an MPCal model may write to that channel. type OutputChannel struct { distsys.ArchetypeResourceLeafMixin @@ -138,6 +143,11 @@ func (res *OutputChannel) ForkState() (distsys.ArchetypeResource, error) { panic("implement me") } +func (res *OutputChannel) LinkState() error { + //TODO implement me + panic("implement me") +} + const singleOutputChannelWriteTimeout = 20 * time.Millisecond type SingleOutputChannel struct { @@ -188,3 +198,8 @@ func (res *SingleOutputChannel) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *SingleOutputChannel) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/crdt.go b/distsys/resources/crdt.go index 260e06f03..d0c6cc9a4 100644 --- a/distsys/resources/crdt.go +++ b/distsys/resources/crdt.go @@ -192,6 +192,11 @@ func (res *crdt) ForkState() (distsys.ArchetypeResource, error) { panic("implement me") } +func (res *crdt) LinkState() error { + //TODO implement me + panic("implement me") +} + // tryConnectPeers tries to connect to peer nodes with timeout. If dialing // succeeds, retains the client for later RPC. func (res *crdt) tryConnectPeers(selected *immutable.Map) { diff --git a/distsys/resources/dummy.go b/distsys/resources/dummy.go index 6938cb832..2478db3d8 100644 --- a/distsys/resources/dummy.go +++ b/distsys/resources/dummy.go @@ -45,3 +45,8 @@ func (res *Dummy) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *Dummy) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/fd.go b/distsys/resources/fd.go index 9e2879342..dcb5ade83 100644 --- a/distsys/resources/fd.go +++ b/distsys/resources/fd.go @@ -403,3 +403,8 @@ func (res *singleFailureDetector) ForkState() (distsys.ArchetypeResource, error) //TODO implement me panic("implement me") } + +func (res *singleFailureDetector) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/filesystem.go b/distsys/resources/filesystem.go index 6509641e1..5d4fd27d1 100644 --- a/distsys/resources/filesystem.go +++ b/distsys/resources/filesystem.go @@ -96,3 +96,8 @@ func (res *file) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *file) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/incmap.go b/distsys/resources/incmap.go index ef1a91f49..1cd850148 100644 --- a/distsys/resources/incmap.go +++ b/distsys/resources/incmap.go @@ -158,3 +158,8 @@ func (res *IncrementalMap) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *IncrementalMap) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/localshared.go b/distsys/resources/localshared.go index cac6d6081..db4d69f02 100644 --- a/distsys/resources/localshared.go +++ b/distsys/resources/localshared.go @@ -134,6 +134,11 @@ func (res *LocalShared) ForkState() (distsys.ArchetypeResource, error) { panic("implement me") } +func (res *LocalShared) LinkState() error { + //TODO implement me + panic("implement me") +} + func (res *LocalShared) GetState() ([]byte, error) { if res.acquired == 0 { err := res.sharedRes.acquire(1) diff --git a/distsys/resources/mailboxes.go b/distsys/resources/mailboxes.go index 8fd48fc19..fc40f14bd 100644 --- a/distsys/resources/mailboxes.go +++ b/distsys/resources/mailboxes.go @@ -101,3 +101,8 @@ func (res *mailboxesLocalLength) ForkState() (distsys.ArchetypeResource, error) //TODO implement me panic("implement me") } + +func (res *mailboxesLocalLength) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/nestedarch.go b/distsys/resources/nestedarch.go index 422ffe855..8824fa76f 100644 --- a/distsys/resources/nestedarch.go +++ b/distsys/resources/nestedarch.go @@ -402,3 +402,8 @@ func (res *nestedArchetype) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *nestedArchetype) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/persistent.go b/distsys/resources/persistent.go index 57d3f9cf6..dd77e1d2a 100644 --- a/distsys/resources/persistent.go +++ b/distsys/resources/persistent.go @@ -123,3 +123,8 @@ func (res *PersistentResource) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *PersistentResource) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/placeholder.go b/distsys/resources/placeholder.go index 980952d17..12d54e8a1 100644 --- a/distsys/resources/placeholder.go +++ b/distsys/resources/placeholder.go @@ -55,3 +55,8 @@ func (res *PlaceHolder) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *PlaceHolder) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/relaxedmailboxes.go b/distsys/resources/relaxedmailboxes.go index b2ac2eb3f..c060876dc 100644 --- a/distsys/resources/relaxedmailboxes.go +++ b/distsys/resources/relaxedmailboxes.go @@ -192,6 +192,11 @@ func (res *relaxedMailboxesLocal) ForkState() (distsys.ArchetypeResource, error) panic("implement me") } +func (res *relaxedMailboxesLocal) LinkState() error { + //TODO implement me + panic("implement me") +} + func (res *relaxedMailboxesLocal) length() int { return len(res.readBacklog) + len(res.msgChannel) } @@ -289,3 +294,8 @@ func (res *relaxedMailboxesRemote) ForkState() (distsys.ArchetypeResource, error //TODO implement me panic("implement me") } + +func (res *relaxedMailboxesRemote) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/tcpmailboxes.go b/distsys/resources/tcpmailboxes.go index 1c8caa4b4..8d19f83d8 100644 --- a/distsys/resources/tcpmailboxes.go +++ b/distsys/resources/tcpmailboxes.go @@ -287,6 +287,28 @@ func (res *tcpMailboxesLocal) Close() error { } func (res *tcpMailboxesLocal) ForkState() (distsys.ArchetypeResource, error) { + //TODO implement me + //panic("implement me") + msgChannel := make(chan tla.TLAValue, mailboxesReceiveChannelSize) + //listener, err := net.Listen("tcp", res.listenAddr) + //if err != nil { + // panic(fmt.Errorf("could not listen on address %s: %w", listenAddr, err)) + //} + log.Printf("using listening on: %s", res.listenAddr) + clone := &tcpMailboxesLocal{ + listenAddr: res.listenAddr, + msgChannel: msgChannel, + listener: res.listener, + done: make(chan struct{}), + closing: res.closing, + } + // Previous resource already accepted the connection + //go res.listen() + + return clone, nil +} + +func (res *tcpMailboxesLocal) LinkState() error { //TODO implement me panic("implement me") } @@ -507,3 +529,8 @@ func (res *tcpMailboxesRemote) ForkState() (distsys.ArchetypeResource, error) { //TODO implement me panic("implement me") } + +func (res *tcpMailboxesRemote) LinkState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/twopc.go b/distsys/resources/twopc.go index 832f798bf..08bbae3d1 100644 --- a/distsys/resources/twopc.go +++ b/distsys/resources/twopc.go @@ -848,6 +848,11 @@ func (res *TwoPCArchetypeResource) ForkState() (distsys.ArchetypeResource, error panic("implement me") } +func (res *TwoPCArchetypeResource) LinkState() error { + //TODO implement me + panic("implement me") +} + func (res *TwoPCArchetypeResource) inCriticalSection() bool { return res.criticalSectionState != notInCriticalSection } diff --git a/test/files/general/BranchScheduling.tla b/test/files/general/BranchScheduling.tla new file mode 100644 index 000000000..59191a4b2 --- /dev/null +++ b/test/files/general/BranchScheduling.tla @@ -0,0 +1,79 @@ +---- MODULE BranchScheduling ---- + +EXTENDS Naturals, Sequences, TLC, FiniteSets + +(* + +--mpcal BranchScheduling { + +define { + TheSet == {1, 2} +} + +archetype ABranch() +variable i = 0, j = 2, k= 4, mark = {}; +{ +loop: + while (i < 20) { + branchlabel: + either { + i := i + 1; + } or { + j := j + 5; + } or { + k := j + k; + }; + lbl1: + with (a \in TheSet) { + mark := mark \cup {a}; + }; + \* lbl2: + \* i := i + 1; + \* }; + }; +} + +process (Branch = 1) == instance ABranch(); + +} + +\* BEGIN PLUSCAL TRANSLATION +--algorithm BranchScheduling { + define{ + TheSet == {1, 2} + } + + process (Branch = 1) + variables i = 0; j = 2; k = 4; mark = {}; + { + loop: + if ((i) < (20)) { + goto branchlabel; + } else { + goto Done; + }; + branchlabel: + either { + i := (i) + (1); + goto lbl1; + } or { + j := (j) + (5); + goto lbl1; + } or { + k := (j) + (k); + goto lbl1; + }; + lbl1: + with (a \in TheSet) { + mark := (mark) \union ({a}); + goto loop; + }; + } +} + +\* END PLUSCAL TRANSLATION + +*) + +\* BEGIN TRANSLATION +==== diff --git a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go new file mode 100644 index 000000000..50ea08524 --- /dev/null +++ b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go @@ -0,0 +1,223 @@ +package branchscheduling + +import ( + "fmt" + "github.com/UBC-NSS/pgo/distsys" + "github.com/UBC-NSS/pgo/distsys/tla" +) + +var _ = new(fmt.Stringer) // unconditionally prevent go compiler from reporting unused fmt import +var _ = distsys.ErrDone +var _ = tla.TLAValue{} // same, for tla + +func TheSet(iface distsys.ArchetypeInterface) tla.TLAValue { + return tla.MakeTLASet(tla.MakeTLANumber(1), tla.MakeTLANumber(2)) +} + +var procTable = distsys.MakeMPCalProcTable() + +var jumpTable = distsys.MakeMPCalJumpTable( + distsys.MPCalCriticalSection{ + Name: "ABranch.loop", + Body: func(iface distsys.ArchetypeInterface) error { + var err error + _ = err + i := iface.RequireArchetypeResource("ABranch.i") + var condition tla.TLAValue + condition, err = iface.Read(i, []tla.TLAValue{}) + if err != nil { + return err + } + if tla.TLA_LessThanSymbol(condition, tla.MakeTLANumber(20)).AsBool() { + return iface.Goto("ABranch.branchlabel") + } else { + return iface.Goto("ABranch.Done") + } + // no statements + }, + }, + distsys.MPCalCriticalSection{ + Name: "ABranch.branchlabel", + Body: func(iface distsys.ArchetypeInterface) error { + var err error + _ = err + i0 := iface.RequireArchetypeResource("ABranch.i") + j := iface.RequireArchetypeResource("ABranch.j") + k := iface.RequireArchetypeResource("ABranch.k") + + val, _ := iface.Read(i0, []tla.TLAValue{}) + fmt.Printf("i0: %v\n", val) + val, _ = iface.Read(j, []tla.TLAValue{}) + fmt.Printf("j: %v\n", val) + val, _ = iface.Read(k, []tla.TLAValue{}) + fmt.Printf("k: %v\n", val) + + return iface.RunBranchConcurrently( + func(branchResourceMap distsys.BranchResourceMap) error { + fmt.Println("CASE 0") + var exprRead tla.TLAValue + exprRead, err = iface.BranchRead(i0, []tla.TLAValue{}, branchResourceMap) + if err != nil { + return err + } + err = iface.BranchWrite(i0, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead, tla.MakeTLANumber(1)), branchResourceMap) + if err != nil { + return err + } + + val, _ := iface.Read(i0, []tla.TLAValue{}) + fmt.Printf("i0: %v\n", val) + val, _ = iface.Read(j, []tla.TLAValue{}) + fmt.Printf("j: %v\n", val) + val, _ = iface.Read(k, []tla.TLAValue{}) + fmt.Printf("k: %v\n", val) + + return iface.Goto("ABranch.lbl1") + }, + func(branchResourceMap distsys.BranchResourceMap) error { + fmt.Println("CASE 1") + var exprRead0 tla.TLAValue + exprRead0, err = iface.BranchRead(j, []tla.TLAValue{}, branchResourceMap) + if err != nil { + return err + } + err = iface.BranchWrite(j, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead0, tla.MakeTLANumber(5)), branchResourceMap) + if err != nil { + return err + } + + val, _ := iface.Read(i0, []tla.TLAValue{}) + fmt.Printf("i0: %v\n", val) + val, _ = iface.Read(j, []tla.TLAValue{}) + fmt.Printf("j: %v\n", val) + val, _ = iface.Read(k, []tla.TLAValue{}) + fmt.Printf("k: %v\n", val) + + return iface.Goto("ABranch.lbl1") + }, + func(branchResourceMap distsys.BranchResourceMap) error { + fmt.Println("CASE 2") + var exprRead1 tla.TLAValue + exprRead1, err = iface.BranchRead(j, []tla.TLAValue{}, branchResourceMap) + if err != nil { + return err + } + var exprRead2 tla.TLAValue + exprRead2, err = iface.BranchRead(k, []tla.TLAValue{}, branchResourceMap) + if err != nil { + return err + } + err = iface.BranchWrite(k, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead1, exprRead2), branchResourceMap) + if err != nil { + return err + } + + val, _ := iface.Read(i0, []tla.TLAValue{}) + fmt.Printf("i0: %v\n", val) + val, _ = iface.Read(j, []tla.TLAValue{}) + fmt.Printf("j: %v\n", val) + val, _ = iface.Read(k, []tla.TLAValue{}) + fmt.Printf("k: %v\n", val) + + return iface.Goto("ABranch.lbl1") + }) + + //Just fork all the variables above into some kind of structure where each go routine gets its own set + //PrepareBranches() + //Switch all cases into functional go routines sharing one channel that lets us know when they are done + //switch iface.NextFairnessCounter("ABranch.branchlabel.0", 3) { + //case 0: + // fmt.Println("CASE 0") + // var exprRead tla.TLAValue + // exprRead, err = iface.Read(i0, []tla.TLAValue{}) + // if err != nil { + // return err + // } + // err = iface.Write(i0, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead, tla.MakeTLANumber(1))) + // if err != nil { + // return err + // } + // return iface.Goto("ABranch.lbl1") + //case 1: + // fmt.Println("CASE 1") + // var exprRead0 tla.TLAValue + // exprRead0, err = iface.Read(j, []tla.TLAValue{}) + // if err != nil { + // return err + // } + // err = iface.Write(j, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead0, tla.MakeTLANumber(5))) + // if err != nil { + // return err + // } + // return iface.Goto("ABranch.lbl1") + //case 2: + // fmt.Println("CASE 2") + // var exprRead1 tla.TLAValue + // exprRead1, err = iface.Read(j, []tla.TLAValue{}) + // if err != nil { + // return err + // } + // var exprRead2 tla.TLAValue + // exprRead2, err = iface.Read(k, []tla.TLAValue{}) + // if err != nil { + // return err + // } + // err = iface.Write(k, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead1, exprRead2)) + // if err != nil { + // return err + // } + // return iface.Goto("ABranch.lbl1") + //default: + // panic("current branch of either matches no code paths!") + //} + //RecognizeBranch() + // no statements + }, + }, + distsys.MPCalCriticalSection{ + Name: "ABranch.lbl1", + Body: func(iface distsys.ArchetypeInterface) error { + var err error + _ = err + mark := iface.RequireArchetypeResource("ABranch.mark") + var aRead = TheSet(iface) + if aRead.AsSet().Len() == 0 { + return distsys.ErrCriticalSectionAborted + } + var a tla.TLAValue = aRead.SelectElement(iface.NextFairnessCounter("ABranch.lbl1.0", uint(aRead.AsSet().Len()))) + _ = a + var exprRead3 tla.TLAValue + exprRead3, err = iface.Read(mark, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(mark, []tla.TLAValue{}, tla.TLA_UnionSymbol(exprRead3, tla.MakeTLASet(a))) + if err != nil { + return err + } + return iface.Goto("ABranch.loop") + // no statements + }, + }, + distsys.MPCalCriticalSection{ + Name: "ABranch.Done", + Body: func(distsys.ArchetypeInterface) error { + return distsys.ErrDone + }, + }, +) + +var ABranch = distsys.MPCalArchetype{ + Name: "ABranch", + Label: "ABranch.loop", + RequiredRefParams: []string{}, + RequiredValParams: []string{}, + JumpTable: jumpTable, + ProcTable: procTable, + PreAmble: func(iface distsys.ArchetypeInterface) { + iface.EnsureArchetypeResourceLocal("ABranch.i", tla.MakeTLANumber(0)) + iface.EnsureArchetypeResourceLocal("ABranch.j", tla.MakeTLANumber(2)) + iface.EnsureArchetypeResourceLocal("ABranch.k", tla.MakeTLANumber(4)) + iface.EnsureArchetypeResourceLocal("ABranch.mark", tla.MakeTLASet()) + }, +} diff --git a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go new file mode 100644 index 000000000..c4a8113f6 --- /dev/null +++ b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go @@ -0,0 +1,28 @@ +package branchscheduling + +import ( + "testing" + "time" + + "github.com/UBC-NSS/pgo/distsys" + "github.com/UBC-NSS/pgo/distsys/tla" +) + +//run gogen -s test/files/general/BranchScheduling.tla -o test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go + +func TestBasic(t *testing.T) { + errCh := make(chan error, 1) + ctx := distsys.NewMPCalContext(tla.MakeTLAString("self"), ABranch) + go func() { + errCh <- ctx.Run() + }() + + select { + case err := <-errCh: + if err != nil { + panic(err) + } + case <-time.After(2 * time.Second): + t.Fatalf("timeout: ABranch should eventually (within 5 seconds) terminate") + } +} diff --git a/test/files/general/BranchScheduling.tla.gotests/go.mod b/test/files/general/BranchScheduling.tla.gotests/go.mod new file mode 100644 index 000000000..c3b5edf38 --- /dev/null +++ b/test/files/general/BranchScheduling.tla.gotests/go.mod @@ -0,0 +1,7 @@ +module example.org/BranchScheduling + +go 1.13 + +replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys + +require github.com/UBC-NSS/pgo/distsys v0.0.0-00010101000000-000000000000 diff --git a/test/files/general/BranchScheduling.tla.gotests/go.sum b/test/files/general/BranchScheduling.tla.gotests/go.sum new file mode 100644 index 000000000..42487d307 --- /dev/null +++ b/test/files/general/BranchScheduling.tla.gotests/go.sum @@ -0,0 +1,126 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/benbjohnson/immutable v0.3.0 h1:TVRhuZx2wG9SZ0LRdqlbs9S5BZ6Y24hJEHTCgWHZEIw= +github.com/benbjohnson/immutable v0.3.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= +github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/test/files/general/branchtest.tla.gotests/forklinkstate.go b/test/files/general/branchtest.tla.gotests/forklinkstate.go new file mode 100644 index 000000000..4eb39a5a3 --- /dev/null +++ b/test/files/general/branchtest.tla.gotests/forklinkstate.go @@ -0,0 +1,9 @@ +package branchtest_tla_gotests + +import ( + "testing" +) + +func start(t *testing.T) { + t.Errorf("NUM_CONSUMERS(12) should have yielded 13, got %v", "error") +} diff --git a/test/files/general/branchtest.tla.gotests/forklinkstate_test.go b/test/files/general/branchtest.tla.gotests/forklinkstate_test.go new file mode 100644 index 000000000..a6028c8b5 --- /dev/null +++ b/test/files/general/branchtest.tla.gotests/forklinkstate_test.go @@ -0,0 +1,105 @@ +package branchtest_tla_gotests + +import ( + "fmt" + "github.com/UBC-NSS/pgo/distsys" + "github.com/UBC-NSS/pgo/distsys/resources" + "github.com/UBC-NSS/pgo/distsys/tla" + "testing" +) + +func TestLOCALARCHETYPERESOURCE(t *testing.T) { + resource1 := distsys.LocalArchetypeResourceMaker(tla.MakeTLANumber(22)).Make() + + val1, err := resource1.ReadValue() + if err != nil { + t.Errorf("Error: %v", err) + } else if val1 != tla.MakeTLANumber(22) { + t.Errorf("expected to read value 22, got %v", val1) + } + + resource1.WriteValue(tla.MakeTLAString("hello world")) + val2, err := resource1.ReadValue() + if err != nil { + t.Errorf("Error: %v", err) + } else if val2 != tla.MakeTLAString("hello world") { + t.Errorf("expected to read value 22, got %v", val2) + } + + resource2, err := resource1.ForkState() + val3, err := resource2.ReadValue() + if err != nil { + t.Errorf("expected to read value, got %v", err) + } else if val3 != tla.MakeTLAString("hello world") { + t.Errorf("expected to read value 22, got %v", val3) + } + + resource2.WriteValue(tla.MakeTLAString("goodbye world")) + val4, err := resource2.ReadValue() + val5, err := resource1.ReadValue() + if err != nil { + t.Errorf("expected to read value, got %v", err) + } else if val4 != tla.MakeTLAString("goodbye world") { + t.Errorf("expected to read value goodbye world, got %v", val4) + } else if val5 != tla.MakeTLAString("hello world") { + t.Errorf("expected to read value hello world, got %v", val5) + } + + fmt.Println(resource1) + fmt.Println(resource2) + + err = resource2.LinkState() + if err != nil { + fmt.Println(err) + } + + fmt.Println(resource1) + fmt.Println(resource2) + + resource2.Commit() +} + +func TestTCPMAILBOXES(t *testing.T) { + mailbox1 := resources.TCPMailboxesMaker(func(index tla.TLAValue) (resources.MailboxKind, string) { + switch index.AsNumber() { + case 1: + return resources.MailboxesLocal, "localhost:8001" + case 2: + return resources.MailboxesRemote, "localhost:8002" + default: + panic(fmt.Errorf("unknown mailbox index %v", index)) + } + }).Make() + + fmt.Println(mailbox1) + + //val, err := mailbox1.ReadValue() + //if err != nil { + // fmt.Println(err) + //} + //fmt.Println(val) + + //time.Sleep(5000) +} + +// +//func TestSTART(t *testing.T) { +// mailbox := resources.TCPMailboxesMaker(func(index tla.TLAValue) (resources.MailboxKind, string) { +// switch index.AsNumber() { +// case 1: +// return resources.MailboxesLocal, "localhost:8001" +// case 2: +// return resources.MailboxesRemote, "localhost:8002" +// default: +// panic(fmt.Errorf("unknown mailbox index %v", index)) +// } +// }).Make() +// +// mailbox.WriteValue(tla.MakeTLANumber(22)) +// val, err := mailbox.ReadValue() +// if err != nil { +// t.Errorf("expected to read value, got %v", err) +// } +// fmt.Println(val) +// +//} diff --git a/test/files/general/branchtest.tla.gotests/go.mod b/test/files/general/branchtest.tla.gotests/go.mod new file mode 100644 index 000000000..ccb795f43 --- /dev/null +++ b/test/files/general/branchtest.tla.gotests/go.mod @@ -0,0 +1,7 @@ +module example.org/branchtest_tla_gotests + +go 1.13 + +replace github.com/UBC-NSS/pgo/distsys => ../../../../distsys + +require github.com/UBC-NSS/pgo/distsys v0.0.0-00010101000000-000000000000 diff --git a/test/files/general/branchtest.tla.gotests/go.sum b/test/files/general/branchtest.tla.gotests/go.sum new file mode 100644 index 000000000..79e56a6b6 --- /dev/null +++ b/test/files/general/branchtest.tla.gotests/go.sum @@ -0,0 +1,153 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/benbjohnson/immutable v0.3.0 h1:TVRhuZx2wG9SZ0LRdqlbs9S5BZ6Y24hJEHTCgWHZEIw= +github.com/benbjohnson/immutable v0.3.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= +github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From da2bf2c28f51656a0c55c998798212abb3250756 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Mon, 14 Mar 2022 23:55:25 -0700 Subject: [PATCH 03/16] Added commets --- distsys/archetypeinterface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index aed5c3a75..44968ba09 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -168,6 +168,7 @@ func (iface ArchetypeInterface) RunBranchConcurrently(branches ...branch) error }() } + // Wanna remove this wait group later just here for testing purposes!!! wg.Wait() index := <-ch From f9c0c38b266c0d82dbb9eea05d878da532f60ab6 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Sun, 3 Apr 2022 06:10:25 -0700 Subject: [PATCH 04/16] Migrated to forking ifaces --- distsys/archetypeinterface.go | 290 ++++++++++++++++++++++++++++------ distsys/mpcalctx.go | 87 ++++++---- 2 files changed, 303 insertions(+), 74 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index 44968ba09..b9dafc27a 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -12,24 +12,28 @@ import ( // (1) how to configure and run and MPCal archetype (available via a plain MPCalContext) // (2) how the MPCal archetype's code accesses its configuration and internal state while running (available via ArchetypeInterface) type ArchetypeInterface struct { - ctx *MPCalContext + ctx *MPCalContext + ForkedResources map[ArchetypeResourceHandle]ArchetypeResource + parent *ArchetypeInterface + path string + //dirtyResourceHandles map[ArchetypeResourceHandle]bool } // Self returns the associated archetype's self binding. Requires a configured archetype. -func (iface ArchetypeInterface) Self() tla.TLAValue { +func (iface *ArchetypeInterface) Self() tla.TLAValue { iface.ctx.requireRunnable() return iface.ctx.self } -func (iface ArchetypeInterface) ensureCriticalSectionWith(handle ArchetypeResourceHandle) { +func (iface *ArchetypeInterface) ensureCriticalSectionWith(handle ArchetypeResourceHandle) { iface.ctx.dirtyResourceHandles[handle] = true } // Write models the MPCal statement resourceFromHandle[indices...] := value. // It is expected to be called only from PGo-generated code. -func (iface ArchetypeInterface) Write(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { +func (iface *ArchetypeInterface) Write(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { iface.ensureCriticalSectionWith(handle) - res := iface.ctx.getResourceByHandle(handle) + res := iface.getResourceByHandle(handle) for _, index := range indices { res, err = res.Index(index) if err != nil { @@ -42,9 +46,9 @@ func (iface ArchetypeInterface) Write(handle ArchetypeResourceHandle, indices [] // Read models the MPCal expression resourceFromHandle[indices...]. // If is expected to be called only from PGo-generated code. -func (iface ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { +func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { iface.ensureCriticalSectionWith(handle) - res := iface.ctx.getResourceByHandle(handle) + res := iface.getResourceByHandle(handle) for _, index := range indices { res, err = res.Index(index) if err != nil { @@ -55,10 +59,11 @@ func (iface ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []t return } -func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue, branchResources BranchResourceMap) (err error) { +func (iface *ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { iface.ensureCriticalSectionWith(handle) - res, ok := branchResources[handle] + //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] + res, ok := iface.ForkedResources[handle] if ok { // Handle was in the branch resources for _, index := range indices { @@ -71,11 +76,28 @@ func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indi return } else { // Handle wasn't in the branch resources - resource := iface.ctx.getResourceByHandle(handle) + + // Search for resource through tree + //node := iface.ctx.forkedResourceTree.root.parent + //var resource ArchetypeResource + //for { + // if node == nil { + // return fmt.Errorf("could not find resource with name %v", handle) + // } + // + // resource, ok = node.ForkedResources[handle] + // if ok { + // break + // } + // node = node.parent + //} + + resource := iface.getResourceByHandle(handle) res, err = resource.ForkState() // Put the new forked resource in the branch resources - branchResources[handle] = res + iface.ForkedResources[handle] = res + //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res for _, index := range indices { res, err = res.Index(index) @@ -88,10 +110,44 @@ func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indi } } -func (iface ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue, branchResources BranchResourceMap) (value tla.TLAValue, err error) { +//func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue, branchResources BranchResourceMap) (err error) { +// iface.ensureCriticalSectionWith(handle) +// +// res, ok := branchResources[handle] +// if ok { +// // Handle was in the branch resources +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// err = res.WriteValue(value) +// return +// } else { +// // Handle wasn't in the branch resources +// resource := iface.ctx.getResourceByHandle(handle) +// res, err = resource.ForkState() +// +// // Put the new forked resource in the branch resources +// branchResources[handle] = res +// +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// err = res.WriteValue(value) +// return +// } +//} + +func (iface *ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { iface.ensureCriticalSectionWith(handle) - res, ok := branchResources[handle] + //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] + res, ok := iface.ForkedResources[handle] if ok { // Handle was in the branch resources for _, index := range indices { @@ -104,11 +160,12 @@ func (iface ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indic return } else { // Handle wasn't in the branch resources - resource := iface.ctx.getResourceByHandle(handle) + resource := iface.getResourceByHandle(handle) res, err = resource.ForkState() - // Put the new forked resource in the branch resources - branchResources[handle] = res + // Put the new forked resource in the node + iface.ForkedResources[handle] = res + //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res if err != nil { return @@ -120,33 +177,141 @@ func (iface ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indic } } value, err = res.ReadValue() + return } } +//func (iface ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue, branchResources BranchResourceMap) (value tla.TLAValue, err error) { +// iface.ensureCriticalSectionWith(handle) +// +// res, ok := branchResources[handle] +// if ok { +// // Handle was in the branch resources +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// value, err = res.ReadValue() +// return +// } else { +// // Handle wasn't in the branch resources +// resource := iface.ctx.getResourceByHandle(handle) +// res, err = resource.ForkState() +// +// // Put the new forked resource in the branch resources +// branchResources[handle] = res +// +// if err != nil { +// return +// } +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// value, err = res.ReadValue() +// return +// } +//} + // NextFairnessCounter returns number [0,ceiling) indicating a non-deterministic branching decision which, // given an MPCal critical section being retried indefinitely with no other changes, will try to guarantee that all // possible non-deterministic branch choices will be attempted. -func (iface ArchetypeInterface) NextFairnessCounter(id string, ceiling uint) uint { +func (iface *ArchetypeInterface) NextFairnessCounter(id string, ceiling uint) uint { return iface.ctx.fairnessCounter.NextFairnessCounter(id, ceiling) } type BranchResourceMap map[ArchetypeResourceHandle]ArchetypeResource -type branch func(branchResources BranchResourceMap) error +//type branch func(branchResources BranchResourceMap) error +type branch func(iface *ArchetypeInterface) error + +func (iface *ArchetypeInterface) ForkIFace() (*ArchetypeInterface, error) { + childContext := &MPCalContext{ + archetype: iface.ctx.archetype, + self: iface.ctx.self, + // Resources need + fairnessCounter: iface.ctx.fairnessCounter, + jumpTable: iface.ctx.jumpTable, + procTable: iface.ctx.procTable, + + dirtyResourceHandles: make(map[ArchetypeResourceHandle]bool), + + constantDefns: iface.ctx.constantDefns, + allowRun: true, + awaitExit: make(chan struct{}), + } + + childIFace := &ArchetypeInterface{ + ctx: childContext, + ForkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), + parent: iface, + } + + return childIFace, nil +} + +// TODO: Look into if dirty resource handles needs to be linked too +func (iface *ArchetypeInterface) LinkIFace(childIFace *ArchetypeInterface) error { + + //fmt.Println(iface.ctx.forkedResourceTree.root.ForkedResources) + //fmt.Println(childIFace.ctx.forkedResourceTree.root.ForkedResources) + + //for _, forkedResource := range childIFace.ctx.forkedResourceTree.root.ForkedResources { + // forkedResource.LinkState() + //} + + for forkedHandle, forkedResource := range childIFace.ForkedResources { + _, ok := iface.ForkedResources[forkedHandle] + if !ok { + // Parent iface doesn't contain resource so add it in + iface.ForkedResources[forkedHandle] = forkedResource + } else { + // Parent iface had the resource so link its state + err := forkedResource.LinkState() + if err != nil { + return err + } + } -func (iface ArchetypeInterface) RunBranchConcurrently(branches ...branch) error { + } + + return nil +} + +func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error { //return iface.ctx.branchScheduler.RunCriticalSection(branches...) fmt.Println("Running critical section") - var wg sync.WaitGroup - - // Make map of resource handle to actual resource for each branch - branchesResourceMap := make(map[int]BranchResourceMap) + // Create set of forked ifaces + ifaceMap := make(map[int]*ArchetypeInterface) for i, _ := range branches { - branchesResourceMap[i] = make(BranchResourceMap) + //node := ForkedResourceNode{ + // ForkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), + // parent: iface.ctx.forkedResourceTree.root, + // path: fmt.Sprintf("%s.%d", iface.ctx.forkedResourceTree.root.path, i), + //} + + childIFace, err := iface.ForkIFace() + childIFace.path = fmt.Sprintf("%s.%d", iface.path, i) + + if err != nil { + fmt.Println(err) + return err + } + + //childIFace.ctx.forkedResourceTree = ForkedResourceTree{root: &node} + + ifaceMap[i] = childIFace } + // TODO: Remove wait group + var wg sync.WaitGroup + ch := make(chan int, 1) for i, _ := range branches { @@ -156,14 +321,16 @@ func (iface ArchetypeInterface) RunBranchConcurrently(branches ...branch) error go func() { defer wg.Done() // Run branch - branch(branchesResourceMap[index]) + branch(ifaceMap[index]) // Write to channel to see which branch finished first select { case ch <- index: - fmt.Printf("keep branch %d's work \n", index) + //fmt.Printf("keep branch %d's work \n", index) + fmt.Printf("keep branch %s's work \n", ifaceMap[index].path) default: - fmt.Printf("discard branch %d's work \n", index) + //fmt.Printf("discard branch %d's work \n", index) + //fmt.Printf("discard branch %s's work \n", ifaceMap[index].path) } }() } @@ -172,12 +339,7 @@ func (iface ArchetypeInterface) RunBranchConcurrently(branches ...branch) error wg.Wait() index := <-ch - branchResourceMap := branchesResourceMap[index] - for _, forkedResource := range branchResourceMap { - forkedResource.LinkState() - } - - // Do work to maintain the work done by branch (take their branchResourceMap and write it in) + iface.LinkIFace(ifaceMap[index]) fmt.Println("Finished critical section") @@ -186,7 +348,7 @@ func (iface ArchetypeInterface) RunBranchConcurrently(branches ...branch) error // GetConstant returns the constant operator bound to the given name as a variadic Go function. // The function is generated in DefineConstantOperator, and is expected to check its own arguments. -func (iface ArchetypeInterface) GetConstant(name string) func(args ...tla.TLAValue) tla.TLAValue { +func (iface *ArchetypeInterface) GetConstant(name string) func(args ...tla.TLAValue) tla.TLAValue { fn, wasFound := iface.ctx.constantDefns[name] if !wasFound { panic(fmt.Errorf("could not find constant definition %s", name)) @@ -194,18 +356,52 @@ func (iface ArchetypeInterface) GetConstant(name string) func(args ...tla.TLAVal return fn } +func (iface *ArchetypeInterface) getResourceByHandle(handle ArchetypeResourceHandle) ArchetypeResource { + node := iface + + for node != nil { + res, ok := node.ForkedResources[handle] + if ok { + return res + } + + node = node.parent + } + + panic(fmt.Errorf("could not find resource with name %v", handle)) + + //node := ctx.forkedResourceTree.root + //for { + // if node == nil { + // panic(fmt.Errorf("could not find resource with name %v", handle)) + // } + // + // res, ok := node.ForkedResources[handle] + // if ok { + // return res + // } + // node = node.parent + //} + + //res, ok := ctx.resources[handle] + //if !ok { + // panic(fmt.Errorf("could not find resource with name %v", handle)) + //} + //return res +} + // RequireArchetypeResource returns a handle to the archetype resource with the given name. It panics if this resource // does not exist. -func (iface ArchetypeInterface) RequireArchetypeResource(name string) ArchetypeResourceHandle { +func (iface *ArchetypeInterface) RequireArchetypeResource(name string) ArchetypeResourceHandle { handle := ArchetypeResourceHandle(name) - _ = iface.ctx.getResourceByHandle(handle) + _ = iface.getResourceByHandle(handle) return handle } // RequireArchetypeResourceRef returns a handle to the archetype resource with the given name, when the name refers // to a resource that was passed by ref in MPCal (in Go, ref-passing has an extra indirection that must be followed). // If the resource does not exist, or an invalid indirection is used, this method will panic. -func (iface ArchetypeInterface) RequireArchetypeResourceRef(name string) (ArchetypeResourceHandle, error) { +func (iface *ArchetypeInterface) RequireArchetypeResourceRef(name string) (ArchetypeResourceHandle, error) { ptr := iface.RequireArchetypeResource(name) ptrVal, err := iface.Read(ptr, nil) if err != nil { @@ -216,25 +412,25 @@ func (iface ArchetypeInterface) RequireArchetypeResourceRef(name string) (Archet // EnsureArchetypeResourceLocal ensures that a local state variable exists (local to an archetype or procedure), creating // it with the given default value if not. -func (iface ArchetypeInterface) EnsureArchetypeResourceLocal(name string, value tla.TLAValue) { +func (iface *ArchetypeInterface) EnsureArchetypeResourceLocal(name string, value tla.TLAValue) { _ = iface.ctx.ensureArchetypeResource(name, LocalArchetypeResourceMaker(value)) } // ReadArchetypeResourceLocal is a short-cut to reading a local state variable, which, unlike other resources, is // statically known to not require any critical section management. It will return the resource's value as-is, and // will crash if the named resource isn't exactly a local state variable. -func (iface ArchetypeInterface) ReadArchetypeResourceLocal(name string) tla.TLAValue { - return iface.ctx.getResourceByHandle(ArchetypeResourceHandle(name)).(*LocalArchetypeResource).value +func (iface *ArchetypeInterface) ReadArchetypeResourceLocal(name string) tla.TLAValue { + return iface.getResourceByHandle(ArchetypeResourceHandle(name)).(*LocalArchetypeResource).value } -func (iface ArchetypeInterface) getCriticalSection(name string) MPCalCriticalSection { +func (iface *ArchetypeInterface) getCriticalSection(name string) MPCalCriticalSection { if criticalSection, ok := iface.ctx.jumpTable[name]; ok { return criticalSection } panic(fmt.Errorf("could not find critical section %s", name)) } -func (iface ArchetypeInterface) getProc(name string) MPCalProc { +func (iface *ArchetypeInterface) getProc(name string) MPCalProc { if proc, ok := iface.ctx.procTable[name]; ok { return proc } @@ -243,14 +439,14 @@ func (iface ArchetypeInterface) getProc(name string) MPCalProc { var defaultLocalArchetypeResourceMaker = LocalArchetypeResourceMaker(tla.TLAValue{}) -func (iface ArchetypeInterface) ensureArchetypeResourceLocalWithDefault(name string) ArchetypeResourceHandle { +func (iface *ArchetypeInterface) ensureArchetypeResourceLocalWithDefault(name string) ArchetypeResourceHandle { return iface.ctx.ensureArchetypeResource(name, defaultLocalArchetypeResourceMaker) } // Goto sets the running archetype's program counter to the target value. // It will panic if the target is not a valid label name. // This method should be called at the end of a critical section. -func (iface ArchetypeInterface) Goto(target string) error { +func (iface *ArchetypeInterface) Goto(target string) error { _ = iface.getCriticalSection(target) // crash now if the new pc isn't in the jump table pc := iface.RequireArchetypeResource(".pc") return iface.Write(pc, nil, tla.MakeTLAString(target)) @@ -265,7 +461,7 @@ func (iface ArchetypeInterface) Goto(target string) error { // - jump to the callee's first label via Goto // // This method should be called at the end of a critical section. -func (iface ArchetypeInterface) Call(procName string, returnPC string, argVals ...tla.TLAValue) error { +func (iface *ArchetypeInterface) Call(procName string, returnPC string, argVals ...tla.TLAValue) error { proc := iface.getProc(procName) stack := iface.RequireArchetypeResource(".stack") stackVal, err := iface.Read(stack, nil) @@ -333,7 +529,7 @@ func (iface ArchetypeInterface) Call(procName string, returnPC string, argVals . // Note: like Return, this should never be called outside a procedure, as it relies on an existing stack frame. // // This method, like those it wraps, should be called at the end of a critical section. -func (iface ArchetypeInterface) TailCall(procName string, argVals ...tla.TLAValue) error { +func (iface *ArchetypeInterface) TailCall(procName string, argVals ...tla.TLAValue) error { // pull the top-of-stack return address from the initial stack, so we can use it in the tail-call process below stack := iface.RequireArchetypeResource(".stack") stackVal, err := iface.Read(stack, nil) @@ -366,7 +562,7 @@ func (iface ArchetypeInterface) TailCall(procName string, argVals ...tla.TLAValu // is needed for that. // // This method should be called at the end of a critical section. -func (iface ArchetypeInterface) Return() error { +func (iface *ArchetypeInterface) Return() error { stack := iface.RequireArchetypeResource(".stack") // rewrite the stack, "popping" one the head element stackVal, err := iface.Read(stack, nil) diff --git a/distsys/mpcalctx.go b/distsys/mpcalctx.go index d7399a79d..8b897839f 100644 --- a/distsys/mpcalctx.go +++ b/distsys/mpcalctx.go @@ -39,8 +39,8 @@ func MakeMPCalJumpTable(criticalSections ...MPCalCriticalSection) MPCalJumpTable // MPCalCriticalSection holds metadata for a single MPCal critical section type MPCalCriticalSection struct { - Name string // the critical section's full name (in the form ArchetypeOrProcedureName.LabelName) - Body func(iface ArchetypeInterface) error // code for executing this critical section. should be straight-line code that runs in a bounded amount of time. + Name string // the critical section's full name (in the form ArchetypeOrProcedureName.LabelName) + Body func(iface *ArchetypeInterface) error // code for executing this critical section. should be straight-line code that runs in a bounded amount of time. } // MPCalProcTable is an immutable table of all procedures a given collection of archetypes and procedures might call @@ -57,20 +57,20 @@ func MakeMPCalProcTable(procs ...MPCalProc) MPCalProcTable { // MPCalProc holds all metadata necessary for calling an MPCal procedure type MPCalProc struct { - Name string // the procedure's name, as given in the MPCal model - Label string // the fully qualified name of the procedure's first label - StateVars []string // the fuly-qualified names of all the procedure's local state variables, including arguments and refs - PreAmble func(iface ArchetypeInterface) error // code to initialize local state variables, writing any initial values they might have. runs as part of a call to the procedure. + Name string // the procedure's name, as given in the MPCal model + Label string // the fully qualified name of the procedure's first label + StateVars []string // the fuly-qualified names of all the procedure's local state variables, including arguments and refs + PreAmble func(iface *ArchetypeInterface) error // code to initialize local state variables, writing any initial values they might have. runs as part of a call to the procedure. } // MPCalArchetype holds all the metadata necessary to run an archetype, aside from user-provided configuration type MPCalArchetype struct { - Name string // the archetype's name, as it reads in the MPCal source code - Label string // the full label name of the first critical section this archetype should execute - RequiredRefParams, RequiredValParams []string // names of ref and non-ref parameters - JumpTable MPCalJumpTable // a cross-reference to a jump table containing this archetype's critical sections - ProcTable MPCalProcTable // a cross-reference to a table of all MPCal procedures this archetype might call - PreAmble func(iface ArchetypeInterface) // called on archetype start-up, this code should initialize any local variables the archetype has + Name string // the archetype's name, as it reads in the MPCal source code + Label string // the full label name of the first critical section this archetype should execute + RequiredRefParams, RequiredValParams []string // names of ref and non-ref parameters + JumpTable MPCalJumpTable // a cross-reference to a jump table containing this archetype's critical sections + ProcTable MPCalProcTable // a cross-reference to a table of all MPCal procedures this archetype might call + PreAmble func(iface *ArchetypeInterface) // called on archetype start-up, this code should initialize any local variables the archetype has } // ArchetypeResourceHandle encapsulates a reference to an ArchetypeResource. @@ -109,6 +109,16 @@ type ArchetypeResourceMakerStruct struct { ConfigureFn func(res ArchetypeResource) } +//type ForkedResourceNode struct { +// parent *ForkedResourceNode +// ForkedResources map[ArchetypeResourceHandle]ArchetypeResource +// path string +//} + +//type ForkedResourceTree struct { +// root *ForkedResourceNode +//} + var _ ArchetypeResourceMaker = ArchetypeResourceMakerStruct{} func (mkStruct ArchetypeResourceMakerStruct) Make() ArchetypeResource { @@ -132,6 +142,8 @@ type MPCalContext struct { // state for ArchetypeInterface.NextFairnessCounter fairnessCounter FairnessCounter + // Forked resource tree + //forkedResourceTree ForkedResourceTree //branchScheduler BranchScheduler jumpTable MPCalJumpTable @@ -140,7 +152,7 @@ type MPCalContext struct { dirtyResourceHandles map[ArchetypeResourceHandle]bool // iface points right back to this *MPCalContext; used to separate external and internal APIs - iface ArchetypeInterface + iface *ArchetypeInterface constantDefns map[string]func(args ...tla.TLAValue) tla.TLAValue @@ -195,7 +207,15 @@ func NewMPCalContext(self tla.TLAValue, archetype MPCalArchetype, configFns ...M awaitExit: make(chan struct{}), } - ctx.iface = ArchetypeInterface{ctx: ctx} + ctx.iface = &ArchetypeInterface{ + ctx: ctx, + ForkedResources: ctx.resources, + parent: nil, + path: "0", + } + + //root := ForkedResourceNode{ForkedResources: ctx.resources, path: "0"} + //ctx.forkedResourceTree = ForkedResourceTree{root: &root} ctx.ensureArchetypeResource(".pc", LocalArchetypeResourceMaker(tla.MakeTLAString(archetype.Label))) ctx.ensureArchetypeResource(".stack", LocalArchetypeResourceMaker(tla.MakeTLATuple())) @@ -258,7 +278,7 @@ func EnsureArchetypeDerivedRefParam(name string, parentName string, dMaker Deriv if err != nil { panic(fmt.Errorf("error in finding archetype derived ref param parent: %s", err)) } - parentRes := ctx.getResourceByHandle(parentHandle) + parentRes := ctx.iface.getResourceByHandle(parentHandle) maker := dMaker(parentRes) EnsureArchetypeRefParam(name, maker)(ctx) } @@ -392,7 +412,7 @@ func NewMPCalContextWithoutArchetype(configFns ...MPCalContextConfigFn) *MPCalCo ctx := &MPCalContext{ constantDefns: make(map[string]func(args ...tla.TLAValue) tla.TLAValue), } - ctx.iface = ArchetypeInterface{ctx} + ctx.iface = &ArchetypeInterface{ctx: ctx} for _, configFn := range configFns { configFn(ctx) @@ -404,7 +424,7 @@ func NewMPCalContextWithoutArchetype(configFns ...MPCalContextConfigFn) *MPCalCo // IFace provides an ArchetypeInterface, giving access to methods considered MPCal-internal. // This is useful when directly calling pure TLA+ operators using a context constructed via NewMPCalContextWithoutArchetype, // and is one of very few operations that will work on such a context. -func (ctx *MPCalContext) IFace() ArchetypeInterface { +func (ctx *MPCalContext) IFace() *ArchetypeInterface { return ctx.iface } @@ -421,18 +441,31 @@ func (ctx *MPCalContext) ensureArchetypeResource(name string, maker ArchetypeRes return handle } -func (ctx *MPCalContext) getResourceByHandle(handle ArchetypeResourceHandle) ArchetypeResource { - res, ok := ctx.resources[handle] - if !ok { - panic(fmt.Errorf("could not find resource with name %v", handle)) - } - return res -} +//func (ctx *MPCalContext) getResourceByHandle(handle ArchetypeResourceHandle) ArchetypeResource { +// //node := ctx.forkedResourceTree.root +// //for { +// // if node == nil { +// // panic(fmt.Errorf("could not find resource with name %v", handle)) +// // } +// // +// // res, ok := node.ForkedResources[handle] +// // if ok { +// // return res +// // } +// // node = node.parent +// //} +// +// //res, ok := ctx.resources[handle] +// //if !ok { +// // panic(fmt.Errorf("could not find resource with name %v", handle)) +// //} +// //return res +//} func (ctx *MPCalContext) abort() { var nonTrivialAborts []chan struct{} for resHandle := range ctx.dirtyResourceHandles { - ch := ctx.getResourceByHandle(resHandle).Abort() + ch := ctx.iface.getResourceByHandle(resHandle).Abort() if ch != nil { nonTrivialAborts = append(nonTrivialAborts, ch) } @@ -451,7 +484,7 @@ func (ctx *MPCalContext) commit() (err error) { // dispatch all parts of the pre-commit phase asynchronously, so we only wait as long as the slowest resource var nonTrivialPreCommits []chan error for resHandle := range ctx.dirtyResourceHandles { - ch := ctx.getResourceByHandle(resHandle).PreCommit() + ch := ctx.iface.getResourceByHandle(resHandle).PreCommit() if ch != nil { nonTrivialPreCommits = append(nonTrivialPreCommits, ch) } @@ -471,7 +504,7 @@ func (ctx *MPCalContext) commit() (err error) { // same as above, run all the commit processes async var nonTrivialCommits []chan struct{} for resHandle := range ctx.dirtyResourceHandles { - ch := ctx.getResourceByHandle(resHandle).Commit() + ch := ctx.iface.getResourceByHandle(resHandle).Commit() if ch != nil { nonTrivialCommits = append(nonTrivialCommits, ch) } From 1bb44baa7fdb89f903c911ef2fd2a8156393ca30 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Sun, 3 Apr 2022 06:17:19 -0700 Subject: [PATCH 05/16] Added new spec with nested either clauses --- test/files/general/BranchScheduling.tla | 101 +++++++++++++++++++++++- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/test/files/general/BranchScheduling.tla b/test/files/general/BranchScheduling.tla index 59191a4b2..43dec5e03 100644 --- a/test/files/general/BranchScheduling.tla +++ b/test/files/general/BranchScheduling.tla @@ -14,14 +14,55 @@ archetype ABranch() variable i = 0, j = 2, k= 4, mark = {}; { loop: - while (i < 20) { + while (i < 10) { branchlabel: either { i := i + 1; } or { j := j + 5; } or { - k := j + k; + k := i + k; + }; + lbl1: + with (a \in TheSet) { + mark := mark \cup {a}; + }; + \* lbl2: + \* i := i + 1; + \* }; + }; +} + +archetype ANestedBranch() +variable i = 0, j = 2, k= 4, mark = {}; +{ +loop: + while (i < 10) { + branchlabel: + either { + either { + i := i + 1; + } or { + i := i + 2; + } or { + i := i + 3; + } or { + i := i + 4; + } + } or { + either { + j := j + 5; + } or { + j := j + j; + } + } or { + either { + k := i + k; + } or { + k := i + i + k; + } or { + k := i + i + i + k; + } }; lbl1: with (a \in TheSet) { @@ -34,6 +75,7 @@ loop: } process (Branch = 1) == instance ABranch(); +process (Branch = 2) == instance ANestedBranch(); } @@ -47,7 +89,7 @@ process (Branch = 1) == instance ABranch(); variables i = 0; j = 2; k = 4; mark = {}; { loop: - if ((i) < (20)) { + if ((i) < (10)) { goto branchlabel; } else { goto Done; @@ -60,7 +102,7 @@ process (Branch = 1) == instance ABranch(); j := (j) + (5); goto lbl1; } or { - k := (j) + (k); + k := (i) + (k); goto lbl1; }; lbl1: @@ -69,6 +111,57 @@ process (Branch = 1) == instance ABranch(); goto loop; }; } + + process (Branch = 2) + variables i0 = 0; j0 = 2; k0 = 4; mark0 = {}; + { + loop: + if ((i0) < (10)) { + goto branchlabel; + } else { + goto Done; + }; + branchlabel: + either { + either { + i0 := (i0) + (1); + goto lbl1; + } or { + i0 := (i0) + (2); + goto lbl1; + } or { + i0 := (i0) + (3); + goto lbl1; + } or { + i0 := (i0) + (4); + goto lbl1; + }; + } or { + either { + j0 := (j0) + (5); + goto lbl1; + } or { + j0 := (j0) + (j0); + goto lbl1; + }; + } or { + either { + k0 := (i0) + (k0); + goto lbl1; + } or { + k0 := ((i0) + (i0)) + (k0); + goto lbl1; + } or { + k0 := (((i0) + (i0)) + (i0)) + (k0); + goto lbl1; + }; + }; + lbl1: + with (a \in TheSet) { + mark0 := (mark0) \union ({a}); + goto loop; + }; + } } \* END PLUSCAL TRANSLATION From 7d6b957d76b2113deec4ab96f29a724bda22f7d1 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Sun, 3 Apr 2022 17:42:17 -0700 Subject: [PATCH 06/16] Replaced Read and Write with branchhread and branchwrite --- distsys/archetypeinterface.go | 206 ++++++++++++++++++++++++---------- 1 file changed, 146 insertions(+), 60 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index b9dafc27a..0b2a4518e 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -32,34 +32,17 @@ func (iface *ArchetypeInterface) ensureCriticalSectionWith(handle ArchetypeResou // Write models the MPCal statement resourceFromHandle[indices...] := value. // It is expected to be called only from PGo-generated code. func (iface *ArchetypeInterface) Write(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { - iface.ensureCriticalSectionWith(handle) - res := iface.getResourceByHandle(handle) - for _, index := range indices { - res, err = res.Index(index) - if err != nil { - return - } - } - err = res.WriteValue(value) - return -} - -// Read models the MPCal expression resourceFromHandle[indices...]. -// If is expected to be called only from PGo-generated code. -func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { - iface.ensureCriticalSectionWith(handle) - res := iface.getResourceByHandle(handle) - for _, index := range indices { - res, err = res.Index(index) - if err != nil { - return - } - } - value, err = res.ReadValue() - return -} + //iface.ensureCriticalSectionWith(handle) + //res := iface.getResourceByHandle(handle) + //for _, index := range indices { + // res, err = res.Index(index) + // if err != nil { + // return + // } + //} + //err = res.WriteValue(value) + //return -func (iface *ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { iface.ensureCriticalSectionWith(handle) //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] @@ -110,40 +93,20 @@ func (iface *ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, ind } } -//func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue, branchResources BranchResourceMap) (err error) { -// iface.ensureCriticalSectionWith(handle) -// -// res, ok := branchResources[handle] -// if ok { -// // Handle was in the branch resources -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// err = res.WriteValue(value) -// return -// } else { -// // Handle wasn't in the branch resources -// resource := iface.ctx.getResourceByHandle(handle) -// res, err = resource.ForkState() -// -// // Put the new forked resource in the branch resources -// branchResources[handle] = res -// -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// err = res.WriteValue(value) -// return -// } -//} +// Read models the MPCal expression resourceFromHandle[indices...]. +// If is expected to be called only from PGo-generated code. +func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { + //iface.ensureCriticalSectionWith(handle) + //res := iface.getResourceByHandle(handle) + //for _, index := range indices { + // res, err = res.Index(index) + // if err != nil { + // return + // } + //} + //value, err = res.ReadValue() + //return -func (iface *ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { iface.ensureCriticalSectionWith(handle) //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] @@ -182,6 +145,129 @@ func (iface *ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indi } } +//func (iface *ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { +// iface.ensureCriticalSectionWith(handle) +// +// //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] +// res, ok := iface.ForkedResources[handle] +// if ok { +// // Handle was in the branch resources +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// err = res.WriteValue(value) +// return +// } else { +// // Handle wasn't in the branch resources +// +// // Search for resource through tree +// //node := iface.ctx.forkedResourceTree.root.parent +// //var resource ArchetypeResource +// //for { +// // if node == nil { +// // return fmt.Errorf("could not find resource with name %v", handle) +// // } +// // +// // resource, ok = node.ForkedResources[handle] +// // if ok { +// // break +// // } +// // node = node.parent +// //} +// +// resource := iface.getResourceByHandle(handle) +// res, err = resource.ForkState() +// +// // Put the new forked resource in the branch resources +// iface.ForkedResources[handle] = res +// //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res +// +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// err = res.WriteValue(value) +// return +// } +//} + +//func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue, branchResources BranchResourceMap) (err error) { +// iface.ensureCriticalSectionWith(handle) +// +// res, ok := branchResources[handle] +// if ok { +// // Handle was in the branch resources +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// err = res.WriteValue(value) +// return +// } else { +// // Handle wasn't in the branch resources +// resource := iface.ctx.getResourceByHandle(handle) +// res, err = resource.ForkState() +// +// // Put the new forked resource in the branch resources +// branchResources[handle] = res +// +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// err = res.WriteValue(value) +// return +// } +//} + +//func (iface *ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { +// iface.ensureCriticalSectionWith(handle) +// +// //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] +// res, ok := iface.ForkedResources[handle] +// if ok { +// // Handle was in the branch resources +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// value, err = res.ReadValue() +// return +// } else { +// // Handle wasn't in the branch resources +// resource := iface.getResourceByHandle(handle) +// res, err = resource.ForkState() +// +// // Put the new forked resource in the node +// iface.ForkedResources[handle] = res +// //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res +// +// if err != nil { +// return +// } +// for _, index := range indices { +// res, err = res.Index(index) +// if err != nil { +// return +// } +// } +// value, err = res.ReadValue() +// +// return +// } +//} + //func (iface ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue, branchResources BranchResourceMap) (value tla.TLAValue, err error) { // iface.ensureCriticalSectionWith(handle) // From 2e4553a0d96668b71b2f0307ee98ba90709a0fea Mon Sep 17 00:00:00 2001 From: ruchitp Date: Sun, 10 Apr 2022 16:58:54 -0700 Subject: [PATCH 07/16] Exposed abort endpoint for resources and added AbortIFace --- distsys/archetypeinterface.go | 35 ++++++++++++++++++++------- distsys/archetyperesource.go | 12 +++++++++ distsys/resources/channels.go | 15 ++++++++++++ distsys/resources/crdt.go | 5 ++++ distsys/resources/dummy.go | 5 ++++ distsys/resources/fd.go | 5 ++++ distsys/resources/filesystem.go | 5 ++++ distsys/resources/incmap.go | 5 ++++ distsys/resources/localshared.go | 5 ++++ distsys/resources/mailboxes.go | 5 ++++ distsys/resources/nestedarch.go | 5 ++++ distsys/resources/persistent.go | 5 ++++ distsys/resources/placeholder.go | 5 ++++ distsys/resources/relaxedmailboxes.go | 10 ++++++++ distsys/resources/tcpmailboxes.go | 10 ++++++++ distsys/resources/twopc.go | 5 ++++ 16 files changed, 128 insertions(+), 9 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index 0b2a4518e..aeece3662 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -341,16 +341,8 @@ func (iface *ArchetypeInterface) ForkIFace() (*ArchetypeInterface, error) { return childIFace, nil } -// TODO: Look into if dirty resource handles needs to be linked too func (iface *ArchetypeInterface) LinkIFace(childIFace *ArchetypeInterface) error { - //fmt.Println(iface.ctx.forkedResourceTree.root.ForkedResources) - //fmt.Println(childIFace.ctx.forkedResourceTree.root.ForkedResources) - - //for _, forkedResource := range childIFace.ctx.forkedResourceTree.root.ForkedResources { - // forkedResource.LinkState() - //} - for forkedHandle, forkedResource := range childIFace.ForkedResources { _, ok := iface.ForkedResources[forkedHandle] if !ok { @@ -363,6 +355,25 @@ func (iface *ArchetypeInterface) LinkIFace(childIFace *ArchetypeInterface) error return err } } + } + + return nil +} + +func (iface *ArchetypeInterface) AbortIFace(childIFace *ArchetypeInterface) error { + + for forkedHandle, forkedResource := range childIFace.ForkedResources { + _, ok := iface.ForkedResources[forkedHandle] + if !ok { + // Parent iface doesn't contain resource so add it in + iface.ForkedResources[forkedHandle] = forkedResource + } else { + // Parent iface had the resource so abort its state + err := forkedResource.AbortState() + if err != nil { + return err + } + } } @@ -425,7 +436,13 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error wg.Wait() index := <-ch - iface.LinkIFace(ifaceMap[index]) + for ifaceIndex, ifaceBranch := range ifaceMap { + if ifaceIndex == index { + iface.LinkIFace(ifaceBranch) + } else { + iface.AbortIFace(ifaceBranch) + } + } fmt.Println("Finished critical section") diff --git a/distsys/archetyperesource.go b/distsys/archetyperesource.go index 4f1f9678b..e6c397cfa 100644 --- a/distsys/archetyperesource.go +++ b/distsys/archetyperesource.go @@ -56,6 +56,9 @@ type ArchetypeResource interface { // LinkState syncs the data from the caller resource into the forkParent resource. It is intended to be called on a // resource who was forked by a call to ForkState. LinkState() error + // AbortState must revert all changes make by sub-resources whose properties are NOT idempotent. It is intended to + // be called on a resource woh was forked by a call to ForkState + AbortState() error } type ArchetypeResourceLeafMixin struct{} @@ -162,6 +165,10 @@ func (res *LocalArchetypeResource) LinkState() error { return nil } +func (res *LocalArchetypeResource) AbortState() error { + return nil +} + func (res *LocalArchetypeResource) GetState() ([]byte, error) { var writer bytes.Buffer encoder := gob.NewEncoder(&writer) @@ -249,3 +256,8 @@ func (res localArchetypeSubResource) LinkState() error { return nil } + +func (res localArchetypeSubResource) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/channels.go b/distsys/resources/channels.go index 31300ff66..46552f1da 100644 --- a/distsys/resources/channels.go +++ b/distsys/resources/channels.go @@ -83,6 +83,11 @@ func (res *InputChannel) LinkState() error { panic("implement me") } +func (res *InputChannel) AbortState() error { + //TODO implement me + panic("implement me") +} + // OutputChannel wraps a native Go channel, such that an MPCal model may write to that channel. type OutputChannel struct { distsys.ArchetypeResourceLeafMixin @@ -148,6 +153,11 @@ func (res *OutputChannel) LinkState() error { panic("implement me") } +func (res *OutputChannel) AbortState() error { + //TODO implement me + panic("implement me") +} + const singleOutputChannelWriteTimeout = 20 * time.Millisecond type SingleOutputChannel struct { @@ -203,3 +213,8 @@ func (res *SingleOutputChannel) LinkState() error { //TODO implement me panic("implement me") } + +func (res *SingleOutputChannel) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/crdt.go b/distsys/resources/crdt.go index d0c6cc9a4..bc9f85ea5 100644 --- a/distsys/resources/crdt.go +++ b/distsys/resources/crdt.go @@ -197,6 +197,11 @@ func (res *crdt) LinkState() error { panic("implement me") } +func (res *crdt) AbortState() error { + //TODO implement me + panic("implement me") +} + // tryConnectPeers tries to connect to peer nodes with timeout. If dialing // succeeds, retains the client for later RPC. func (res *crdt) tryConnectPeers(selected *immutable.Map) { diff --git a/distsys/resources/dummy.go b/distsys/resources/dummy.go index 2478db3d8..6f98dfc6e 100644 --- a/distsys/resources/dummy.go +++ b/distsys/resources/dummy.go @@ -50,3 +50,8 @@ func (res *Dummy) LinkState() error { //TODO implement me panic("implement me") } + +func (res *Dummy) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/fd.go b/distsys/resources/fd.go index dcb5ade83..384d22b0d 100644 --- a/distsys/resources/fd.go +++ b/distsys/resources/fd.go @@ -408,3 +408,8 @@ func (res *singleFailureDetector) LinkState() error { //TODO implement me panic("implement me") } + +func (res *singleFailureDetector) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/filesystem.go b/distsys/resources/filesystem.go index 5d4fd27d1..8f3fd58e5 100644 --- a/distsys/resources/filesystem.go +++ b/distsys/resources/filesystem.go @@ -101,3 +101,8 @@ func (res *file) LinkState() error { //TODO implement me panic("implement me") } + +func (res *file) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/incmap.go b/distsys/resources/incmap.go index 1cd850148..97ec1c719 100644 --- a/distsys/resources/incmap.go +++ b/distsys/resources/incmap.go @@ -163,3 +163,8 @@ func (res *IncrementalMap) LinkState() error { //TODO implement me panic("implement me") } + +func (res *IncrementalMap) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/localshared.go b/distsys/resources/localshared.go index db4d69f02..53b6005cc 100644 --- a/distsys/resources/localshared.go +++ b/distsys/resources/localshared.go @@ -139,6 +139,11 @@ func (res *LocalShared) LinkState() error { panic("implement me") } +func (res *LocalShared) AbortState() error { + //TODO implement me + panic("implement me") +} + func (res *LocalShared) GetState() ([]byte, error) { if res.acquired == 0 { err := res.sharedRes.acquire(1) diff --git a/distsys/resources/mailboxes.go b/distsys/resources/mailboxes.go index fc40f14bd..8159be354 100644 --- a/distsys/resources/mailboxes.go +++ b/distsys/resources/mailboxes.go @@ -106,3 +106,8 @@ func (res *mailboxesLocalLength) LinkState() error { //TODO implement me panic("implement me") } + +func (res *mailboxesLocalLength) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/nestedarch.go b/distsys/resources/nestedarch.go index 8824fa76f..610087998 100644 --- a/distsys/resources/nestedarch.go +++ b/distsys/resources/nestedarch.go @@ -407,3 +407,8 @@ func (res *nestedArchetype) LinkState() error { //TODO implement me panic("implement me") } + +func (res *nestedArchetype) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/persistent.go b/distsys/resources/persistent.go index dd77e1d2a..47957f91f 100644 --- a/distsys/resources/persistent.go +++ b/distsys/resources/persistent.go @@ -128,3 +128,8 @@ func (res *PersistentResource) LinkState() error { //TODO implement me panic("implement me") } + +func (res *PersistentResource) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/placeholder.go b/distsys/resources/placeholder.go index 12d54e8a1..fd10fc6d0 100644 --- a/distsys/resources/placeholder.go +++ b/distsys/resources/placeholder.go @@ -60,3 +60,8 @@ func (res *PlaceHolder) LinkState() error { //TODO implement me panic("implement me") } + +func (res *PlaceHolder) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/relaxedmailboxes.go b/distsys/resources/relaxedmailboxes.go index c060876dc..e81294203 100644 --- a/distsys/resources/relaxedmailboxes.go +++ b/distsys/resources/relaxedmailboxes.go @@ -197,6 +197,11 @@ func (res *relaxedMailboxesLocal) LinkState() error { panic("implement me") } +func (res *relaxedMailboxesLocal) AbortState() error { + //TODO implement me + panic("implement me") +} + func (res *relaxedMailboxesLocal) length() int { return len(res.readBacklog) + len(res.msgChannel) } @@ -299,3 +304,8 @@ func (res *relaxedMailboxesRemote) LinkState() error { //TODO implement me panic("implement me") } + +func (res *relaxedMailboxesRemote) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/tcpmailboxes.go b/distsys/resources/tcpmailboxes.go index 8d19f83d8..ecacd4041 100644 --- a/distsys/resources/tcpmailboxes.go +++ b/distsys/resources/tcpmailboxes.go @@ -313,6 +313,11 @@ func (res *tcpMailboxesLocal) LinkState() error { panic("implement me") } +func (res *tcpMailboxesLocal) AbortState() error { + //TODO implement me + panic("implement me") +} + func (res *tcpMailboxesLocal) length() int { return len(res.readBacklog) + len(res.msgChannel) } @@ -534,3 +539,8 @@ func (res *tcpMailboxesRemote) LinkState() error { //TODO implement me panic("implement me") } + +func (res *tcpMailboxesRemote) AbortState() error { + //TODO implement me + panic("implement me") +} diff --git a/distsys/resources/twopc.go b/distsys/resources/twopc.go index 08bbae3d1..8f2110e78 100644 --- a/distsys/resources/twopc.go +++ b/distsys/resources/twopc.go @@ -853,6 +853,11 @@ func (res *TwoPCArchetypeResource) LinkState() error { panic("implement me") } +func (res *TwoPCArchetypeResource) AbortState() error { + //TODO implement me + panic("implement me") +} + func (res *TwoPCArchetypeResource) inCriticalSection() bool { return res.criticalSectionState != notInCriticalSection } From 9640dd4b60acd2865ff3f84805f4c28e98145de9 Mon Sep 17 00:00:00 2001 From: fhackett Date: Tue, 12 Apr 2022 17:33:47 -0700 Subject: [PATCH 08/16] Clean up MPCalCtx/ArchetypeInterface split --- distsys/archetypeinterface.go | 276 ++++++++++++---------------------- distsys/archetyperesource.go | 29 +--- distsys/mpcalctx.go | 111 ++++---------- 3 files changed, 138 insertions(+), 278 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index aeece3662..8748e5630 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -13,10 +13,10 @@ import ( // (2) how the MPCal archetype's code accesses its configuration and internal state while running (available via ArchetypeInterface) type ArchetypeInterface struct { ctx *MPCalContext - ForkedResources map[ArchetypeResourceHandle]ArchetypeResource + forkedResources map[ArchetypeResourceHandle]ArchetypeResource parent *ArchetypeInterface path string - //dirtyResourceHandles map[ArchetypeResourceHandle]bool + killCh chan struct{} // not used yet, but intended for resource implementations to detect preemption } // Self returns the associated archetype's self binding. Requires a configured archetype. @@ -26,130 +26,47 @@ func (iface *ArchetypeInterface) Self() tla.TLAValue { } func (iface *ArchetypeInterface) ensureCriticalSectionWith(handle ArchetypeResourceHandle) { - iface.ctx.dirtyResourceHandles[handle] = true + res := iface.getResourceByHandle(handle) + if _, ok := iface.forkedResources[handle]; !ok { + iface.forkedResources[handle] = res.ForkState() + } } // Write models the MPCal statement resourceFromHandle[indices...] := value. // It is expected to be called only from PGo-generated code. func (iface *ArchetypeInterface) Write(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { - //iface.ensureCriticalSectionWith(handle) - //res := iface.getResourceByHandle(handle) - //for _, index := range indices { - // res, err = res.Index(index) - // if err != nil { - // return - // } - //} - //err = res.WriteValue(value) - //return - iface.ensureCriticalSectionWith(handle) - - //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] - res, ok := iface.ForkedResources[handle] - if ok { - // Handle was in the branch resources - for _, index := range indices { - res, err = res.Index(index) - if err != nil { - return - } - } - err = res.WriteValue(value) - return - } else { - // Handle wasn't in the branch resources - - // Search for resource through tree - //node := iface.ctx.forkedResourceTree.root.parent - //var resource ArchetypeResource - //for { - // if node == nil { - // return fmt.Errorf("could not find resource with name %v", handle) - // } - // - // resource, ok = node.ForkedResources[handle] - // if ok { - // break - // } - // node = node.parent - //} - - resource := iface.getResourceByHandle(handle) - res, err = resource.ForkState() - - // Put the new forked resource in the branch resources - iface.ForkedResources[handle] = res - //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res - - for _, index := range indices { - res, err = res.Index(index) - if err != nil { - return - } + res := iface.getResourceByHandle(handle) + for _, index := range indices { + res, err = res.Index(index) + if err != nil { + return } - err = res.WriteValue(value) - return } + err = res.WriteValue(value) + return } // Read models the MPCal expression resourceFromHandle[indices...]. // If is expected to be called only from PGo-generated code. func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { - //iface.ensureCriticalSectionWith(handle) - //res := iface.getResourceByHandle(handle) - //for _, index := range indices { - // res, err = res.Index(index) - // if err != nil { - // return - // } - //} - //value, err = res.ReadValue() - //return - iface.ensureCriticalSectionWith(handle) - - //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] - res, ok := iface.ForkedResources[handle] - if ok { - // Handle was in the branch resources - for _, index := range indices { - res, err = res.Index(index) - if err != nil { - return - } - } - value, err = res.ReadValue() - return - } else { - // Handle wasn't in the branch resources - resource := iface.getResourceByHandle(handle) - res, err = resource.ForkState() - - // Put the new forked resource in the node - iface.ForkedResources[handle] = res - //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res - + res := iface.getResourceByHandle(handle) + for _, index := range indices { + res, err = res.Index(index) if err != nil { return } - for _, index := range indices { - res, err = res.Index(index) - if err != nil { - return - } - } - value, err = res.ReadValue() - - return } + value, err = res.ReadValue() + return } //func (iface *ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { // iface.ensureCriticalSectionWith(handle) // -// //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] -// res, ok := iface.ForkedResources[handle] +// //res, ok := iface.ctx.forkedResourceTree.root.forkedResources[handle] +// res, ok := iface.forkedResources[handle] // if ok { // // Handle was in the branch resources // for _, index := range indices { @@ -171,7 +88,7 @@ func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices [] // // return fmt.Errorf("could not find resource with name %v", handle) // // } // // -// // resource, ok = node.ForkedResources[handle] +// // resource, ok = node.forkedResources[handle] // // if ok { // // break // // } @@ -182,8 +99,8 @@ func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices [] // res, err = resource.ForkState() // // // Put the new forked resource in the branch resources -// iface.ForkedResources[handle] = res -// //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res +// iface.forkedResources[handle] = res +// //iface.ctx.forkedResourceTree.root.forkedResources[handle] = res // // for _, index := range indices { // res, err = res.Index(index) @@ -232,8 +149,8 @@ func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices [] //func (iface *ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { // iface.ensureCriticalSectionWith(handle) // -// //res, ok := iface.ctx.forkedResourceTree.root.ForkedResources[handle] -// res, ok := iface.ForkedResources[handle] +// //res, ok := iface.ctx.forkedResourceTree.root.forkedResources[handle] +// res, ok := iface.forkedResources[handle] // if ok { // // Handle was in the branch resources // for _, index := range indices { @@ -250,8 +167,8 @@ func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices [] // res, err = resource.ForkState() // // // Put the new forked resource in the node -// iface.ForkedResources[handle] = res -// //iface.ctx.forkedResourceTree.root.ForkedResources[handle] = res +// iface.forkedResources[handle] = res +// //iface.ctx.forkedResourceTree.root.forkedResources[handle] = res // // if err != nil { // return @@ -316,68 +233,80 @@ type BranchResourceMap map[ArchetypeResourceHandle]ArchetypeResource //type branch func(branchResources BranchResourceMap) error type branch func(iface *ArchetypeInterface) error -func (iface *ArchetypeInterface) ForkIFace() (*ArchetypeInterface, error) { - childContext := &MPCalContext{ - archetype: iface.ctx.archetype, - self: iface.ctx.self, - // Resources need - fairnessCounter: iface.ctx.fairnessCounter, - jumpTable: iface.ctx.jumpTable, - procTable: iface.ctx.procTable, - - dirtyResourceHandles: make(map[ArchetypeResourceHandle]bool), - - constantDefns: iface.ctx.constantDefns, - allowRun: true, - awaitExit: make(chan struct{}), - } - - childIFace := &ArchetypeInterface{ - ctx: childContext, - ForkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), +func (iface *ArchetypeInterface) ForkIFace() *ArchetypeInterface { + return &ArchetypeInterface{ + ctx: iface.ctx, + forkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), parent: iface, + killCh: make(chan struct{}), } - - return childIFace, nil } -func (iface *ArchetypeInterface) LinkIFace(childIFace *ArchetypeInterface) error { - - for forkedHandle, forkedResource := range childIFace.ForkedResources { - _, ok := iface.ForkedResources[forkedHandle] - if !ok { - // Parent iface doesn't contain resource so add it in - iface.ForkedResources[forkedHandle] = forkedResource - } else { - // Parent iface had the resource so link its state - err := forkedResource.LinkState() - if err != nil { - return err - } +func (iface *ArchetypeInterface) LinkIFace(childIFace *ArchetypeInterface) { + var nonTrivialAborts []chan struct{} + for forkedHandle, forkedResource := range childIFace.forkedResources { + if parentRes, ok := iface.forkedResources[forkedHandle]; ok { + nonTrivialAborts = append(nonTrivialAborts, parentRes.Abort()) } + iface.forkedResources[forkedHandle] = forkedResource } - return nil + for _, ch := range nonTrivialAborts { + <-ch + } } -func (iface *ArchetypeInterface) AbortIFace(childIFace *ArchetypeInterface) error { +func (iface *ArchetypeInterface) abort() { + var nonTrivialAborts []chan struct{} + for _, res := range iface.forkedResources { + ch := res.Abort() + if ch != nil { + nonTrivialAborts = append(nonTrivialAborts, ch) + } + } + for _, ch := range nonTrivialAborts { + <-ch + } +} - for forkedHandle, forkedResource := range childIFace.ForkedResources { - _, ok := iface.ForkedResources[forkedHandle] - if !ok { - // Parent iface doesn't contain resource so add it in - iface.ForkedResources[forkedHandle] = forkedResource - } else { - // Parent iface had the resource so abort its state - err := forkedResource.AbortState() - if err != nil { - return err - } +func (iface *ArchetypeInterface) commit() (err error) { + // dispatch all parts of the pre-commit phase asynchronously, so we only wait as long as the slowest resource + var nonTrivialPreCommits []chan error + for _, res := range iface.forkedResources { + ch := res.PreCommit() + if ch != nil { + nonTrivialPreCommits = append(nonTrivialPreCommits, ch) + } + } + for _, ch := range nonTrivialPreCommits { + localErr := <-ch + if localErr != nil { + err = localErr } + } + // if there was an error, stop now, and expect either (1) total crash, or (2) Abort to be called + if err != nil { + return } - return nil + // same as above, run all the commit processes async + var nonTrivialCommits []chan struct{} + for _, res := range iface.forkedResources { + ch := res.Commit() + if ch != nil { + nonTrivialCommits = append(nonTrivialCommits, ch) + } + } + for _, ch := range nonTrivialCommits { + <-ch + } + + // the go compiler optimizes this to a map clear operation + for resHandle := range iface.forkedResources { + delete(iface.forkedResources, resHandle) + } + return } func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error { @@ -385,46 +314,40 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error fmt.Println("Running critical section") // Create set of forked ifaces - ifaceMap := make(map[int]*ArchetypeInterface) + var childIfaces []*ArchetypeInterface for i, _ := range branches { //node := ForkedResourceNode{ - // ForkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), + // forkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), // parent: iface.ctx.forkedResourceTree.root, // path: fmt.Sprintf("%s.%d", iface.ctx.forkedResourceTree.root.path, i), //} - childIFace, err := iface.ForkIFace() + childIFace := iface.ForkIFace() childIFace.path = fmt.Sprintf("%s.%d", iface.path, i) - - if err != nil { - fmt.Println(err) - return err - } - //childIFace.ctx.forkedResourceTree = ForkedResourceTree{root: &node} - ifaceMap[i] = childIFace + childIfaces = append(childIfaces, childIFace) } // TODO: Remove wait group var wg sync.WaitGroup ch := make(chan int, 1) + wg.Add(len(branches)) for i, _ := range branches { - wg.Add(1) index := i branch := branches[index] go func() { defer wg.Done() // Run branch - branch(ifaceMap[index]) + branch(childIfaces[index]) // Write to channel to see which branch finished first select { case ch <- index: //fmt.Printf("keep branch %d's work \n", index) - fmt.Printf("keep branch %s's work \n", ifaceMap[index].path) + fmt.Printf("keep branch %s's work \n", childIfaces[index].path) default: //fmt.Printf("discard branch %d's work \n", index) //fmt.Printf("discard branch %s's work \n", ifaceMap[index].path) @@ -436,11 +359,12 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error wg.Wait() index := <-ch - for ifaceIndex, ifaceBranch := range ifaceMap { + for ifaceIndex, ifaceBranch := range childIfaces { if ifaceIndex == index { iface.LinkIFace(ifaceBranch) } else { - iface.AbortIFace(ifaceBranch) + iface.abort() + close(iface.killCh) } } @@ -463,7 +387,7 @@ func (iface *ArchetypeInterface) getResourceByHandle(handle ArchetypeResourceHan node := iface for node != nil { - res, ok := node.ForkedResources[handle] + res, ok := node.forkedResources[handle] if ok { return res } @@ -471,7 +395,7 @@ func (iface *ArchetypeInterface) getResourceByHandle(handle ArchetypeResourceHan node = node.parent } - panic(fmt.Errorf("could not find resource with name %v", handle)) + return iface.ctx.getResourceByHandle(handle) //node := ctx.forkedResourceTree.root //for { @@ -479,7 +403,7 @@ func (iface *ArchetypeInterface) getResourceByHandle(handle ArchetypeResourceHan // panic(fmt.Errorf("could not find resource with name %v", handle)) // } // - // res, ok := node.ForkedResources[handle] + // res, ok := node.forkedResources[handle] // if ok { // return res // } diff --git a/distsys/archetyperesource.go b/distsys/archetyperesource.go index e6c397cfa..f29be868f 100644 --- a/distsys/archetyperesource.go +++ b/distsys/archetyperesource.go @@ -52,13 +52,7 @@ type ArchetypeResource interface { // a copy of this current resource along with any other properties required so that it can be run independently. // For now this is a BLOCKING CALL (though perhaps can be converted to NON-BLOCKING by returning a channel) // This is meant to be called before doing concurrent operations with the same resource for critical sections - ForkState() (ArchetypeResource, error) - // LinkState syncs the data from the caller resource into the forkParent resource. It is intended to be called on a - // resource who was forked by a call to ForkState. - LinkState() error - // AbortState must revert all changes make by sub-resources whose properties are NOT idempotent. It is intended to - // be called on a resource woh was forked by a call to ForkState - AbortState() error + ForkState() ArchetypeResource } type ArchetypeResourceLeafMixin struct{} @@ -148,13 +142,13 @@ func (res *LocalArchetypeResource) Close() error { return nil } -func (res *LocalArchetypeResource) ForkState() (ArchetypeResource, error) { +func (res *LocalArchetypeResource) ForkState() ArchetypeResource { return &LocalArchetypeResource{ value: res.value, oldValue: res.oldValue, hasOldValue: res.hasOldValue, forkParent: res, - }, nil + } } func (res *LocalArchetypeResource) LinkState() error { @@ -241,23 +235,14 @@ func (res localArchetypeSubResource) Close() error { return nil } -func (res localArchetypeSubResource) ForkState() (ArchetypeResource, error) { - return localArchetypeSubResource{ - indices: res.indices, - parent: res.parent, - forkParent: &res, - }, nil +func (res localArchetypeSubResource) ForkState() ArchetypeResource { + panic("you should never be able to fork a local sub-resource") } func (res localArchetypeSubResource) LinkState() error { - - res.forkParent.indices = res.indices - res.forkParent.parent = res.parent - - return nil + panic("you should never be able to link a local sub-resource") } func (res localArchetypeSubResource) AbortState() error { - //TODO implement me - panic("implement me") + panic("you should never be able to abort a local sub-resource") } diff --git a/distsys/mpcalctx.go b/distsys/mpcalctx.go index 8b897839f..6d3f47414 100644 --- a/distsys/mpcalctx.go +++ b/distsys/mpcalctx.go @@ -111,7 +111,7 @@ type ArchetypeResourceMakerStruct struct { //type ForkedResourceNode struct { // parent *ForkedResourceNode -// ForkedResources map[ArchetypeResourceHandle]ArchetypeResource +// forkedResources map[ArchetypeResourceHandle]ArchetypeResource // path string //} @@ -149,11 +149,6 @@ type MPCalContext struct { jumpTable MPCalJumpTable procTable MPCalProcTable - dirtyResourceHandles map[ArchetypeResourceHandle]bool - - // iface points right back to this *MPCalContext; used to separate external and internal APIs - iface *ArchetypeInterface - constantDefns map[string]func(args ...tla.TLAValue) tla.TLAValue // whether anything related to Run() is allowed. true if we were created by NewMPCalContext, false otherwise @@ -197,8 +192,6 @@ func NewMPCalContext(self tla.TLAValue, archetype MPCalArchetype, configFns ...M jumpTable: archetype.JumpTable, procTable: archetype.ProcTable, - dirtyResourceHandles: make(map[ArchetypeResourceHandle]bool), - // iface constantDefns: make(map[string]func(args ...tla.TLAValue) tla.TLAValue), @@ -207,14 +200,8 @@ func NewMPCalContext(self tla.TLAValue, archetype MPCalArchetype, configFns ...M awaitExit: make(chan struct{}), } - ctx.iface = &ArchetypeInterface{ - ctx: ctx, - ForkedResources: ctx.resources, - parent: nil, - path: "0", - } - //root := ForkedResourceNode{ForkedResources: ctx.resources, path: "0"} + //root := ForkedResourceNode{forkedResources: ctx.resources, path: "0"} //ctx.forkedResourceTree = ForkedResourceTree{root: &root} ctx.ensureArchetypeResource(".pc", LocalArchetypeResourceMaker(tla.MakeTLAString(archetype.Label))) @@ -274,11 +261,11 @@ func EnsureArchetypeDerivedRefParam(name string, parentName string, dMaker Deriv return func(ctx *MPCalContext) { ctx.requireRunnable() parentRefName := ctx.archetype.Name + "." + parentName - parentHandle, err := ctx.iface.RequireArchetypeResourceRef(parentRefName) + parentHandle, err := ctx.IFace().RequireArchetypeResourceRef(parentRefName) if err != nil { panic(fmt.Errorf("error in finding archetype derived ref param parent: %s", err)) } - parentRes := ctx.iface.getResourceByHandle(parentHandle) + parentRes := ctx.getResourceByHandle(parentHandle) maker := dMaker(parentRes) EnsureArchetypeRefParam(name, maker)(ctx) } @@ -412,7 +399,6 @@ func NewMPCalContextWithoutArchetype(configFns ...MPCalContextConfigFn) *MPCalCo ctx := &MPCalContext{ constantDefns: make(map[string]func(args ...tla.TLAValue) tla.TLAValue), } - ctx.iface = &ArchetypeInterface{ctx: ctx} for _, configFn := range configFns { configFn(ctx) @@ -425,7 +411,13 @@ func NewMPCalContextWithoutArchetype(configFns ...MPCalContextConfigFn) *MPCalCo // This is useful when directly calling pure TLA+ operators using a context constructed via NewMPCalContextWithoutArchetype, // and is one of very few operations that will work on such a context. func (ctx *MPCalContext) IFace() *ArchetypeInterface { - return ctx.iface + return &ArchetypeInterface{ + ctx: ctx, + forkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), + parent: nil, + path: "", + killCh: make(chan struct{}), + } } func (ctx *MPCalContext) ensureArchetypeResource(name string, maker ArchetypeResourceMaker) ArchetypeResourceHandle { @@ -448,7 +440,7 @@ func (ctx *MPCalContext) ensureArchetypeResource(name string, maker ArchetypeRes // // panic(fmt.Errorf("could not find resource with name %v", handle)) // // } // // -// // res, ok := node.ForkedResources[handle] +// // res, ok := node.forkedResources[handle] // // if ok { // // return res // // } @@ -462,62 +454,18 @@ func (ctx *MPCalContext) ensureArchetypeResource(name string, maker ArchetypeRes // //return res //} -func (ctx *MPCalContext) abort() { - var nonTrivialAborts []chan struct{} - for resHandle := range ctx.dirtyResourceHandles { - ch := ctx.iface.getResourceByHandle(resHandle).Abort() - if ch != nil { - nonTrivialAborts = append(nonTrivialAborts, ch) - } - } - for _, ch := range nonTrivialAborts { - <-ch - } - - // the go compiler optimizes this to a map clear operation - for resHandle := range ctx.dirtyResourceHandles { - delete(ctx.dirtyResourceHandles, resHandle) +func (ctx *MPCalContext) getResourceByHandle(handle ArchetypeResourceHandle) ArchetypeResource { + res, ok := ctx.resources[handle] + if !ok { + panic(fmt.Errorf("could not find resource with handle: %v", handle)) } + return res } -func (ctx *MPCalContext) commit() (err error) { - // dispatch all parts of the pre-commit phase asynchronously, so we only wait as long as the slowest resource - var nonTrivialPreCommits []chan error - for resHandle := range ctx.dirtyResourceHandles { - ch := ctx.iface.getResourceByHandle(resHandle).PreCommit() - if ch != nil { - nonTrivialPreCommits = append(nonTrivialPreCommits, ch) - } - } - for _, ch := range nonTrivialPreCommits { - localErr := <-ch - if localErr != nil { - err = localErr - } - } - - // if there was an error, stop now, and expect either (1) total crash, or (2) Abort to be called - if err != nil { - return - } - - // same as above, run all the commit processes async - var nonTrivialCommits []chan struct{} - for resHandle := range ctx.dirtyResourceHandles { - ch := ctx.iface.getResourceByHandle(resHandle).Commit() - if ch != nil { - nonTrivialCommits = append(nonTrivialCommits, ch) - } - } - for _, ch := range nonTrivialCommits { - <-ch - } - - // the go compiler optimizes this to a map clear operation - for resHandle := range ctx.dirtyResourceHandles { - delete(ctx.dirtyResourceHandles, resHandle) - } - return +func (ctx *MPCalContext) requireArchetypeResource(name string) ArchetypeResourceHandle { + handle := ArchetypeResourceHandle(name) + _ = ctx.getResourceByHandle(handle) + return handle } func (ctx *MPCalContext) preRun() { @@ -540,7 +488,7 @@ func (ctx *MPCalContext) preRun() { } // set up any local state variables that the archetype might have - ctx.archetype.PreAmble(ctx.iface) + ctx.archetype.PreAmble(ctx.IFace()) } // Run will execute the archetype loaded into ctx. @@ -596,13 +544,12 @@ func (ctx *MPCalContext) Run() (err error) { // sanity checks and other setup, done here, so you can initialize a context, not call Run, and not get checks ctx.preRun() - pc := ctx.iface.RequireArchetypeResource(".pc") + pc := ctx.requireArchetypeResource(".pc") for { // all error control flow lives here, reached by "continue" from below switch err { case nil: // everything is fine; carry on case ErrCriticalSectionAborted: - ctx.abort() // we want to keep the invariant that always err is nil after the error // handling in the beginning of the loop. It's easier to read the code and // reason about it with this invariant. @@ -624,8 +571,9 @@ func (ctx *MPCalContext) Run() (err error) { default: // pass } + iface := ctx.IFace() var pcVal tla.TLAValue - pcVal, err = ctx.iface.Read(pc, nil) + pcVal, err = iface.Read(pc, nil) if err != nil { continue } @@ -634,13 +582,16 @@ func (ctx *MPCalContext) Run() (err error) { ctx.fairnessCounter.BeginCriticalSection(pcValStr) //ctx.branchScheduler.BeginCriticalSection(pcValStr) - criticalSection := ctx.iface.getCriticalSection(pcValStr) + criticalSection := iface.getCriticalSection(pcValStr) //fmt.Println(criticalSection) - err = criticalSection.Body(ctx.iface) + err = criticalSection.Body(iface) if err != nil { + if err == ErrCriticalSectionAborted { + iface.abort() + } continue } - err = ctx.commit() + err = iface.commit() } } From f418f61cb55539b388e696ee8aee2ffb2f0fc7f0 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Sun, 17 Apr 2022 13:26:21 -0700 Subject: [PATCH 09/16] Added slight edits to testing output --- .../BranchScheduling.go | 354 +++++++++++++----- 1 file changed, 260 insertions(+), 94 deletions(-) diff --git a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go index 50ea08524..96f47d098 100644 --- a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go +++ b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go @@ -19,7 +19,7 @@ var procTable = distsys.MakeMPCalProcTable() var jumpTable = distsys.MakeMPCalJumpTable( distsys.MPCalCriticalSection{ Name: "ABranch.loop", - Body: func(iface distsys.ArchetypeInterface) error { + Body: func(iface *distsys.ArchetypeInterface) error { var err error _ = err i := iface.RequireArchetypeResource("ABranch.i") @@ -28,7 +28,7 @@ var jumpTable = distsys.MakeMPCalJumpTable( if err != nil { return err } - if tla.TLA_LessThanSymbol(condition, tla.MakeTLANumber(20)).AsBool() { + if tla.TLA_LessThanSymbol(condition, tla.MakeTLANumber(10)).AsBool() { return iface.Goto("ABranch.branchlabel") } else { return iface.Goto("ABranch.Done") @@ -38,7 +38,7 @@ var jumpTable = distsys.MakeMPCalJumpTable( }, distsys.MPCalCriticalSection{ Name: "ABranch.branchlabel", - Body: func(iface distsys.ArchetypeInterface) error { + Body: func(iface *distsys.ArchetypeInterface) error { var err error _ = err i0 := iface.RequireArchetypeResource("ABranch.i") @@ -53,134 +53,61 @@ var jumpTable = distsys.MakeMPCalJumpTable( fmt.Printf("k: %v\n", val) return iface.RunBranchConcurrently( - func(branchResourceMap distsys.BranchResourceMap) error { - fmt.Println("CASE 0") + //switch iface.NextFairnessCounter("ABranch.branchlabel.0", 3) { + func(iface *distsys.ArchetypeInterface) error { + //fmt.Println("CASE 0") var exprRead tla.TLAValue - exprRead, err = iface.BranchRead(i0, []tla.TLAValue{}, branchResourceMap) + exprRead, err = iface.Read(i0, []tla.TLAValue{}) if err != nil { return err } - err = iface.BranchWrite(i0, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead, tla.MakeTLANumber(1)), branchResourceMap) + err = iface.Write(i0, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead, tla.MakeTLANumber(1))) if err != nil { return err } - - val, _ := iface.Read(i0, []tla.TLAValue{}) - fmt.Printf("i0: %v\n", val) - val, _ = iface.Read(j, []tla.TLAValue{}) - fmt.Printf("j: %v\n", val) - val, _ = iface.Read(k, []tla.TLAValue{}) - fmt.Printf("k: %v\n", val) - return iface.Goto("ABranch.lbl1") }, - func(branchResourceMap distsys.BranchResourceMap) error { - fmt.Println("CASE 1") + func(iface *distsys.ArchetypeInterface) error { + //fmt.Println("CASE 1") var exprRead0 tla.TLAValue - exprRead0, err = iface.BranchRead(j, []tla.TLAValue{}, branchResourceMap) + exprRead0, err = iface.Read(j, []tla.TLAValue{}) if err != nil { return err } - err = iface.BranchWrite(j, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead0, tla.MakeTLANumber(5)), branchResourceMap) + err = iface.Write(j, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead0, tla.MakeTLANumber(5))) if err != nil { return err } - - val, _ := iface.Read(i0, []tla.TLAValue{}) - fmt.Printf("i0: %v\n", val) - val, _ = iface.Read(j, []tla.TLAValue{}) - fmt.Printf("j: %v\n", val) - val, _ = iface.Read(k, []tla.TLAValue{}) - fmt.Printf("k: %v\n", val) - return iface.Goto("ABranch.lbl1") }, - func(branchResourceMap distsys.BranchResourceMap) error { - fmt.Println("CASE 2") + func(iface *distsys.ArchetypeInterface) error { + //fmt.Println("CASE 2") var exprRead1 tla.TLAValue - exprRead1, err = iface.BranchRead(j, []tla.TLAValue{}, branchResourceMap) + exprRead1, err = iface.Read(i0, []tla.TLAValue{}) if err != nil { return err } var exprRead2 tla.TLAValue - exprRead2, err = iface.BranchRead(k, []tla.TLAValue{}, branchResourceMap) + exprRead2, err = iface.Read(k, []tla.TLAValue{}) if err != nil { return err } - err = iface.BranchWrite(k, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead1, exprRead2), branchResourceMap) + err = iface.Write(k, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead1, exprRead2)) if err != nil { return err } - - val, _ := iface.Read(i0, []tla.TLAValue{}) - fmt.Printf("i0: %v\n", val) - val, _ = iface.Read(j, []tla.TLAValue{}) - fmt.Printf("j: %v\n", val) - val, _ = iface.Read(k, []tla.TLAValue{}) - fmt.Printf("k: %v\n", val) - return iface.Goto("ABranch.lbl1") }) - - //Just fork all the variables above into some kind of structure where each go routine gets its own set - //PrepareBranches() - //Switch all cases into functional go routines sharing one channel that lets us know when they are done - //switch iface.NextFairnessCounter("ABranch.branchlabel.0", 3) { - //case 0: - // fmt.Println("CASE 0") - // var exprRead tla.TLAValue - // exprRead, err = iface.Read(i0, []tla.TLAValue{}) - // if err != nil { - // return err - // } - // err = iface.Write(i0, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead, tla.MakeTLANumber(1))) - // if err != nil { - // return err - // } - // return iface.Goto("ABranch.lbl1") - //case 1: - // fmt.Println("CASE 1") - // var exprRead0 tla.TLAValue - // exprRead0, err = iface.Read(j, []tla.TLAValue{}) - // if err != nil { - // return err - // } - // err = iface.Write(j, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead0, tla.MakeTLANumber(5))) - // if err != nil { - // return err - // } - // return iface.Goto("ABranch.lbl1") - //case 2: - // fmt.Println("CASE 2") - // var exprRead1 tla.TLAValue - // exprRead1, err = iface.Read(j, []tla.TLAValue{}) - // if err != nil { - // return err - // } - // var exprRead2 tla.TLAValue - // exprRead2, err = iface.Read(k, []tla.TLAValue{}) - // if err != nil { - // return err - // } - // err = iface.Write(k, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead1, exprRead2)) - // if err != nil { - // return err - // } - // return iface.Goto("ABranch.lbl1") - //default: - // panic("current branch of either matches no code paths!") - //} - //RecognizeBranch() // no statements }, }, distsys.MPCalCriticalSection{ Name: "ABranch.lbl1", - Body: func(iface distsys.ArchetypeInterface) error { + Body: func(iface *distsys.ArchetypeInterface) error { var err error _ = err mark := iface.RequireArchetypeResource("ABranch.mark") - var aRead = TheSet(iface) + var aRead = TheSet(*iface) if aRead.AsSet().Len() == 0 { return distsys.ErrCriticalSectionAborted } @@ -201,7 +128,231 @@ var jumpTable = distsys.MakeMPCalJumpTable( }, distsys.MPCalCriticalSection{ Name: "ABranch.Done", - Body: func(distsys.ArchetypeInterface) error { + Body: func(*distsys.ArchetypeInterface) error { + return distsys.ErrDone + }, + }, + distsys.MPCalCriticalSection{ + Name: "ANestedBranch.loop", + Body: func(iface *distsys.ArchetypeInterface) error { + var err error + _ = err + i3 := iface.RequireArchetypeResource("ANestedBranch.i") + var condition0 tla.TLAValue + condition0, err = iface.Read(i3, []tla.TLAValue{}) + if err != nil { + return err + } + if tla.TLA_LessThanSymbol(condition0, tla.MakeTLANumber(10)).AsBool() { + return iface.Goto("ANestedBranch.branchlabel") + } else { + return iface.Goto("ANestedBranch.Done") + } + // no statements + }, + }, + distsys.MPCalCriticalSection{ + Name: "ANestedBranch.branchlabel", + Body: func(iface *distsys.ArchetypeInterface) error { + var err error + _ = err + i4 := iface.RequireArchetypeResource("ANestedBranch.i") + j1 := iface.RequireArchetypeResource("ANestedBranch.j") + k1 := iface.RequireArchetypeResource("ANestedBranch.k") + + val, _ := iface.Read(i4, []tla.TLAValue{}) + fmt.Printf("i: %v\n", val) + val, _ = iface.Read(j1, []tla.TLAValue{}) + fmt.Printf("j: %v\n", val) + val, _ = iface.Read(k1, []tla.TLAValue{}) + fmt.Printf("k: %v\n", val) + return iface.RunBranchConcurrently( + func(iface *distsys.ArchetypeInterface) error { + return iface.RunBranchConcurrently( + func(iface *distsys.ArchetypeInterface) error { + var exprRead4 tla.TLAValue + exprRead4, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(i4, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead4, tla.MakeTLANumber(1))) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }, + func(iface *distsys.ArchetypeInterface) error { + var exprRead5 tla.TLAValue + exprRead5, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(i4, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead5, tla.MakeTLANumber(2))) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }, + func(iface *distsys.ArchetypeInterface) error { + var exprRead6 tla.TLAValue + exprRead6, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(i4, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead6, tla.MakeTLANumber(3))) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }, + func(iface *distsys.ArchetypeInterface) error { + var exprRead7 tla.TLAValue + exprRead7, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(i4, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead7, tla.MakeTLANumber(4))) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }) + // no statements + }, + func(iface *distsys.ArchetypeInterface) error { + return iface.RunBranchConcurrently( + func(iface *distsys.ArchetypeInterface) error { + var exprRead8 tla.TLAValue + exprRead8, err = iface.Read(j1, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(j1, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead8, tla.MakeTLANumber(5))) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }, + func(iface *distsys.ArchetypeInterface) error { + var exprRead9 tla.TLAValue + exprRead9, err = iface.Read(j1, []tla.TLAValue{}) + if err != nil { + return err + } + var exprRead10 tla.TLAValue + exprRead10, err = iface.Read(j1, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(j1, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead9, exprRead10)) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }) + // no statements + }, + func(iface *distsys.ArchetypeInterface) error { + return iface.RunBranchConcurrently( + func(iface *distsys.ArchetypeInterface) error { + var exprRead11 tla.TLAValue + exprRead11, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + var exprRead12 tla.TLAValue + exprRead12, err = iface.Read(k1, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(k1, []tla.TLAValue{}, tla.TLA_PlusSymbol(exprRead11, exprRead12)) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }, + func(iface *distsys.ArchetypeInterface) error { + var exprRead13 tla.TLAValue + exprRead13, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + var exprRead14 tla.TLAValue + exprRead14, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + var exprRead15 tla.TLAValue + exprRead15, err = iface.Read(k1, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(k1, []tla.TLAValue{}, tla.TLA_PlusSymbol(tla.TLA_PlusSymbol(exprRead13, exprRead14), exprRead15)) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }, + func(iface *distsys.ArchetypeInterface) error { + var exprRead16 tla.TLAValue + exprRead16, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + var exprRead17 tla.TLAValue + exprRead17, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + var exprRead18 tla.TLAValue + exprRead18, err = iface.Read(i4, []tla.TLAValue{}) + if err != nil { + return err + } + var exprRead19 tla.TLAValue + exprRead19, err = iface.Read(k1, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(k1, []tla.TLAValue{}, tla.TLA_PlusSymbol(tla.TLA_PlusSymbol(tla.TLA_PlusSymbol(exprRead16, exprRead17), exprRead18), exprRead19)) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.lbl1") + }) + // no statements + }) + // no statements + }, + }, + distsys.MPCalCriticalSection{ + Name: "ANestedBranch.lbl1", + Body: func(iface *distsys.ArchetypeInterface) error { + var err error + _ = err + mark1 := iface.RequireArchetypeResource("ANestedBranch.mark") + var aRead0 = TheSet(*iface) + if aRead0.AsSet().Len() == 0 { + return distsys.ErrCriticalSectionAborted + } + var a0 tla.TLAValue = aRead0.SelectElement(iface.NextFairnessCounter("ANestedBranch.lbl1.0", uint(aRead0.AsSet().Len()))) + _ = a0 + var exprRead20 tla.TLAValue + exprRead20, err = iface.Read(mark1, []tla.TLAValue{}) + if err != nil { + return err + } + err = iface.Write(mark1, []tla.TLAValue{}, tla.TLA_UnionSymbol(exprRead20, tla.MakeTLASet(a0))) + if err != nil { + return err + } + return iface.Goto("ANestedBranch.loop") + // no statements + }, + }, + distsys.MPCalCriticalSection{ + Name: "ANestedBranch.Done", + Body: func(*distsys.ArchetypeInterface) error { return distsys.ErrDone }, }, @@ -214,10 +365,25 @@ var ABranch = distsys.MPCalArchetype{ RequiredValParams: []string{}, JumpTable: jumpTable, ProcTable: procTable, - PreAmble: func(iface distsys.ArchetypeInterface) { + PreAmble: func(iface *distsys.ArchetypeInterface) { iface.EnsureArchetypeResourceLocal("ABranch.i", tla.MakeTLANumber(0)) iface.EnsureArchetypeResourceLocal("ABranch.j", tla.MakeTLANumber(2)) iface.EnsureArchetypeResourceLocal("ABranch.k", tla.MakeTLANumber(4)) iface.EnsureArchetypeResourceLocal("ABranch.mark", tla.MakeTLASet()) }, } + +var ANestedBranch = distsys.MPCalArchetype{ + Name: "ANestedBranch", + Label: "ANestedBranch.loop", + RequiredRefParams: []string{}, + RequiredValParams: []string{}, + JumpTable: jumpTable, + ProcTable: procTable, + PreAmble: func(iface *distsys.ArchetypeInterface) { + iface.EnsureArchetypeResourceLocal("ANestedBranch.i", tla.MakeTLANumber(0)) + iface.EnsureArchetypeResourceLocal("ANestedBranch.j", tla.MakeTLANumber(2)) + iface.EnsureArchetypeResourceLocal("ANestedBranch.k", tla.MakeTLANumber(4)) + iface.EnsureArchetypeResourceLocal("ANestedBranch.mark", tla.MakeTLASet()) + }, +} From 28041d2ce154a166ff4334edf1bcce3999c21c13 Mon Sep 17 00:00:00 2001 From: fhackett Date: Mon, 18 Apr 2022 19:49:36 -0700 Subject: [PATCH 10/16] WIP: make structural improvements to archetype resource API, including "complicated" example in incmap.go. Warning: this will not compile! --- distsys/archetypeinterface.go | 377 ++++++++-------------------------- distsys/archetyperesource.go | 208 +++++++------------ distsys/mpcalctx.go | 21 +- distsys/resources/incmap.go | 189 ++++++++--------- 4 files changed, 256 insertions(+), 539 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index 8748e5630..6af2a5587 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/UBC-NSS/pgo/distsys/tla" "github.com/benbjohnson/immutable" - "sync" + "sync/atomic" ) // ArchetypeInterface provides an archetype-centric interface to an MPCalContext. @@ -12,11 +12,9 @@ import ( // (1) how to configure and run and MPCal archetype (available via a plain MPCalContext) // (2) how the MPCal archetype's code accesses its configuration and internal state while running (available via ArchetypeInterface) type ArchetypeInterface struct { - ctx *MPCalContext - forkedResources map[ArchetypeResourceHandle]ArchetypeResource - parent *ArchetypeInterface - path string - killCh chan struct{} // not used yet, but intended for resource implementations to detect preemption + ctx *MPCalContext + resourceStates map[ArchetypeResourceHandle]ArchetypeResourceState + killCh chan struct{} // TODO: not used yet, but intended for resource implementations to detect preemption } // Self returns the associated archetype's self binding. Requires a configured archetype. @@ -26,24 +24,22 @@ func (iface *ArchetypeInterface) Self() tla.TLAValue { } func (iface *ArchetypeInterface) ensureCriticalSectionWith(handle ArchetypeResourceHandle) { - res := iface.getResourceByHandle(handle) - if _, ok := iface.forkedResources[handle]; !ok { - iface.forkedResources[handle] = res.ForkState() - } + _ = iface.getResourceStateByHandle(handle) } // Write models the MPCal statement resourceFromHandle[indices...] := value. // It is expected to be called only from PGo-generated code. func (iface *ArchetypeInterface) Write(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { iface.ensureCriticalSectionWith(handle) - res := iface.getResourceByHandle(handle) + state := iface.getResourceStateByHandle(handle) + var component ArchetypeResourceComponent = state for _, index := range indices { - res, err = res.Index(index) + component, err = component.Index(index) if err != nil { return } } - err = res.WriteValue(value) + err = component.WriteValue(iface, value) return } @@ -51,176 +47,18 @@ func (iface *ArchetypeInterface) Write(handle ArchetypeResourceHandle, indices [ // If is expected to be called only from PGo-generated code. func (iface *ArchetypeInterface) Read(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { iface.ensureCriticalSectionWith(handle) - res := iface.getResourceByHandle(handle) + state := iface.getResourceStateByHandle(handle) + var component ArchetypeResourceComponent = state for _, index := range indices { - res, err = res.Index(index) + component, err = component.Index(index) if err != nil { return } } - value, err = res.ReadValue() + value, err = component.ReadValue(iface) return } -//func (iface *ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue) (err error) { -// iface.ensureCriticalSectionWith(handle) -// -// //res, ok := iface.ctx.forkedResourceTree.root.forkedResources[handle] -// res, ok := iface.forkedResources[handle] -// if ok { -// // Handle was in the branch resources -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// err = res.WriteValue(value) -// return -// } else { -// // Handle wasn't in the branch resources -// -// // Search for resource through tree -// //node := iface.ctx.forkedResourceTree.root.parent -// //var resource ArchetypeResource -// //for { -// // if node == nil { -// // return fmt.Errorf("could not find resource with name %v", handle) -// // } -// // -// // resource, ok = node.forkedResources[handle] -// // if ok { -// // break -// // } -// // node = node.parent -// //} -// -// resource := iface.getResourceByHandle(handle) -// res, err = resource.ForkState() -// -// // Put the new forked resource in the branch resources -// iface.forkedResources[handle] = res -// //iface.ctx.forkedResourceTree.root.forkedResources[handle] = res -// -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// err = res.WriteValue(value) -// return -// } -//} - -//func (iface ArchetypeInterface) BranchWrite(handle ArchetypeResourceHandle, indices []tla.TLAValue, value tla.TLAValue, branchResources BranchResourceMap) (err error) { -// iface.ensureCriticalSectionWith(handle) -// -// res, ok := branchResources[handle] -// if ok { -// // Handle was in the branch resources -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// err = res.WriteValue(value) -// return -// } else { -// // Handle wasn't in the branch resources -// resource := iface.ctx.getResourceByHandle(handle) -// res, err = resource.ForkState() -// -// // Put the new forked resource in the branch resources -// branchResources[handle] = res -// -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// err = res.WriteValue(value) -// return -// } -//} - -//func (iface *ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue) (value tla.TLAValue, err error) { -// iface.ensureCriticalSectionWith(handle) -// -// //res, ok := iface.ctx.forkedResourceTree.root.forkedResources[handle] -// res, ok := iface.forkedResources[handle] -// if ok { -// // Handle was in the branch resources -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// value, err = res.ReadValue() -// return -// } else { -// // Handle wasn't in the branch resources -// resource := iface.getResourceByHandle(handle) -// res, err = resource.ForkState() -// -// // Put the new forked resource in the node -// iface.forkedResources[handle] = res -// //iface.ctx.forkedResourceTree.root.forkedResources[handle] = res -// -// if err != nil { -// return -// } -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// value, err = res.ReadValue() -// -// return -// } -//} - -//func (iface ArchetypeInterface) BranchRead(handle ArchetypeResourceHandle, indices []tla.TLAValue, branchResources BranchResourceMap) (value tla.TLAValue, err error) { -// iface.ensureCriticalSectionWith(handle) -// -// res, ok := branchResources[handle] -// if ok { -// // Handle was in the branch resources -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// value, err = res.ReadValue() -// return -// } else { -// // Handle wasn't in the branch resources -// resource := iface.ctx.getResourceByHandle(handle) -// res, err = resource.ForkState() -// -// // Put the new forked resource in the branch resources -// branchResources[handle] = res -// -// if err != nil { -// return -// } -// for _, index := range indices { -// res, err = res.Index(index) -// if err != nil { -// return -// } -// } -// value, err = res.ReadValue() -// return -// } -//} - // NextFairnessCounter returns number [0,ceiling) indicating a non-deterministic branching decision which, // given an MPCal critical section being retried indefinitely with no other changes, will try to guarantee that all // possible non-deterministic branch choices will be attempted. @@ -233,36 +71,22 @@ type BranchResourceMap map[ArchetypeResourceHandle]ArchetypeResource //type branch func(branchResources BranchResourceMap) error type branch func(iface *ArchetypeInterface) error -func (iface *ArchetypeInterface) ForkIFace() *ArchetypeInterface { - return &ArchetypeInterface{ - ctx: iface.ctx, - forkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), - parent: iface, - killCh: make(chan struct{}), - } -} - -func (iface *ArchetypeInterface) LinkIFace(childIFace *ArchetypeInterface) { - var nonTrivialAborts []chan struct{} - for forkedHandle, forkedResource := range childIFace.forkedResources { - if parentRes, ok := iface.forkedResources[forkedHandle]; ok { - nonTrivialAborts = append(nonTrivialAborts, parentRes.Abort()) - } - iface.forkedResources[forkedHandle] = forkedResource +func (iface *ArchetypeInterface) forkIFace() *ArchetypeInterface { + copiedStates := make(map[ArchetypeResourceHandle]ArchetypeResourceState, len(iface.resourceStates)) + for handle, state := range iface.resourceStates { + copiedStates[handle] = state } - - for _, ch := range nonTrivialAborts { - <-ch + return &ArchetypeInterface{ + ctx: iface.ctx, + resourceStates: copiedStates, + killCh: make(chan struct{}), } } func (iface *ArchetypeInterface) abort() { var nonTrivialAborts []chan struct{} - for _, res := range iface.forkedResources { - ch := res.Abort() - if ch != nil { - nonTrivialAborts = append(nonTrivialAborts, ch) - } + for _, res := range iface.resourceStates { + nonTrivialAborts = append(nonTrivialAborts, res.Abort()...) } for _, ch := range nonTrivialAborts { <-ch @@ -272,11 +96,8 @@ func (iface *ArchetypeInterface) abort() { func (iface *ArchetypeInterface) commit() (err error) { // dispatch all parts of the pre-commit phase asynchronously, so we only wait as long as the slowest resource var nonTrivialPreCommits []chan error - for _, res := range iface.forkedResources { - ch := res.PreCommit() - if ch != nil { - nonTrivialPreCommits = append(nonTrivialPreCommits, ch) - } + for _, res := range iface.resourceStates { + nonTrivialPreCommits = append(nonTrivialPreCommits, res.PreCommit()...) } for _, ch := range nonTrivialPreCommits { localErr := <-ch @@ -292,85 +113,78 @@ func (iface *ArchetypeInterface) commit() (err error) { // same as above, run all the commit processes async var nonTrivialCommits []chan struct{} - for _, res := range iface.forkedResources { - ch := res.Commit() - if ch != nil { - nonTrivialCommits = append(nonTrivialCommits, ch) - } + for _, res := range iface.resourceStates { + nonTrivialCommits = append(nonTrivialCommits, res.Commit()...) } for _, ch := range nonTrivialCommits { <-ch } // the go compiler optimizes this to a map clear operation - for resHandle := range iface.forkedResources { - delete(iface.forkedResources, resHandle) + for resHandle := range iface.resourceStates { + delete(iface.resourceStates, resHandle) } return } func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error { - //return iface.ctx.branchScheduler.RunCriticalSection(branches...) - fmt.Println("Running critical section") + if len(branches) == 0 { + return ErrCriticalSectionAborted // no branches => no success + } // Create set of forked ifaces - var childIfaces []*ArchetypeInterface - for i, _ := range branches { - //node := ForkedResourceNode{ - // forkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), - // parent: iface.ctx.forkedResourceTree.root, - // path: fmt.Sprintf("%s.%d", iface.ctx.forkedResourceTree.root.path, i), - //} - - childIFace := iface.ForkIFace() - childIFace.path = fmt.Sprintf("%s.%d", iface.path, i) - //childIFace.ctx.forkedResourceTree = ForkedResourceTree{root: &node} - - childIfaces = append(childIfaces, childIFace) + childIfaces := []*ArchetypeInterface{iface} + for range branches[1:] { + childIfaces = append(childIfaces, iface.forkIFace()) } - // TODO: Remove wait group - var wg sync.WaitGroup + type branchResult struct { + idx int32 + err error + } - ch := make(chan int, 1) - wg.Add(len(branches)) + doneSignal := make(chan struct{}) + result := branchResult{ + idx: -1, + } + var abortCount int32 = 0 - for i, _ := range branches { + for i := range branches { index := i branch := branches[index] + iface := childIfaces[index] go func() { - defer wg.Done() // Run branch - branch(childIfaces[index]) - - // Write to channel to see which branch finished first - select { - case ch <- index: - //fmt.Printf("keep branch %d's work \n", index) - fmt.Printf("keep branch %s's work \n", childIfaces[index].path) - default: - //fmt.Printf("discard branch %d's work \n", index) - //fmt.Printf("discard branch %s's work \n", ifaceMap[index].path) + err := branch(iface) + if err == ErrCriticalSectionAborted { + currentAbortCount := atomic.AddInt32(&abortCount, 1) + if currentAbortCount == int32(len(branches)) { + result.err = ErrCriticalSectionAborted // ok to write, we know we're last + close(doneSignal) // we all aborted, give up + } + iface.abort() // abort on-thread, because abort might block a little + return // we aborted, so, unless we were the last, let someone else maybe succeed + } + if err != ErrCriticalSectionAborted { + // something happened that wasn't an abort. notify the waiting goroutine it was us + // (but only if we were the first to see something; ensure this with atomic CAS) + amIFirst := atomic.CompareAndSwapInt32(&result.idx, -1, int32(index)) + if amIFirst { + result.err = err // write before signal, so the waiting goroutine sees err + close(doneSignal) + } else { + iface.abort() // abort on-thread, because abort might block a little + } } }() } - // Wanna remove this wait group later just here for testing purposes!!! - wg.Wait() - - index := <-ch - for ifaceIndex, ifaceBranch := range childIfaces { - if ifaceIndex == index { - iface.LinkIFace(ifaceBranch) - } else { - iface.abort() - close(iface.killCh) - } + <-doneSignal + if result.idx != -1 && result.err == nil { + // steal resources of successful child to continue, if there is one + iface.resourceStates = childIfaces[result.idx].resourceStates } - - fmt.Println("Finished critical section") - - return nil + return result.err } // GetConstant returns the constant operator bound to the given name as a variadic Go function. @@ -383,45 +197,21 @@ func (iface *ArchetypeInterface) GetConstant(name string) func(args ...tla.TLAVa return fn } -func (iface *ArchetypeInterface) getResourceByHandle(handle ArchetypeResourceHandle) ArchetypeResource { - node := iface - - for node != nil { - res, ok := node.forkedResources[handle] - if ok { - return res - } - - node = node.parent - } - - return iface.ctx.getResourceByHandle(handle) - - //node := ctx.forkedResourceTree.root - //for { - // if node == nil { - // panic(fmt.Errorf("could not find resource with name %v", handle)) - // } - // - // res, ok := node.forkedResources[handle] - // if ok { - // return res - // } - // node = node.parent - //} - - //res, ok := ctx.resources[handle] - //if !ok { - // panic(fmt.Errorf("could not find resource with name %v", handle)) - //} - //return res +func (iface *ArchetypeInterface) getResourceStateByHandle(handle ArchetypeResourceHandle) ArchetypeResourceState { + if state, ok := iface.resourceStates[handle]; ok { + return state + } + res := iface.ctx.getResourceByHandle(handle) + state := res.FreshState() + iface.resourceStates[handle] = state + return state } // RequireArchetypeResource returns a handle to the archetype resource with the given name. It panics if this resource // does not exist. func (iface *ArchetypeInterface) RequireArchetypeResource(name string) ArchetypeResourceHandle { handle := ArchetypeResourceHandle(name) - _ = iface.getResourceByHandle(handle) + _ = iface.getResourceStateByHandle(handle) return handle } @@ -443,13 +233,6 @@ func (iface *ArchetypeInterface) EnsureArchetypeResourceLocal(name string, value _ = iface.ctx.ensureArchetypeResource(name, LocalArchetypeResourceMaker(value)) } -// ReadArchetypeResourceLocal is a short-cut to reading a local state variable, which, unlike other resources, is -// statically known to not require any critical section management. It will return the resource's value as-is, and -// will crash if the named resource isn't exactly a local state variable. -func (iface *ArchetypeInterface) ReadArchetypeResourceLocal(name string) tla.TLAValue { - return iface.getResourceByHandle(ArchetypeResourceHandle(name)).(*LocalArchetypeResource).value -} - func (iface *ArchetypeInterface) getCriticalSection(name string) MPCalCriticalSection { if criticalSection, ok := iface.ctx.jumpTable[name]; ok { return criticalSection diff --git a/distsys/archetyperesource.go b/distsys/archetyperesource.go index f29be868f..a62a7d493 100644 --- a/distsys/archetyperesource.go +++ b/distsys/archetyperesource.go @@ -1,8 +1,6 @@ package distsys import ( - "bytes" - "encoding/gob" "errors" "github.com/UBC-NSS/pgo/distsys/tla" ) @@ -12,54 +10,60 @@ import ( // Many implementations are available under ./resources. // This API describes what is expected of those implementations, and any others. type ArchetypeResource interface { + // Close will be called when the archetype stops running (as a result, it's + // not in the middle of a critical section). Close stops running of any + // background jobs and cleans up the stuff that no longer needed when the + // archetype is not running. Close will be called at most once by the MPCal + // Context. + Close() error + FreshState() ArchetypeResourceState +} + +type ArchetypeResourceState interface { // Abort will be called when the resource should be reset to a state similar to the last Commit. // May return nil. If it doesn't return nil, the channel should notify one time, when the operation is complete. // If it returns nil, the operation is considered complete immediately. - Abort() chan struct{} + Abort() []chan struct{} // PreCommit will be called after any number of ReadValue, WriteValue, or Index operations. // It signals if it is reasonable to go ahead with a Commit. // If the resource might need to back out, it should do it here. // May return nil. If it doesn't return nil, the channel should yield one error value. If the error is nil, // Commit may go ahead. Otherwise, it may not. // Returning nil is considered a short-cut to immediately yielding a nil error. - PreCommit() chan error + PreCommit() []chan error // Commit will be called if no sibling PreCommit calls raised any errors. // It must unconditionally commit current resource state. By necessity, this is the only resource operation that // may block indefinitely. // May return nil. If it doesn't return nil, the channel should notify once the commit is complete. // Returning nil is considered as an immediately successful commit. - Commit() chan struct{} + Commit() []chan struct{} // ReadValue must return the resource's current value. // If the resource is not ready, ErrCriticalSectionAborted may be returned alongside a default TLAValue. // This operation should not block indefinitely. // This makes no sense for a map-like resource, and should be blocked off with ArchetypeResourceMapMixin in that case. - ReadValue() (tla.TLAValue, error) + ReadValue(iface *ArchetypeInterface) (tla.TLAValue, error) // WriteValue must update the resource's current value. // It follows the same conventions as ReadValue. - WriteValue(value tla.TLAValue) error + WriteValue(iface *ArchetypeInterface, value tla.TLAValue) error // Index must return the resource's sub-resource at the given index. // It's unclear when this would be needed, but, if the resource is not ready, then this operation may return // ErrCriticalSectionAborted. // This makes no sense for a value-like resource, and should be blocked off with ArchetypeResourceLeafMixin in that case. - Index(index tla.TLAValue) (ArchetypeResource, error) - // Close will be called when the archetype stops running (as a result, it's - // not in the middle of a critical section). Close stops running of any - // background jobs and cleans up the stuff that no longer needed when the - // archetype is not running. Close will be called at most once by the MPCal - // Context. - Close() error - // ForkState must clone all sub-resources whose properties are NOT idempotent. ForkState returns - // a copy of this current resource along with any other properties required so that it can be run independently. - // For now this is a BLOCKING CALL (though perhaps can be converted to NON-BLOCKING by returning a channel) - // This is meant to be called before doing concurrent operations with the same resource for critical sections - ForkState() ArchetypeResource + Index(index tla.TLAValue) (ArchetypeResourceComponent, error) + ForkState() ArchetypeResourceState +} + +type ArchetypeResourceComponent interface { + Index(index tla.TLAValue) (ArchetypeResourceComponent, error) + ReadValue(iface *ArchetypeInterface) (tla.TLAValue, error) + WriteValue(iface *ArchetypeInterface, value tla.TLAValue) error } type ArchetypeResourceLeafMixin struct{} var ErrArchetypeResourceLeafIndexed = errors.New("internal error: attempted to index a leaf archetype resource") -func (ArchetypeResourceLeafMixin) Index(tla.TLAValue) (ArchetypeResource, error) { +func (ArchetypeResourceLeafMixin) Index(tla.TLAValue) (ArchetypeResourceComponent, error) { return nil, ErrArchetypeResourceLeafIndexed } @@ -67,11 +71,11 @@ type ArchetypeResourceMapMixin struct{} var ErrArchetypeResourceMapReadWrite = errors.New("internal error: attempted to read/write a map archetype resource") -func (ArchetypeResourceMapMixin) ReadValue() (tla.TLAValue, error) { +func (ArchetypeResourceMapMixin) ReadValue(*ArchetypeInterface) (tla.TLAValue, error) { return tla.TLAValue{}, ErrArchetypeResourceMapReadWrite } -func (ArchetypeResourceMapMixin) WriteValue(tla.TLAValue) error { +func (ArchetypeResourceMapMixin) WriteValue(*ArchetypeInterface, tla.TLAValue) error { return ErrArchetypeResourceMapReadWrite } @@ -79,11 +83,7 @@ func (ArchetypeResourceMapMixin) WriteValue(tla.TLAValue) error { // -------------------------------------------------------- type LocalArchetypeResource struct { - hasOldValue bool // if true, this resource has already been written in this critical section - // if this resource is already written in this critical section, oldValue contains prev value - // value always contains the "current" value - value, oldValue tla.TLAValue - forkParent *LocalArchetypeResource + value tla.TLAValue } var _ ArchetypeResource = &LocalArchetypeResource{} @@ -96,153 +96,99 @@ func LocalArchetypeResourceMaker(value tla.TLAValue) ArchetypeResourceMaker { }) } -func (res *LocalArchetypeResource) Abort() chan struct{} { - if res.hasOldValue { - res.value = res.oldValue - res.hasOldValue = false - res.oldValue = tla.TLAValue{} - } - return nil -} - -func (res *LocalArchetypeResource) PreCommit() chan error { +func (res *LocalArchetypeResource) Close() error { return nil } -func (res *LocalArchetypeResource) Commit() chan struct{} { - res.hasOldValue = false - res.oldValue = tla.TLAValue{} - return nil +func (res *LocalArchetypeResource) FreshState() ArchetypeResourceState { + return &localArchetypeResourceState{ + parent: res, + value: res.value, + } } -func (res *LocalArchetypeResource) ReadValue() (tla.TLAValue, error) { - return res.value, nil +type localArchetypeResourceState struct { + parent *LocalArchetypeResource + value tla.TLAValue } -func (res *LocalArchetypeResource) WriteValue(value tla.TLAValue) error { - if !res.hasOldValue { - res.oldValue = res.value - res.hasOldValue = true - } - res.value = value +func (state *localArchetypeResourceState) Abort() []chan struct{} { return nil } -// Index is a special case: a local resource uniquely _can_ be indexed and read/written interchangeably! -// See comment on localArchetypeSubResource -func (res *LocalArchetypeResource) Index(index tla.TLAValue) (ArchetypeResource, error) { - subRes := localArchetypeSubResource{ - indices: nil, - parent: res, - } - return subRes.Index(index) +func (state *localArchetypeResourceState) PreCommit() []chan error { + return nil } -func (res *LocalArchetypeResource) Close() error { +func (state *localArchetypeResourceState) Commit() []chan struct{} { + state.parent.value = state.value return nil } -func (res *LocalArchetypeResource) ForkState() ArchetypeResource { - return &LocalArchetypeResource{ - value: res.value, - oldValue: res.oldValue, - hasOldValue: res.hasOldValue, - forkParent: res, - } +func (state *localArchetypeResourceState) ReadValue(_ *ArchetypeInterface) (tla.TLAValue, error) { + return state.value, nil } -func (res *LocalArchetypeResource) LinkState() error { - res.forkParent.value = res.value - res.forkParent.oldValue = res.oldValue - res.forkParent.hasOldValue = res.hasOldValue - +func (state *localArchetypeResourceState) WriteValue(_ *ArchetypeInterface, value tla.TLAValue) error { + state.value = value return nil } -func (res *LocalArchetypeResource) AbortState() error { - return nil +func (state *localArchetypeResourceState) Index(index tla.TLAValue) (ArchetypeResourceComponent, error) { + return &localArchetypeResourceComponent{ + state: state, + indices: []tla.TLAValue{index}, + }, nil } -func (res *LocalArchetypeResource) GetState() ([]byte, error) { - var writer bytes.Buffer - encoder := gob.NewEncoder(&writer) - err := encoder.Encode(&res.value) - return writer.Bytes(), err +func (state *localArchetypeResourceState) ForkState() ArchetypeResourceState { + return &localArchetypeResourceState{ + parent: state.parent, + value: state.value, + } } -// localArchetypeSubResource is used to implement the no-op case for function-mapping. -// -// It's what happens when you call LocalArchetypeResource.Index, which is what happens when you write code like this: -// i[idx] := i[idx] + 1; \* where i is a local variable -type localArchetypeSubResource struct { - // indices gives the total path from root value, as accumulated from calls to Index, e.g with `i[a][b] := ...` you get []{a, b} +type localArchetypeResourceComponent struct { + state *localArchetypeResourceState + stolen bool indices []tla.TLAValue - // the parent local resource. it does everything important, which is why most methods here just return nil; they shouldn't even be called - parent *LocalArchetypeResource - forkParent *localArchetypeSubResource -} - -var _ ArchetypeResource = &localArchetypeSubResource{} - -func (res localArchetypeSubResource) Abort() chan struct{} { - return nil } -func (res localArchetypeSubResource) PreCommit() chan error { - return nil -} - -func (res localArchetypeSubResource) Commit() chan struct{} { - return nil +func (comp *localArchetypeResourceComponent) Index(index tla.TLAValue) (ArchetypeResourceComponent, error) { + if !comp.stolen { + comp.stolen = true + return &localArchetypeResourceComponent{ + state: comp.state, + indices: append(comp.indices, index), + }, nil + } + return &localArchetypeResourceComponent{ + state: comp.state, + indices: append(append([]tla.TLAValue(nil), comp.indices...), index), + }, nil } -func (res localArchetypeSubResource) ReadValue() (tla.TLAValue, error) { - fn, err := res.parent.ReadValue() +func (comp *localArchetypeResourceComponent) ReadValue(iface *ArchetypeInterface) (tla.TLAValue, error) { + fn, err := comp.state.ReadValue(iface) if err != nil { return tla.TLAValue{}, err } - for _, index := range res.indices { + for _, index := range comp.indices { fn = fn.ApplyFunction(index) } return fn, nil } -func (res localArchetypeSubResource) WriteValue(value tla.TLAValue) error { - fn, err := res.parent.ReadValue() +func (comp *localArchetypeResourceComponent) WriteValue(iface *ArchetypeInterface, value tla.TLAValue) error { + fn, err := comp.state.ReadValue(iface) if err != nil { return err } fn = tla.TLAFunctionSubstitution(fn, []tla.TLAFunctionSubstitutionRecord{{ - Keys: res.indices, + Keys: comp.indices, Value: func(_ tla.TLAValue) tla.TLAValue { return value }, }}) - return res.parent.WriteValue(fn) -} - -func (res localArchetypeSubResource) Index(index tla.TLAValue) (ArchetypeResource, error) { - newIndices := make([]tla.TLAValue, len(res.indices)+1) - lastIdx := copy(newIndices, res.indices) - newIndices[lastIdx] = index - return localArchetypeSubResource{ - indices: newIndices, - parent: res.parent, - }, nil -} - -func (res localArchetypeSubResource) Close() error { - return nil -} - -func (res localArchetypeSubResource) ForkState() ArchetypeResource { - panic("you should never be able to fork a local sub-resource") -} - -func (res localArchetypeSubResource) LinkState() error { - panic("you should never be able to link a local sub-resource") -} - -func (res localArchetypeSubResource) AbortState() error { - panic("you should never be able to abort a local sub-resource") + return comp.state.WriteValue(iface, fn) } diff --git a/distsys/mpcalctx.go b/distsys/mpcalctx.go index 6d3f47414..078fe842e 100644 --- a/distsys/mpcalctx.go +++ b/distsys/mpcalctx.go @@ -111,7 +111,7 @@ type ArchetypeResourceMakerStruct struct { //type ForkedResourceNode struct { // parent *ForkedResourceNode -// forkedResources map[ArchetypeResourceHandle]ArchetypeResource +// resourceStates map[ArchetypeResourceHandle]ArchetypeResource // path string //} @@ -201,7 +201,7 @@ func NewMPCalContext(self tla.TLAValue, archetype MPCalArchetype, configFns ...M awaitExit: make(chan struct{}), } - //root := ForkedResourceNode{forkedResources: ctx.resources, path: "0"} + //root := ForkedResourceNode{resourceStates: ctx.resources, path: "0"} //ctx.forkedResourceTree = ForkedResourceTree{root: &root} ctx.ensureArchetypeResource(".pc", LocalArchetypeResourceMaker(tla.MakeTLAString(archetype.Label))) @@ -412,11 +412,9 @@ func NewMPCalContextWithoutArchetype(configFns ...MPCalContextConfigFn) *MPCalCo // and is one of very few operations that will work on such a context. func (ctx *MPCalContext) IFace() *ArchetypeInterface { return &ArchetypeInterface{ - ctx: ctx, - forkedResources: make(map[ArchetypeResourceHandle]ArchetypeResource), - parent: nil, - path: "", - killCh: make(chan struct{}), + ctx: ctx, + resourceStates: make(map[ArchetypeResourceHandle]ArchetypeResourceState), + killCh: make(chan struct{}), } } @@ -440,7 +438,7 @@ func (ctx *MPCalContext) ensureArchetypeResource(name string, maker ArchetypeRes // // panic(fmt.Errorf("could not find resource with name %v", handle)) // // } // // -// // res, ok := node.forkedResources[handle] +// // res, ok := node.resourceStates[handle] // // if ok { // // return res // // } @@ -468,6 +466,13 @@ func (ctx *MPCalContext) requireArchetypeResource(name string) ArchetypeResource return handle } +// ReadArchetypeResourceLocal is a short-cut to reading a local state variable, which, unlike other resources, is +// statically known to not require any critical section management. It will return the resource's value as-is, and +// will crash if the named resource isn't exactly a local state variable. +func (ctx *MPCalContext) ReadArchetypeResourceLocal(name string) tla.TLAValue { + return ctx.getResourceByHandle(ArchetypeResourceHandle(name)).(*LocalArchetypeResource).value +} + func (ctx *MPCalContext) preRun() { // sanity checks, so we don't try to run with missing resources, refs passed as vals, or vals passed as refs: for _, requiredValParam := range ctx.archetype.RequiredValParams { diff --git a/distsys/resources/incmap.go b/distsys/resources/incmap.go index 97ec1c719..8005829d0 100644 --- a/distsys/resources/incmap.go +++ b/distsys/resources/incmap.go @@ -1,10 +1,12 @@ package resources import ( + "fmt" "github.com/UBC-NSS/pgo/distsys" "github.com/UBC-NSS/pgo/distsys/tla" "github.com/benbjohnson/immutable" "go.uber.org/multierr" + "sync" ) // FillFn maps from an index of a given map resource into a distsys.ArchetypeResourceMaker for the resource @@ -15,10 +17,9 @@ type FillFn func(index tla.TLAValue) distsys.ArchetypeResourceMaker // IncrementalMap is a generic map resource, with hooks to programmatically // realize child resources during execution. type IncrementalMap struct { - distsys.ArchetypeResourceMapMixin + lock sync.Mutex // lock is needed for when we lazily fill realizedMap, potentially from multiple goroutines realizedMap *immutable.Map fillFunction FillFn - dirtyElems *immutable.Map } var _ distsys.ArchetypeResource = &IncrementalMap{} @@ -28,7 +29,6 @@ func IncrementalMapMaker(fillFunction FillFn) distsys.ArchetypeResourceMaker { MakeFn: func() distsys.ArchetypeResource { return &IncrementalMap{ realizedMap: immutable.NewMap(tla.TLAValueHasher{}), - dirtyElems: immutable.NewMap(tla.TLAValueHasher{}), } }, ConfigureFn: func(res distsys.ArchetypeResource) { @@ -38,133 +38,116 @@ func IncrementalMapMaker(fillFunction FillFn) distsys.ArchetypeResourceMaker { } } -func (res *IncrementalMap) Index(index tla.TLAValue) (distsys.ArchetypeResource, error) { - maker := res.fillFunction(index) - if subRes, ok := res.realizedMap.Get(index); ok { - r := subRes.(distsys.ArchetypeResource) - maker.Configure(r) - res.dirtyElems = res.dirtyElems.Set(index, subRes) - return subRes.(distsys.ArchetypeResource), nil +func (res *IncrementalMap) FreshState() distsys.ArchetypeResourceState { + return &incrementalMapState{ + parent: res, + states: immutable.NewMap(tla.TLAValueHasher{}), } - - subRes := maker.Make() - maker.Configure(subRes) - res.realizedMap = res.realizedMap.Set(index, subRes) - res.dirtyElems = res.dirtyElems.Set(index, subRes) - return subRes, nil } -func (res *IncrementalMap) PreCommit() chan error { - var nonTrivialOps []chan error - it := res.dirtyElems.Iterator() +func (res *IncrementalMap) Close() error { + it := res.realizedMap.Iterator() + var err error for !it.Done() { - _, r := it.Next() - ch := r.(distsys.ArchetypeResource).PreCommit() - if ch != nil { - nonTrivialOps = append(nonTrivialOps, ch) - } - } - - if len(nonTrivialOps) != 0 { - doneCh := make(chan error) - go func() { - var err error - for _, ch := range nonTrivialOps { - err = <-ch - if err != nil { - break - } - } - doneCh <- err - }() - return doneCh + _, subResource := it.Next() + err = multierr.Append(err, subResource.(distsys.ArchetypeResource).Close()) } - - return nil + return err } -func (res *IncrementalMap) Commit() chan struct{} { - defer func() { - res.dirtyElems = immutable.NewMap(tla.TLAValueHasher{}) - }() +type incrementalMapState struct { + distsys.ArchetypeResourceMapMixin + parent *IncrementalMap + states *immutable.Map // mapping from tla.TLAValue to distsys.ArchetypeResourceState +} - var nonTrivialOps []chan struct{} - it := res.dirtyElems.Iterator() +func (state *incrementalMapState) Abort() []chan struct{} { + var aborts []chan struct{} + it := state.states.Iterator() for !it.Done() { - _, r := it.Next() - ch := r.(distsys.ArchetypeResource).Commit() - if ch != nil { - nonTrivialOps = append(nonTrivialOps, ch) - } + _, subState := it.Next() + aborts = append(aborts, subState.(distsys.ArchetypeResourceState).Abort()...) } + return aborts +} - if len(nonTrivialOps) != 0 { - doneCh := make(chan struct{}) - go func() { - for _, ch := range nonTrivialOps { - <-ch - } - doneCh <- struct{}{} - }() - return doneCh +func (state *incrementalMapState) PreCommit() []chan error { + var preCommits []chan error + it := state.states.Iterator() + for !it.Done() { + _, subState := it.Next() + preCommits = append(preCommits, subState.(distsys.ArchetypeResourceState).PreCommit()...) } - - return nil + return preCommits } -func (res *IncrementalMap) Abort() chan struct{} { - defer func() { - res.dirtyElems = immutable.NewMap(tla.TLAValueHasher{}) - }() - - var nonTrivialOps []chan struct{} - it := res.dirtyElems.Iterator() +func (state *incrementalMapState) Commit() []chan struct{} { + var commits []chan struct{} + it := state.states.Iterator() for !it.Done() { - _, r := it.Next() - ch := r.(distsys.ArchetypeResource).Abort() - if ch != nil { - nonTrivialOps = append(nonTrivialOps, ch) + _, subState := it.Next() + commits = append(commits, subState.(distsys.ArchetypeResourceState).Commit()...) + } + return commits +} + +func (state *incrementalMapState) Index(index tla.TLAValue) (distsys.ArchetypeResourceComponent, error) { + if _, ok := state.states.Get(index); !ok { + // lock here, not inside the else case, because the check and update might interfere with each other otherwise + state.parent.lock.Lock() + defer state.parent.lock.Unlock() + var resource distsys.ArchetypeResource + if resourceI, ok := state.parent.realizedMap.Get(index); ok { + resource = resourceI.(distsys.ArchetypeResource) + } else { + // lazy fill the realizedMap on demand + maker := state.parent.fillFunction(index) + resource = maker.Make() + maker.Configure(resource) + state.parent.realizedMap = state.parent.realizedMap.Set(index, resource) } + state.states = state.states.Set(index, resource.FreshState()) } + return &incrementalMapComponent{ + state: state, + index: index, + }, nil +} - if len(nonTrivialOps) != 0 { - doneCh := make(chan struct{}) - go func() { - for _, ch := range nonTrivialOps { - <-ch - } - doneCh <- struct{}{} - }() - return doneCh +func (state *incrementalMapState) ForkState() distsys.ArchetypeResourceState { + forkedStatesBuilder := immutable.NewMapBuilder(tla.TLAValueHasher{}) + it := state.states.Iterator() + for !it.Done() { + index, subState := it.Next() + forkedStatesBuilder.Set(index, subState.(distsys.ArchetypeResourceState).ForkState()) + } + return &incrementalMapState{ + parent: state.parent, + states: forkedStatesBuilder.Map(), } +} - return nil +type incrementalMapComponent struct { + state *incrementalMapState + index tla.TLAValue } -func (res *IncrementalMap) Close() error { - var err error - // Note that we should close all the realized elements, not just the dirty - // ones. - it := res.realizedMap.Iterator() - for !it.Done() { - _, r := it.Next() - cerr := r.(distsys.ArchetypeResource).Close() - err = multierr.Append(err, cerr) +func (comp *incrementalMapComponent) getState() distsys.ArchetypeResourceState { + state, ok := comp.state.states.Get(comp.index) + if !ok { + panic(fmt.Errorf("resource not found at index %v, should be unreachable", comp.index)) } - return err + return state.(distsys.ArchetypeResourceState) } -func (res *IncrementalMap) ForkState() (distsys.ArchetypeResource, error) { - //TODO implement me - panic("implement me") +func (comp *incrementalMapComponent) Index(index tla.TLAValue) (distsys.ArchetypeResourceComponent, error) { + return comp.getState().Index(index) } -func (res *IncrementalMap) LinkState() error { - //TODO implement me - panic("implement me") +func (comp *incrementalMapComponent) ReadValue(iface *distsys.ArchetypeInterface) (tla.TLAValue, error) { + return comp.getState().ReadValue(iface) } -func (res *IncrementalMap) AbortState() error { - //TODO implement me - panic("implement me") +func (comp *incrementalMapComponent) WriteValue(iface *distsys.ArchetypeInterface, value tla.TLAValue) error { + return comp.getState().WriteValue(iface, value) } From 3d6341338bb7bac6c28831d76ffce1a3d0bbd16d Mon Sep 17 00:00:00 2001 From: ruchitp Date: Tue, 19 Apr 2022 16:35:44 -0700 Subject: [PATCH 11/16] Slight tweak --- distsys/archetypeinterface.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index 6af2a5587..1b8fbc061 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/UBC-NSS/pgo/distsys/tla" "github.com/benbjohnson/immutable" + "sync" "sync/atomic" ) @@ -74,7 +75,7 @@ type branch func(iface *ArchetypeInterface) error func (iface *ArchetypeInterface) forkIFace() *ArchetypeInterface { copiedStates := make(map[ArchetypeResourceHandle]ArchetypeResourceState, len(iface.resourceStates)) for handle, state := range iface.resourceStates { - copiedStates[handle] = state + copiedStates[handle] = state.ForkState() } return &ArchetypeInterface{ ctx: iface.ctx, @@ -128,6 +129,8 @@ func (iface *ArchetypeInterface) commit() (err error) { } func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error { + fmt.Println("Starting critical section") + if len(branches) == 0 { return ErrCriticalSectionAborted // no branches => no success } @@ -149,11 +152,15 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error } var abortCount int32 = 0 + temp := sync.WaitGroup{} + temp.Add(3) + for i := range branches { index := i branch := branches[index] iface := childIfaces[index] go func() { + defer temp.Done() // Run branch err := branch(iface) if err == ErrCriticalSectionAborted { @@ -163,13 +170,15 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error close(doneSignal) // we all aborted, give up } iface.abort() // abort on-thread, because abort might block a little - return // we aborted, so, unless we were the last, let someone else maybe succeed + + return // we aborted, so, unless we were the last, let someone else maybe succeed } if err != ErrCriticalSectionAborted { // something happened that wasn't an abort. notify the waiting goroutine it was us // (but only if we were the first to see something; ensure this with atomic CAS) amIFirst := atomic.CompareAndSwapInt32(&result.idx, -1, int32(index)) if amIFirst { + fmt.Printf("%d chosen\n", index) result.err = err // write before signal, so the waiting goroutine sees err close(doneSignal) } else { @@ -181,9 +190,20 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error <-doneSignal if result.idx != -1 && result.err == nil { + //for h, r := range iface.resourceStates { + // fmt.Printf("{%v, %v}\n", h, r) + //} + for h, r := range childIfaces[result.idx].resourceStates { + fmt.Printf("{%v, %v}\n", h, r) + } // steal resources of successful child to continue, if there is one iface.resourceStates = childIfaces[result.idx].resourceStates } + fmt.Println("result") + for h, r := range iface.resourceStates { + fmt.Printf("{%v, %v}\n", h, r) + } + temp.Wait() return result.err } From 8f7c1f53a5dba3a60d47c3ec5d9ff207a04e7358 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Tue, 19 Apr 2022 16:51:02 -0700 Subject: [PATCH 12/16] Comments --- distsys/archetypeinterface.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index 1b8fbc061..d9b76d700 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -193,16 +193,16 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error //for h, r := range iface.resourceStates { // fmt.Printf("{%v, %v}\n", h, r) //} - for h, r := range childIfaces[result.idx].resourceStates { - fmt.Printf("{%v, %v}\n", h, r) - } + //for h, r := range childIfaces[result.idx].resourceStates { + // fmt.Printf("{%v, %v}\n", h, r) + //} // steal resources of successful child to continue, if there is one iface.resourceStates = childIfaces[result.idx].resourceStates } - fmt.Println("result") - for h, r := range iface.resourceStates { - fmt.Printf("{%v, %v}\n", h, r) - } + //fmt.Println("result") + //for h, r := range iface.resourceStates { + // fmt.Printf("{%v, %v}\n", h, r) + //} temp.Wait() return result.err } From 5845815c95d5b959ec9ea0d982100ad3c395b067 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Tue, 19 Apr 2022 16:51:47 -0700 Subject: [PATCH 13/16] More comments --- .../BranchScheduling_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go index c4a8113f6..583a83fe9 100644 --- a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go +++ b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go @@ -1,11 +1,10 @@ package branchscheduling import ( - "testing" - "time" - + "fmt" "github.com/UBC-NSS/pgo/distsys" "github.com/UBC-NSS/pgo/distsys/tla" + "testing" ) //run gogen -s test/files/general/BranchScheduling.tla -o test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go @@ -15,6 +14,9 @@ func TestBasic(t *testing.T) { ctx := distsys.NewMPCalContext(tla.MakeTLAString("self"), ABranch) go func() { errCh <- ctx.Run() + fmt.Println(ctx.ReadArchetypeResourceLocal("ABranch.i")) + fmt.Println(ctx.ReadArchetypeResourceLocal("ABranch.j")) + fmt.Println(ctx.ReadArchetypeResourceLocal("ABranch.k")) }() select { @@ -22,7 +24,7 @@ func TestBasic(t *testing.T) { if err != nil { panic(err) } - case <-time.After(2 * time.Second): - t.Fatalf("timeout: ABranch should eventually (within 5 seconds) terminate") + //case <-time.After(2 * time.Second): + // t.Fatalf("timeout: ABranch should eventually (within 5 seconds) terminate") } } From f4ecbd931c00cd39cfaa3ef66fd747d10a2c5fa2 Mon Sep 17 00:00:00 2001 From: fhackett Date: Tue, 19 Apr 2022 17:46:46 -0700 Subject: [PATCH 14/16] WIP: Fix concurrency bug in either implementation --- distsys/archetypeinterface.go | 63 +++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index d9b76d700..e8e704d1b 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/UBC-NSS/pgo/distsys/tla" "github.com/benbjohnson/immutable" - "sync" "sync/atomic" ) @@ -85,19 +84,28 @@ func (iface *ArchetypeInterface) forkIFace() *ArchetypeInterface { } func (iface *ArchetypeInterface) abort() { + resourceStates := iface.resourceStates var nonTrivialAborts []chan struct{} - for _, res := range iface.resourceStates { + for _, res := range resourceStates { nonTrivialAborts = append(nonTrivialAborts, res.Abort()...) } for _, ch := range nonTrivialAborts { <-ch } + + // the go compiler optimizes this to a map clear operation + // secretly important: this also means repeated aborts will become no-ops, + // which simplifies non-deterministic cases that end up behaving like that + for resHandle := range resourceStates { + delete(resourceStates, resHandle) + } } func (iface *ArchetypeInterface) commit() (err error) { + resourceStates := iface.resourceStates // dispatch all parts of the pre-commit phase asynchronously, so we only wait as long as the slowest resource var nonTrivialPreCommits []chan error - for _, res := range iface.resourceStates { + for _, res := range resourceStates { nonTrivialPreCommits = append(nonTrivialPreCommits, res.PreCommit()...) } for _, ch := range nonTrivialPreCommits { @@ -114,7 +122,7 @@ func (iface *ArchetypeInterface) commit() (err error) { // same as above, run all the commit processes async var nonTrivialCommits []chan struct{} - for _, res := range iface.resourceStates { + for _, res := range resourceStates { nonTrivialCommits = append(nonTrivialCommits, res.Commit()...) } for _, ch := range nonTrivialCommits { @@ -122,8 +130,8 @@ func (iface *ArchetypeInterface) commit() (err error) { } // the go compiler optimizes this to a map clear operation - for resHandle := range iface.resourceStates { - delete(iface.resourceStates, resHandle) + for resHandle := range resourceStates { + delete(resourceStates, resHandle) } return } @@ -136,7 +144,12 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error } // Create set of forked ifaces - childIfaces := []*ArchetypeInterface{iface} + childIfaces := []*ArchetypeInterface{&ArchetypeInterface{ + ctx: iface.ctx, + resourceStates: iface.resourceStates, + killCh: nil, // TODO + }} + iface.resourceStates = nil for range branches[1:] { childIfaces = append(childIfaces, iface.forkIFace()) } @@ -152,15 +165,15 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error } var abortCount int32 = 0 - temp := sync.WaitGroup{} - temp.Add(3) + //temp := sync.WaitGroup{} + //temp.Add(3) for i := range branches { index := i branch := branches[index] iface := childIfaces[index] go func() { - defer temp.Done() + //defer temp.Done() // Run branch err := branch(iface) if err == ErrCriticalSectionAborted { @@ -173,22 +186,21 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error return // we aborted, so, unless we were the last, let someone else maybe succeed } - if err != ErrCriticalSectionAborted { - // something happened that wasn't an abort. notify the waiting goroutine it was us - // (but only if we were the first to see something; ensure this with atomic CAS) - amIFirst := atomic.CompareAndSwapInt32(&result.idx, -1, int32(index)) - if amIFirst { - fmt.Printf("%d chosen\n", index) - result.err = err // write before signal, so the waiting goroutine sees err - close(doneSignal) - } else { - iface.abort() // abort on-thread, because abort might block a little - } + // something happened that wasn't an abort. notify the waiting goroutine it was us + // (but only if we were the first to see something; ensure this with atomic CAS) + amIFirst := atomic.CompareAndSwapInt32(&result.idx, -1, int32(index)) + if amIFirst { + //fmt.Printf("%d chosen\n", index) + // write before signal, so the waiting goroutine sees err + result.err = err + close(doneSignal) + } else { + iface.abort() // abort on-thread, because abort might block a little } }() } - <-doneSignal + <-doneSignal // this counts as a memory barrier! that is, it's safe to read err and idx without atomics if result.idx != -1 && result.err == nil { //for h, r := range iface.resourceStates { // fmt.Printf("{%v, %v}\n", h, r) @@ -203,7 +215,7 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error //for h, r := range iface.resourceStates { // fmt.Printf("{%v, %v}\n", h, r) //} - temp.Wait() + //temp.Wait() return result.err } @@ -218,12 +230,13 @@ func (iface *ArchetypeInterface) GetConstant(name string) func(args ...tla.TLAVa } func (iface *ArchetypeInterface) getResourceStateByHandle(handle ArchetypeResourceHandle) ArchetypeResourceState { - if state, ok := iface.resourceStates[handle]; ok { + resourceStates := iface.resourceStates + if state, ok := resourceStates[handle]; ok { return state } res := iface.ctx.getResourceByHandle(handle) state := res.FreshState() - iface.resourceStates[handle] = state + resourceStates[handle] = state return state } From 8d8bf8b094e61dd8060efac0974b94d0edfc274f Mon Sep 17 00:00:00 2001 From: ruchitp Date: Tue, 19 Apr 2022 19:04:50 -0700 Subject: [PATCH 15/16] Code clean up and slight tweaks --- distsys/archetypeinterface.go | 17 ----------------- .../BranchScheduling.go | 1 + .../BranchScheduling_test.go | 6 +----- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/distsys/archetypeinterface.go b/distsys/archetypeinterface.go index e8e704d1b..7b2aa69c9 100644 --- a/distsys/archetypeinterface.go +++ b/distsys/archetypeinterface.go @@ -137,7 +137,6 @@ func (iface *ArchetypeInterface) commit() (err error) { } func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error { - fmt.Println("Starting critical section") if len(branches) == 0 { return ErrCriticalSectionAborted // no branches => no success @@ -165,15 +164,11 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error } var abortCount int32 = 0 - //temp := sync.WaitGroup{} - //temp.Add(3) - for i := range branches { index := i branch := branches[index] iface := childIfaces[index] go func() { - //defer temp.Done() // Run branch err := branch(iface) if err == ErrCriticalSectionAborted { @@ -190,7 +185,6 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error // (but only if we were the first to see something; ensure this with atomic CAS) amIFirst := atomic.CompareAndSwapInt32(&result.idx, -1, int32(index)) if amIFirst { - //fmt.Printf("%d chosen\n", index) // write before signal, so the waiting goroutine sees err result.err = err close(doneSignal) @@ -202,20 +196,9 @@ func (iface *ArchetypeInterface) RunBranchConcurrently(branches ...branch) error <-doneSignal // this counts as a memory barrier! that is, it's safe to read err and idx without atomics if result.idx != -1 && result.err == nil { - //for h, r := range iface.resourceStates { - // fmt.Printf("{%v, %v}\n", h, r) - //} - //for h, r := range childIfaces[result.idx].resourceStates { - // fmt.Printf("{%v, %v}\n", h, r) - //} // steal resources of successful child to continue, if there is one iface.resourceStates = childIfaces[result.idx].resourceStates } - //fmt.Println("result") - //for h, r := range iface.resourceStates { - // fmt.Printf("{%v, %v}\n", h, r) - //} - //temp.Wait() return result.err } diff --git a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go index 96f47d098..14564ddb0 100644 --- a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go +++ b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling.go @@ -154,6 +154,7 @@ var jumpTable = distsys.MakeMPCalJumpTable( distsys.MPCalCriticalSection{ Name: "ANestedBranch.branchlabel", Body: func(iface *distsys.ArchetypeInterface) error { + fmt.Println("starting branching") var err error _ = err i4 := iface.RequireArchetypeResource("ANestedBranch.i") diff --git a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go index 583a83fe9..541093aa5 100644 --- a/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go +++ b/test/files/general/BranchScheduling.tla.gotests/BranchScheduling_test.go @@ -1,7 +1,6 @@ package branchscheduling import ( - "fmt" "github.com/UBC-NSS/pgo/distsys" "github.com/UBC-NSS/pgo/distsys/tla" "testing" @@ -11,12 +10,9 @@ import ( func TestBasic(t *testing.T) { errCh := make(chan error, 1) - ctx := distsys.NewMPCalContext(tla.MakeTLAString("self"), ABranch) + ctx := distsys.NewMPCalContext(tla.MakeTLAString("self"), ANestedBranch) go func() { errCh <- ctx.Run() - fmt.Println(ctx.ReadArchetypeResourceLocal("ABranch.i")) - fmt.Println(ctx.ReadArchetypeResourceLocal("ABranch.j")) - fmt.Println(ctx.ReadArchetypeResourceLocal("ABranch.k")) }() select { From ab080be315bfd8b0db45900d01d603ec95efeb24 Mon Sep 17 00:00:00 2001 From: ruchitp Date: Wed, 20 Apr 2022 14:34:36 -0700 Subject: [PATCH 16/16] Code cleanup and documentation --- distsys/archetyperesource.go | 10 ++++++++++ distsys/mpcalctx.go | 34 ---------------------------------- 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/distsys/archetyperesource.go b/distsys/archetyperesource.go index a62a7d493..e409df52a 100644 --- a/distsys/archetyperesource.go +++ b/distsys/archetyperesource.go @@ -16,6 +16,12 @@ type ArchetypeResource interface { // archetype is not running. Close will be called at most once by the MPCal // Context. Close() error + // FreshState will be called upon the first use of a resource during the execution of a + // critical section. It provides an ArchetypeResourceState whose purpose is to be used + // to perform operations during the execution of the critical section. A call to FreshState + // creates the expectation that there will be an eventual call to Commit/Abort on the + // resulting ArchetypeResourceState + // Note: FreshState is only meant to be used ONCE during a critical section for a resource FreshState() ArchetypeResourceState } @@ -50,6 +56,10 @@ type ArchetypeResourceState interface { // ErrCriticalSectionAborted. // This makes no sense for a value-like resource, and should be blocked off with ArchetypeResourceLeafMixin in that case. Index(index tla.TLAValue) (ArchetypeResourceComponent, error) + // ForkState provides a copy to the ArchetypeResourceState such that operations performed on other ArchetypeResourceStates + // with the same parent ArchetypeResource will not cause side effects for the current ArchetypeResourceState. + // ForkState will clone all the parts of a resource whose properties are NOT idempotent. A call to ForkState + // creates the expectation that there will be an eventual call to Commit/Abort on the resulting ArchetypeResourceState ForkState() ArchetypeResourceState } diff --git a/distsys/mpcalctx.go b/distsys/mpcalctx.go index 078fe842e..8aaacbd62 100644 --- a/distsys/mpcalctx.go +++ b/distsys/mpcalctx.go @@ -109,16 +109,6 @@ type ArchetypeResourceMakerStruct struct { ConfigureFn func(res ArchetypeResource) } -//type ForkedResourceNode struct { -// parent *ForkedResourceNode -// resourceStates map[ArchetypeResourceHandle]ArchetypeResource -// path string -//} - -//type ForkedResourceTree struct { -// root *ForkedResourceNode -//} - var _ ArchetypeResourceMaker = ArchetypeResourceMakerStruct{} func (mkStruct ArchetypeResourceMakerStruct) Make() ArchetypeResource { @@ -142,9 +132,6 @@ type MPCalContext struct { // state for ArchetypeInterface.NextFairnessCounter fairnessCounter FairnessCounter - // Forked resource tree - //forkedResourceTree ForkedResourceTree - //branchScheduler BranchScheduler jumpTable MPCalJumpTable procTable MPCalProcTable @@ -431,27 +418,6 @@ func (ctx *MPCalContext) ensureArchetypeResource(name string, maker ArchetypeRes return handle } -//func (ctx *MPCalContext) getResourceByHandle(handle ArchetypeResourceHandle) ArchetypeResource { -// //node := ctx.forkedResourceTree.root -// //for { -// // if node == nil { -// // panic(fmt.Errorf("could not find resource with name %v", handle)) -// // } -// // -// // res, ok := node.resourceStates[handle] -// // if ok { -// // return res -// // } -// // node = node.parent -// //} -// -// //res, ok := ctx.resources[handle] -// //if !ok { -// // panic(fmt.Errorf("could not find resource with name %v", handle)) -// //} -// //return res -//} - func (ctx *MPCalContext) getResourceByHandle(handle ArchetypeResourceHandle) ArchetypeResource { res, ok := ctx.resources[handle] if !ok {