Added UnlinkLockables and UnlinkThreads

graph-rework-2
noah metz 2023-07-03 16:37:54 -06:00
parent 64171c6c85
commit 5bdc06bf0f
3 changed files with 123 additions and 120 deletions

@ -560,9 +560,17 @@ func NewGQLThread(ctx * GraphContext, listen string, requirements []Lockable) (*
http_done: &sync.WaitGroup{}, http_done: &sync.WaitGroup{},
} }
err = LinkLockables(ctx, thread, requirements) if len(requirements) > 0 {
if err != nil { req_nodes := make([]GraphNode, len(requirements))
return nil, err for i, req := range(requirements) {
req_nodes[i] = req
}
err = UpdateStates(ctx, req_nodes, func(nodes NodeMap) error {
return LinkLockables(ctx, thread, requirements, nodes)
})
if err != nil {
return nil, err
}
} }
return thread, nil return thread, nil
} }

@ -195,36 +195,30 @@ func (state * BaseLockableState) RemoveRequirement(requirement Lockable) {
state.requirements = state.requirements[0:(req_len-1)] state.requirements = state.requirements[0:(req_len-1)]
} }
// Requires lockable and requirement's states to be locked for write
func UnlinkLockables(ctx * GraphContext, lockable Lockable, requirement Lockable) error { func UnlinkLockables(ctx * GraphContext, lockable Lockable, requirement Lockable) error {
// Check if requirement is a requirement of lockable state := lockable.State().(LockableState)
err := UpdateStates(ctx, []GraphNode{lockable}, func(nodes NodeMap) error{ var found GraphNode = nil
state := lockable.State().(LockableState) for _, req := range(state.Requirements()) {
var found GraphNode = nil if requirement.ID() == req.ID() {
for _, req := range(state.Requirements()) { found = req
if requirement.ID() == req.ID() { break
found = req
break
}
}
if found == nil {
return fmt.Errorf("UNLINK_LOCKABLES_ERR: %s is not a requirement of %s", requirement.ID(), lockable.ID())
} }
}
err := UpdateMoreStates(ctx, []GraphNode{found}, nodes, func(nodes NodeMap) error { if found == nil {
req_state := found.State().(LockableState) return fmt.Errorf("UNLINK_LOCKABLES_ERR: %s is not a requirement of %s", requirement.ID(), lockable.ID())
req_state.RemoveDependency(lockable) }
state.RemoveRequirement(requirement)
return nil
})
return err req_state := found.State().(LockableState)
}) req_state.RemoveDependency(lockable)
state.RemoveRequirement(requirement)
return err return nil
} }
func LinkLockables(ctx * GraphContext, lockable Lockable, requirements []Lockable) error { // Requires lockable and requirements to be locked for write, nodes passed because requirement check recursively locks
func LinkLockables(ctx * GraphContext, lockable Lockable, requirements []Lockable, nodes NodeMap) error {
if lockable == nil { if lockable == nil {
return fmt.Errorf("LOCKABLE_LINK_ERR: Will not link Lockables to nil as requirements") return fmt.Errorf("LOCKABLE_LINK_ERR: Will not link Lockables to nil as requirements")
} }
@ -250,59 +244,48 @@ func LinkLockables(ctx * GraphContext, lockable Lockable, requirements []Lockabl
found[requirement.ID()] = true found[requirement.ID()] = true
} }
gnodes := make([]GraphNode, len(requirements) + 1) // Check that all the requirements can be added
gnodes[0] = lockable lockable_state := lockable.State().(LockableState)
for i, node := range(requirements) { // If the lockable is already locked, need to lock this resource as well before we can add it
gnodes[i+1] = node for _, requirement := range(requirements) {
} requirement_state := requirement.State().(LockableState)
for _, req := range(requirements) {
err := UpdateStates(ctx, gnodes, func(nodes NodeMap) error { if req.ID() == requirement.ID() {
// Check that all the requirements can be added continue
lockable_state := lockable.State().(LockableState)
// If the lockable is already locked, need to lock this resource as well before we can add it
for _, requirement := range(requirements) {
requirement_state := requirement.State().(LockableState)
for _, req := range(requirements) {
if req.ID() == requirement.ID() {
continue
}
if checkIfRequirement(ctx, req.ID(), requirement_state, requirement.ID(), nodes) == true {
return fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependenyc of %s so cannot add the same dependency", req.ID(), requirement.ID())
}
}
if checkIfRequirement(ctx, lockable.ID(), requirement_state, requirement.ID(), nodes) == true {
return fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependency of %s so cannot link as requirement", requirement.ID(), lockable.ID())
}
if checkIfRequirement(ctx, requirement.ID(), lockable_state, lockable.ID(), nodes) == true {
return fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependency of %s so cannot link as dependency again", lockable.ID(), requirement.ID())
} }
if lockable_state.Owner() == nil { if checkIfRequirement(ctx, req.ID(), requirement_state, requirement.ID(), nodes) == true {
// If the new owner isn't locked, we can add the requirement return fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependenyc of %s so cannot add the same dependency", req.ID(), requirement.ID())
} else if requirement_state.Owner() == nil {
// if the new requirement isn't already locked but the owner is, the requirement needs to be locked first
return fmt.Errorf("LOCKABLE_LINK_ERR: %s is locked, %s must be locked to add", lockable.ID(), requirement.ID())
} else {
// If the new requirement is already locked and the owner is already locked, their owners need to match
if requirement_state.Owner().ID() != lockable_state.Owner().ID() {
return fmt.Errorf("LOCKABLE_LINK_ERR: %s is not locked by the same owner as %s, can't link as requirement", requirement.ID(), lockable.ID())
}
} }
} }
// Update the states of the requirements if checkIfRequirement(ctx, lockable.ID(), requirement_state, requirement.ID(), nodes) == true {
for _, requirement := range(requirements) { return fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependency of %s so cannot link as requirement", requirement.ID(), lockable.ID())
requirement_state := requirement.State().(LockableState)
requirement_state.AddDependency(lockable)
lockable_state.AddRequirement(requirement)
ctx.Log.Logf("lockable", "LOCKABLE_LINK: linked %s to %s as a requirement", requirement.ID(), lockable.ID())
} }
// Return no error if checkIfRequirement(ctx, requirement.ID(), lockable_state, lockable.ID(), nodes) == true {
return nil return fmt.Errorf("LOCKABLE_LINK_ERR: %s is a dependency of %s so cannot link as dependency again", lockable.ID(), requirement.ID())
}) }
if lockable_state.Owner() == nil {
// If the new owner isn't locked, we can add the requirement
} else if requirement_state.Owner() == nil {
// if the new requirement isn't already locked but the owner is, the requirement needs to be locked first
return fmt.Errorf("LOCKABLE_LINK_ERR: %s is locked, %s must be locked to add", lockable.ID(), requirement.ID())
} else {
// If the new requirement is already locked and the owner is already locked, their owners need to match
if requirement_state.Owner().ID() != lockable_state.Owner().ID() {
return fmt.Errorf("LOCKABLE_LINK_ERR: %s is not locked by the same owner as %s, can't link as requirement", requirement.ID(), lockable.ID())
}
}
}
// Update the states of the requirements
for _, requirement := range(requirements) {
requirement_state := requirement.State().(LockableState)
requirement_state.AddDependency(lockable)
lockable_state.AddRequirement(requirement)
ctx.Log.Logf("lockable", "LOCKABLE_LINK: linked %s to %s as a requirement", requirement.ID(), lockable.ID())
}
return err // Return no error
return nil
} }
func NewBaseLockableState(name string, _type string) BaseLockableState { func NewBaseLockableState(name string, _type string) BaseLockableState {
@ -711,10 +694,20 @@ func NewSimpleLockable(ctx * GraphContext, name string, requirements []Lockable)
if err != nil { if err != nil {
return nil, err return nil, err
} }
lockable_ptr := &lockable lockable_ptr := &lockable
err = LinkLockables(ctx, lockable_ptr, requirements)
if err != nil { if len(requirements) > 0 {
return nil, err req_nodes := make([]GraphNode, len(requirements))
for i, req := range(requirements) {
req_nodes[i] = req
}
err = UpdateStates(ctx, req_nodes, func(nodes NodeMap) error {
return LinkLockables(ctx, lockable_ptr, requirements, nodes)
})
if err != nil {
return nil, err
}
} }
return lockable_ptr, nil return lockable_ptr, nil

@ -261,30 +261,26 @@ func (state * BaseThreadState) ChildInfo(child NodeID) ThreadInfo {
return state.child_info[child] return state.child_info[child]
} }
// Requires thread and childs state to be locked for write
func UnlinkThreads(ctx * GraphContext, thread Thread, child Thread) error { func UnlinkThreads(ctx * GraphContext, thread Thread, child Thread) error {
err := UpdateStates(ctx, []GraphNode{thread}, func(nodes NodeMap) error{ state := thread.State().(ThreadState)
state := thread.State().(ThreadState) var found GraphNode = nil
var found GraphNode = nil for _, c := range(state.Children()) {
for _, c := range(state.Children()) { if child.ID() == c.ID() {
if child.ID() == c.ID() { found = c
found = c break
break
}
} }
}
if found == nil { if found == nil {
return fmt.Errorf("UNLINK_THREADS_ERR: %s is not a child of %s", child.ID(), thread.ID()) return fmt.Errorf("UNLINK_THREADS_ERR: %s is not a child of %s", child.ID(), thread.ID())
} }
err := UpdateMoreStates(ctx, []GraphNode{found}, nodes, func(nodes NodeMap) error { child_state := child.State().(ThreadState)
child_state := found.State().(ThreadState) child_state.SetParent(nil)
child_state.SetParent(nil) state.RemoveChild(child)
state.RemoveChild(child)
return nil return nil
})
return err
})
return err
} }
func (state * BaseThreadState) RemoveChild(child Thread) { func (state * BaseThreadState) RemoveChild(child Thread) {
@ -348,6 +344,7 @@ func checkIfChild(ctx * GraphContext, thread_id NodeID, cur_state ThreadState, c
return false return false
} }
// Requires thread and childs state to be locked for write
func LinkThreads(ctx * GraphContext, thread Thread, child Thread, info ThreadInfo) error { func LinkThreads(ctx * GraphContext, thread Thread, child Thread, info ThreadInfo) error {
if ctx == nil || thread == nil || child == nil { if ctx == nil || thread == nil || child == nil {
return fmt.Errorf("invalid input") return fmt.Errorf("invalid input")
@ -357,31 +354,26 @@ func LinkThreads(ctx * GraphContext, thread Thread, child Thread, info ThreadInf
return fmt.Errorf("Will not link %s as a child of itself", thread.ID()) return fmt.Errorf("Will not link %s as a child of itself", thread.ID())
} }
thread_state := thread.State().(ThreadState)
child_state := child.State().(ThreadState)
err := UpdateStates(ctx, []GraphNode{thread, child}, func(nodes NodeMap) error { if child_state.Parent() != nil {
thread_state := thread.State().(ThreadState) return fmt.Errorf("EVENT_LINK_ERR: %s already has a parent, cannot link as child", child.ID())
child_state := child.State().(ThreadState) }
if child_state.Parent() != nil {
return fmt.Errorf("EVENT_LINK_ERR: %s already has a parent, cannot link as child", child.ID())
}
if checkIfChild(ctx, thread.ID(), child_state, child.ID()) == true {
return fmt.Errorf("EVENT_LINK_ERR: %s is a child of %s so cannot add as parent", thread.ID(), child.ID())
}
if checkIfChild(ctx, child.ID(), thread_state, thread.ID()) == true { if checkIfChild(ctx, thread.ID(), child_state, child.ID()) == true {
return fmt.Errorf("EVENT_LINK_ERR: %s is already a parent of %s so will not add again", thread.ID(), child.ID()) return fmt.Errorf("EVENT_LINK_ERR: %s is a child of %s so cannot add as parent", thread.ID(), child.ID())
} }
err := thread_state.AddChild(child, info) if checkIfChild(ctx, child.ID(), thread_state, thread.ID()) == true {
if err != nil { return fmt.Errorf("EVENT_LINK_ERR: %s is already a parent of %s so will not add again", thread.ID(), child.ID())
return fmt.Errorf("EVENT_LINK_ERR: error adding %s as child to %s: %e", child.ID(), thread.ID(), err) }
}
child_state.SetParent(thread)
return nil err := thread_state.AddChild(child, info)
}) if err != nil {
return fmt.Errorf("EVENT_LINK_ERR: error adding %s as child to %s: %e", child.ID(), thread.ID(), err)
}
child_state.SetParent(thread)
if err != nil { if err != nil {
return err return err
@ -701,9 +693,19 @@ func NewSimpleThread(ctx * GraphContext, name string, requirements []Lockable, a
thread_ptr := &thread thread_ptr := &thread
err = LinkLockables(ctx, thread_ptr, requirements)
if err != nil { if len(requirements) > 0 {
return nil, err req_nodes := make([]GraphNode, len(requirements))
for i, req := range(requirements) {
req_nodes[i] = req
}
err = UpdateStates(ctx, req_nodes, func(nodes NodeMap) error {
return LinkLockables(ctx, thread_ptr, requirements, nodes)
})
if err != nil {
return nil, err
}
} }
return thread_ptr, nil return thread_ptr, nil
} }