Fixed lockable link sequences

gql_cataclysm
noah metz 2023-07-27 23:05:19 -06:00
parent 027bb74887
commit c763725a34
2 changed files with 63 additions and 56 deletions

@ -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" {
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 {
ext.Requirements[source] = ReqState{"linking", "unlocked"}
ctx.Send(node.ID, source, NewLinkSignal("link_as_req"))
}
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("dep_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.Requirements, source)
ctx.Send(node.ID, source, NewLinkSignal("req_reset"))
ext.Dependencies[source] = "linking"
ctx.Send(node.ID, source, NewLinkSignal("link_as_dep"))
}
case "dep_link":
_, exists := ext.Dependencies[source]
case "linked_as_dep":
state, 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"))
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"))
}
} else {
delete(ext.Dependencies, source)
ctx.Send(node.ID, source, NewLinkSignal("dep_reset"))
}
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 "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)

@ -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)