From c763725a34f5fff07f6872c5342de3fadbc2d1ef Mon Sep 17 00:00:00 2001 From: Noah Metz Date: Thu, 27 Jul 2023 23:05:19 -0600 Subject: [PATCH] Fixed lockable link sequences --- lockable.go | 95 ++++++++++++++++++++++++++---------------------- lockable_test.go | 24 ++++++------ 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/lockable.go b/lockable.go index 3b9fb89..be80e04 100644 --- a/lockable.go +++ b/lockable.go @@ -113,7 +113,7 @@ func LinkRequirement(ctx *Context, dependency *Node, requirement NodeID) error { } dep_ext.Requirements[requirement] = ReqState{"linking", "unlocked"} - return ctx.Send(dependency.ID, requirement, NewLinkSignal("req_link")) + return ctx.Send(dependency.ID, requirement, NewLinkSignal("link_as_req")) } func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) { @@ -272,62 +272,69 @@ func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node } // TODO: don't allow changes to requirements or dependencies while being locked or locked +// TODO: add unlink func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) { ctx.Log.Logf("lockable", "LINK_SIGNAL: %s->%s %+v", source, node.ID, signal) state := signal.State switch state { - // sent by a node to link this node as a requirement - case "req_link": - _, exists := ext.Requirements[source] - if exists == false { - dep_state, exists := ext.Dependencies[source] - if exists == false { - ext.Dependencies[source] = "linking" - ctx.Send(node.ID, source, NewLinkSignal("dep_link")) - } else if dep_state == "linking" { - ext.Dependencies[source] = "linked" - ctx.Send(node.ID, source, NewLinkSignal("dep_linked")) + case "link_as_dep": + state, exists := ext.Requirements[source] + if exists == true && state.Link == "linked" { + ctx.Send(node.ID, source, NewLinkSignal("already_req")) + } else if state.Link == "linking" { + state.Link = "linked" + ext.Requirements[source] = state + ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep")) + } else if ext.PendingOwner != ext.Owner { + if ext.Owner == nil { + ctx.Send(node.ID, source, NewLinkSignal("locking")) + } else { + ctx.Send(node.ID, source, NewLinkSignal("unlocking")) } } else { - delete(ext.Requirements, source) - ctx.Send(node.ID, source, NewLinkSignal("req_reset")) + ext.Requirements[source] = ReqState{"linking", "unlocked"} + ctx.Send(node.ID, source, NewLinkSignal("link_as_req")) } - case "dep_link": - _, exists := ext.Dependencies[source] - if exists == false { - req_state, exists := ext.Requirements[source] - if exists == false { - ext.Requirements[source] = ReqState{"linking", "unlocked"} - ctx.Send(node.ID, source, NewLinkSignal("req_link")) - } else if req_state.Link == "linking" { - req_state.Link = "linked" - ext.Requirements[source] = req_state - ctx.Send(node.ID, source, NewLinkSignal("req_linked")) + + case "link_as_req": + state, exists := ext.Dependencies[source] + if exists == true && state == "linked" { + ctx.Send(node.ID, source, NewLinkSignal("already_dep")) + } else if state == "linking" { + ext.Dependencies[source] = "linked" + ctx.Send(node.ID, source, NewLinkSignal("linked_as_req")) + } else if ext.PendingOwner != ext.Owner { + if ext.Owner == nil { + ctx.Send(node.ID, source, NewLinkSignal("locking")) + } else { + ctx.Send(node.ID, source, NewLinkSignal("unlocking")) } } else { - delete(ext.Dependencies, source) - ctx.Send(node.ID, source, NewLinkSignal("dep_reset")) + ext.Dependencies[source] = "linking" + ctx.Send(node.ID, source, NewLinkSignal("link_as_dep")) } - case "dep_reset": - ctx.Log.Logf("lockable", "%s reset %s dependency state", node.ID, source) - case "req_reset": - ctx.Log.Logf("lockable", "%s reset %s requirement state", node.ID, source) - case "dep_linked": - ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source) - req_state, exists := ext.Requirements[source] - if exists == true && req_state.Link == "linking" { - req_state.Link = "linked" - ext.Requirements[source] = req_state - ctx.Send(node.ID, source, NewLinkSignal("req_linked")) + case "linked_as_dep": + state, exists := ext.Dependencies[source] + if exists == false { + ctx.Send(node.ID, source, NewLinkSignal("not_linking")) + } else if state == "linked" { + } else if state == "linking" { + ext.Dependencies[source] = "linked" + ctx.Send(node.ID, source, NewLinkSignal("linked_as_req")) } + ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source) - case "req_linked": - ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source) - dep_state, exists := ext.Dependencies[source] - if exists == true && dep_state == "linking" { - ext.Dependencies[source] = "linked" - ctx.Send(node.ID, source, NewLinkSignal("dep_linked")) + case "linked_as_req": + state, exists := ext.Requirements[source] + if exists == false { + ctx.Send(node.ID, source, NewLinkSignal("not_linking")) + } else if state.Link == "linked" { + } else if state.Link == "linking" { + state.Link = "linked" + ext.Requirements[source] = state + ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep")) } + ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source) default: ctx.Log.Logf("lockable", "LINK_ERROR: unknown state %s", state) diff --git a/lockable_test.go b/lockable_test.go index e2f0e17..7dab6bb 100644 --- a/lockable_test.go +++ b/lockable_test.go @@ -20,7 +20,7 @@ var link_policy = NewAllNodesPolicy([]SignalType{LinkSignalType, StatusSignalTyp var lock_policy = NewAllNodesPolicy([]SignalType{LinkSignalType, LockSignalType, StatusSignalType}) func TestLink(t *testing.T) { - ctx := lockableTestContext(t, []string{}) + ctx := lockableTestContext(t, []string{"lockable"}) l1_listener := NewListenerExt(10) l1 := NewNode(ctx, RandID(), TestLockableType, nil, @@ -39,8 +39,8 @@ func TestLink(t *testing.T) { err := LinkRequirement(ctx, l1, l2.ID) fatalErr(t, err) - (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") - (*GraphTester)(t).WaitForState(ctx, l2_listener, LinkSignalType, "req_linked", time.Millisecond*10, "No req_linked") + (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") + (*GraphTester)(t).WaitForState(ctx, l2_listener, LinkSignalType, "linked_as_dep", time.Millisecond*10, "No req_linked") err = ctx.Send(l2.ID, l2.ID, NewStatusSignal("TEST", l2.ID)) fatalErr(t, err) @@ -50,7 +50,7 @@ func TestLink(t *testing.T) { } func TestLock(t *testing.T) { - ctx := lockableTestContext(t, []string{"lockable"}) + ctx := lockableTestContext(t, []string{}) NewLockable := func()(*Node, *ListenerExt) { listener := NewListenerExt(10) @@ -89,15 +89,15 @@ func TestLock(t *testing.T) { err = LinkRequirement(ctx, l0, l5.ID) fatalErr(t, err) - (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") - (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") - (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") - (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") + (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") + (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") + (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") + (*GraphTester)(t).WaitForState(ctx, l1_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") - (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") - (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") - (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") - (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "dep_linked", time.Millisecond*10, "No dep_link") + (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") + (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") + (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") + (*GraphTester)(t).WaitForState(ctx, l0_listener, LinkSignalType, "linked_as_req", time.Millisecond*10, "No linked_as_req") err = LockLockable(ctx, l1) fatalErr(t, err)