diff --git a/graph.go b/graph.go index f30c8b8..75d9702 100644 --- a/graph.go +++ b/graph.go @@ -133,12 +133,63 @@ type Resource interface { AddParent(parent Resource) error Children() []Resource Parents() []Resource + Lock() error + Unlock() error } type BaseResource struct { BaseNode parents []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 { @@ -169,6 +220,8 @@ func (resource * BaseResource) AddParent(parent Resource) error { return nil } + + type Event interface { GraphNode Children() []Event diff --git a/graph_test.go b/graph_test.go index c096b89..6891059 100644 --- a/graph_test.go +++ b/graph_test.go @@ -149,13 +149,51 @@ func TestLockResource(t * testing.T) { r1 := NewResource("r1", "", []Resource{}) r2 := NewResource("r2", "", []Resource{}) r3 := NewResource("r3", "", []Resource{r1, r2}) + r4 := NewResource("r3", "", []Resource{r1, r2}) event_manager := NewEventManager() err_1 := event_manager.AddResource(r1) err_2 := event_manager.AddResource(r2) 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") } + + // 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") + } }