Added unlock, need to update link to match pattern and see if it can be generalized

gql_cataclysm
noah metz 2023-07-27 22:25:00 -06:00
parent a1ce4238cc
commit 027bb74887
2 changed files with 151 additions and 44 deletions

@ -71,18 +71,25 @@ func NewLockableExt() *LockableExt {
return &LockableExt{ return &LockableExt{
Owner: nil, Owner: nil,
PendingOwner: nil, PendingOwner: nil,
Requirements: map[NodeID]string{}, Requirements: map[NodeID]ReqState{},
Dependencies: map[NodeID]string{}, Dependencies: map[NodeID]string{},
LockStates: map[NodeID]string{},
} }
} }
type ReqState struct {
Link string `json:"link"`
Lock string `json:"lock"`
}
type LockableExt struct { type LockableExt struct {
Owner *NodeID `json:"owner"` Owner *NodeID `json:"owner"`
PendingOwner *NodeID `json:"pending_owner"` PendingOwner *NodeID `json:"pending_owner"`
Requirements map[NodeID]string `json:"requirements"` Requirements map[NodeID]ReqState `json:"requirements"`
Dependencies map[NodeID]string `json:"dependencies"` Dependencies map[NodeID]string `json:"dependencies"`
LockStates map[NodeID]string `json:"lock_states"` }
func UnlockLockable(ctx *Context, node *Node) error {
return ctx.Send(node.ID, node.ID, NewLockSignal("unlock"))
} }
func LockLockable(ctx *Context, node *Node) error { func LockLockable(ctx *Context, node *Node) error {
@ -105,7 +112,7 @@ func LinkRequirement(ctx *Context, dependency *Node, requirement NodeID) error {
return fmt.Errorf("%s is a dependency of %s, cannot link as requirement", requirement, dependency.ID) return fmt.Errorf("%s is a dependency of %s, cannot link as requirement", requirement, dependency.ID)
} }
dep_ext.Requirements[requirement] = "start" dep_ext.Requirements[requirement] = ReqState{"linking", "unlocked"}
return ctx.Send(dependency.ID, requirement, NewLinkSignal("req_link")) return ctx.Send(dependency.ID, requirement, NewLinkSignal("req_link"))
} }
@ -113,55 +120,149 @@ func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node
ctx.Log.Logf("lockable", "LOCK_SIGNAL: %s->%s %+v", source, node.ID, signal) ctx.Log.Logf("lockable", "LOCK_SIGNAL: %s->%s %+v", source, node.ID, signal)
state := signal.State state := signal.State
switch state { switch state {
case "unlock":
if ext.Owner == nil {
ctx.Send(node.ID, source, NewLockSignal("already_unlocked"))
} else if source != *ext.Owner {
ctx.Send(node.ID, source, NewLockSignal("not_owner"))
} else if ext.PendingOwner == nil {
ctx.Send(node.ID, source, NewLockSignal("already_unlocking"))
} else {
if len(ext.Requirements) == 0 {
ext.Owner = nil
ext.PendingOwner = nil
ctx.Send(node.ID, source, NewLockSignal("unlocked"))
} else {
ext.PendingOwner = nil
for id, state := range(ext.Requirements) {
if state.Link == "linked" {
if state.Lock != "locked" {
panic("NOT_LOCKED")
}
state.Lock = "unlocking"
ext.Requirements[id] = state
ctx.Send(node.ID, id, NewLockSignal("unlock"))
}
}
if source != node.ID {
ctx.Send(node.ID, source, NewLockSignal("unlocking"))
}
}
}
case "unlocking":
state, exists := ext.Requirements[source]
if exists == false {
ctx.Send(node.ID, source, NewLockSignal("not_requirement"))
} else if state.Link != "linked" {
ctx.Send(node.ID, source, NewLockSignal("node_not_linked"))
} else if state.Lock != "unlocking" {
ctx.Send(node.ID, source, NewLockSignal("not_unlocking"))
}
case "unlocked":
if source == node.ID {
return
}
state, exists := ext.Requirements[source]
if exists == false {
ctx.Send(node.ID, source, NewLockSignal("not_requirement"))
} else if state.Link != "linked" {
ctx.Send(node.ID, source, NewLockSignal("not_linked"))
} else if state.Lock != "unlocking" {
ctx.Send(node.ID, source, NewLockSignal("not_unlocking"))
} else {
state.Lock = "unlocked"
ext.Requirements[source] = state
if ext.PendingOwner == nil {
linked := 0
unlocked := 0
for _, s := range(ext.Requirements) {
if s.Link == "linked" {
linked += 1
}
if s.Lock == "unlocked" {
unlocked += 1
}
}
if linked == unlocked {
previous_owner := *ext.Owner
ext.Owner = nil
ctx.Send(node.ID, previous_owner, NewLockSignal("unlocked"))
}
}
}
case "locked": case "locked":
if source == node.ID { if source == node.ID {
return return
} }
_, exists := ext.LockStates[source] state, exists := ext.Requirements[source]
if exists == true { if exists == false {
ext.LockStates[source] = "locked" ctx.Send(node.ID, source, NewLockSignal("not_requirement"))
locked_reqs := 0 } else if state.Link != "linked" {
for _, state := range(ext.LockStates) { ctx.Send(node.ID, source, NewLockSignal("not_linked"))
if state == "locked" { } else if state.Lock != "locking" {
locked_reqs += 1 ctx.Send(node.ID, source, NewLockSignal("not_locking"))
} else {
state.Lock = "locked"
ext.Requirements[source] = state
if ext.PendingOwner != nil {
linked := 0
locked := 0
for _, s := range(ext.Requirements) {
if s.Link == "linked" {
linked += 1
}
if s.Lock == "locked" {
locked += 1
} }
} }
if len(ext.Requirements) == locked_reqs {
if linked == locked {
ext.Owner = ext.PendingOwner ext.Owner = ext.PendingOwner
ext.PendingOwner = nil
ctx.Send(node.ID, *ext.Owner, NewLockSignal("locked")) ctx.Send(node.ID, *ext.Owner, NewLockSignal("locked"))
} }
} else {
ctx.Send(node.ID, source, NewLockSignal("reset"))
} }
case "pending":
state, exists := ext.LockStates[source]
if exists == true && state != "pending" {
delete(ext.LockStates, source)
ctx.Send(node.ID, source, NewLockSignal("reset"))
} else if exists == false {
ctx.Send(node.ID, source, NewLockSignal("reset"))
} }
case "locking":
state, exists := ext.Requirements[source]
if exists == false {
ctx.Send(node.ID, source, NewLockSignal("not_requirement"))
} else if state.Link != "linked" {
ctx.Send(node.ID, source, NewLockSignal("node_not_linked"))
} else if state.Lock != "locking" {
ctx.Send(node.ID, source, NewLockSignal("not_locking"))
}
case "lock": case "lock":
if ext.Owner != nil { if ext.Owner != nil {
ctx.Send(node.ID, source, NewLockSignal("already_locked")) ctx.Send(node.ID, source, NewLockSignal("already_locked"))
} else if ext.PendingOwner == nil { } else if ext.PendingOwner != nil {
if len(ext.Requirements) == 0 { ctx.Send(node.ID, source, NewLockSignal("already_locking"))
} else {
owner := source owner := source
if len(ext.Requirements) == 0 {
ext.Owner = &owner ext.Owner = &owner
ext.PendingOwner = ext.Owner
ctx.Send(node.ID, source, NewLockSignal("locked")) ctx.Send(node.ID, source, NewLockSignal("locked"))
} else { } else {
pending_owner := source ext.PendingOwner = &owner
ext.PendingOwner = &pending_owner
for id, state := range(ext.Requirements) { for id, state := range(ext.Requirements) {
if state == "linked" { if state.Link == "linked" {
ext.LockStates[id] = "pending" if state.Lock != "unlocked" {
panic("NOT_UNLOCKED")
}
state.Lock = "locking"
ext.Requirements[id] = state
ctx.Send(node.ID, id, NewLockSignal("lock")) ctx.Send(node.ID, id, NewLockSignal("lock"))
} }
} }
if source != node.ID { if source != node.ID {
ctx.Send(node.ID, source, NewLockSignal("pending")) ctx.Send(node.ID, source, NewLockSignal("locking"))
} }
} }
} }
@ -181,9 +282,9 @@ func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node
if exists == false { if exists == false {
dep_state, exists := ext.Dependencies[source] dep_state, exists := ext.Dependencies[source]
if exists == false { if exists == false {
ext.Dependencies[source] = "start" ext.Dependencies[source] = "linking"
ctx.Send(node.ID, source, NewLinkSignal("dep_link")) ctx.Send(node.ID, source, NewLinkSignal("dep_link"))
} else if dep_state == "start" { } else if dep_state == "linking" {
ext.Dependencies[source] = "linked" ext.Dependencies[source] = "linked"
ctx.Send(node.ID, source, NewLinkSignal("dep_linked")) ctx.Send(node.ID, source, NewLinkSignal("dep_linked"))
} }
@ -196,10 +297,11 @@ func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node
if exists == false { if exists == false {
req_state, exists := ext.Requirements[source] req_state, exists := ext.Requirements[source]
if exists == false { if exists == false {
ext.Requirements[source] = "start" ext.Requirements[source] = ReqState{"linking", "unlocked"}
ctx.Send(node.ID, source, NewLinkSignal("req_link")) ctx.Send(node.ID, source, NewLinkSignal("req_link"))
} else if req_state == "start" { } else if req_state.Link == "linking" {
ext.Requirements[source] = "linked" req_state.Link = "linked"
ext.Requirements[source] = req_state
ctx.Send(node.ID, source, NewLinkSignal("req_linked")) ctx.Send(node.ID, source, NewLinkSignal("req_linked"))
} }
} else { } else {
@ -213,15 +315,16 @@ func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node
case "dep_linked": case "dep_linked":
ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source) ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source)
req_state, exists := ext.Requirements[source] req_state, exists := ext.Requirements[source]
if exists == true && req_state == "start" { if exists == true && req_state.Link == "linking" {
ext.Requirements[source] = "linked" req_state.Link = "linked"
ext.Requirements[source] = req_state
ctx.Send(node.ID, source, NewLinkSignal("req_linked")) ctx.Send(node.ID, source, NewLinkSignal("req_linked"))
} }
case "req_linked": case "req_linked":
ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source) ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source)
dep_state, exists := ext.Dependencies[source] dep_state, exists := ext.Dependencies[source]
if exists == true && dep_state == "start" { if exists == true && dep_state == "linking" {
ext.Dependencies[source] = "linked" ext.Dependencies[source] = "linked"
ctx.Send(node.ID, source, NewLinkSignal("dep_linked")) ctx.Send(node.ID, source, NewLinkSignal("dep_linked"))
} }
@ -262,7 +365,7 @@ func (ext *LockableExt) Process(ctx *Context, source NodeID, node *Node, signal
} }
case Down: case Down:
for requirement, state := range(ext.Requirements) { for requirement, state := range(ext.Requirements) {
if state == "linked" { if state.Link == "linked" {
err := ctx.Send(node.ID, requirement, signal) err := ctx.Send(node.ID, requirement, signal)
if err != nil { if err != nil {
ctx.Log.Logf("signal", "LOCKABLE_SIGNAL_ERR: %s->%s - %e", node.ID, requirement, err) ctx.Log.Logf("signal", "LOCKABLE_SIGNAL_ERR: %s->%s - %e", node.ID, requirement, err)

@ -102,7 +102,11 @@ func TestLock(t *testing.T) {
err = LockLockable(ctx, l1) err = LockLockable(ctx, l1)
fatalErr(t, err) fatalErr(t, err)
(*GraphTester)(t).WaitForState(ctx, l1_listener, LockSignalType, "locked", time.Millisecond*10, "No locked") (*GraphTester)(t).WaitForState(ctx, l1_listener, LockSignalType, "locked", time.Millisecond*10, "No locked")
err = LockLockable(ctx, l0)
fatalErr(t, err)
(*GraphTester)(t).WaitForState(ctx, l1_listener, LockSignalType, "locked", time.Millisecond*10, "No locked") (*GraphTester)(t).WaitForState(ctx, l1_listener, LockSignalType, "locked", time.Millisecond*10, "No locked")
(*GraphTester)(t).WaitForState(ctx, l1_listener, LockSignalType, "locked", time.Millisecond*10, "No locked")
(*GraphTester)(t).WaitForState(ctx, l1_listener, LockSignalType, "locked", time.Millisecond*10, "No locked")
(*GraphTester)(t).WaitForState(ctx, l1_listener, LockSignalType, "locked", time.Millisecond*10, "No locked")
err = UnlockLockable(ctx, l1)
fatalErr(t, err)
} }