Added naive locking/unlocking. Want to verify if it's possible to get into a deadlock with current implementation.

graph-rework
noah metz 2023-04-08 15:47:51 -06:00
parent e4366c5bf2
commit 2b562abe01
2 changed files with 92 additions and 1 deletions

@ -133,12 +133,63 @@ type Resource interface {
AddParent(parent Resource) error AddParent(parent Resource) error
Children() []Resource Children() []Resource
Parents() []Resource Parents() []Resource
Lock() error
Unlock() error
} }
type BaseResource struct { type BaseResource struct {
BaseNode BaseNode
parents []Resource parents []Resource
children []Resource children []Resource
locked bool
state_lock sync.Mutex
}
// Grab the state mutex and check the state, if unlocked continue to hold the mutex while doing the same for children
// When the bottom of a tree is reached(no more children) go back up and set the lock state
func (resource * BaseResource) Lock() error {
var err error = nil
resource.state_lock.Lock()
if resource.locked == true {
err = errors.New("Resource already locked")
} else {
all_children_locked := true
for _, child := range resource.Children() {
err = child.Lock()
if err != nil {
all_children_locked = false
break
}
}
if all_children_locked == true {
resource.locked = true
}
}
resource.state_lock.Unlock()
return err
}
// Recurse through children, unlocking until no more children
func (resource * BaseResource) Unlock() error {
var err error = nil
resource.state_lock.Lock()
if resource.locked == false {
err = errors.New("Resource already unlocked")
} else {
all_children_unlocked := true
for _, child := range resource.Children() {
err = child.Unlock()
if err != nil {
all_children_unlocked = false
break
}
}
if all_children_unlocked == true{
resource.locked = false
}
}
resource.state_lock.Unlock()
return err
} }
func (resource * BaseResource) Children() []Resource { func (resource * BaseResource) Children() []Resource {
@ -169,6 +220,8 @@ func (resource * BaseResource) AddParent(parent Resource) error {
return nil return nil
} }
type Event interface { type Event interface {
GraphNode GraphNode
Children() []Event Children() []Event

@ -149,13 +149,51 @@ func TestLockResource(t * testing.T) {
r1 := NewResource("r1", "", []Resource{}) r1 := NewResource("r1", "", []Resource{})
r2 := NewResource("r2", "", []Resource{}) r2 := NewResource("r2", "", []Resource{})
r3 := NewResource("r3", "", []Resource{r1, r2}) r3 := NewResource("r3", "", []Resource{r1, r2})
r4 := NewResource("r3", "", []Resource{r1, r2})
event_manager := NewEventManager() event_manager := NewEventManager()
err_1 := event_manager.AddResource(r1) err_1 := event_manager.AddResource(r1)
err_2 := event_manager.AddResource(r2) err_2 := event_manager.AddResource(r2)
err_3 := event_manager.AddResource(r3) err_3 := event_manager.AddResource(r3)
err_4 := event_manager.AddResource(r4)
if err_1 != nil || err_2 != nil || err_3 != nil { if err_1 != nil || err_2 != nil || err_3 != nil || err_4 != nil {
t.Fatal("Failed to add initial tiered resources for test") t.Fatal("Failed to add initial tiered resources for test")
} }
// Lock r3(so also r1&r2)
err := r3.Lock()
if err != nil {
t.Fatal("Failed to lock r3")
}
err = r3.Lock()
if err == nil {
t.Fatal("Locked r3 after locking r3")
}
err = r4.Lock()
if err == nil {
t.Fatal("Locked r4 after locking r3")
}
err = r1.Lock()
if err == nil {
t.Fatal("Locked r1 after locking r3")
}
err = r3.Unlock()
if err != nil {
t.Fatal("Failed to unlock r3")
}
err = r4.Lock()
if err != nil {
t.Fatal("Failed to lock r4 after unlocking r3")
}
err = r4.Unlock()
if err != nil {
t.Fatal("Failed to unlock r4")
}
} }