diff --git a/graph.go b/graph.go index c277161..a740200 100644 --- a/graph.go +++ b/graph.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "errors" "sync" graphql "github.com/graph-gophers/graphql-go" @@ -112,6 +113,10 @@ func (resource * BaseResource) Update() error { } } + if resource.lock_holder != nil { + resource.lock_holder.Update() + } + return nil } @@ -133,7 +138,7 @@ type Resource interface { AddParent(parent Resource) error Children() []Resource Parents() []Resource - Lock() error + Lock(event Event) error Unlock() error } @@ -141,30 +146,30 @@ type BaseResource struct { BaseNode parents []Resource children []Resource - locked bool + lock_holder Event 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 { +func (resource * BaseResource) Lock(event Event) error { var err error = nil locked := false resource.state_lock.Lock() - if resource.locked == true { + if resource.lock_holder != nil { err = errors.New("Resource already locked") } else { all_children_locked := true for _, child := range resource.Children() { - err = child.Lock() + err = child.Lock(event) if err != nil { all_children_locked = false break } } if all_children_locked == true { - resource.locked = true + resource.lock_holder = event locked = true } } @@ -183,7 +188,7 @@ func (resource * BaseResource) Unlock() error { unlocked := false resource.state_lock.Lock() - if resource.locked == false { + if resource.lock_holder == nil { err = errors.New("Resource already unlocked") } else { all_children_unlocked := true @@ -195,7 +200,7 @@ func (resource * BaseResource) Unlock() error { } } if all_children_unlocked == true{ - resource.locked = false + resource.lock_holder = nil unlocked = true } } @@ -348,12 +353,26 @@ type EventManager struct { root_event Event } -func NewEventManager() * EventManager { - state := &EventManager{ +// root_event's requirements must be in dag_nodes, and dag_nodes must be ordered by dependency(no children first) +func NewEventManager(root_event Event, dag_nodes []Resource) * EventManager { + + manager := &EventManager{ dag_nodes: map[graphql.ID]Resource{}, root_event: nil, } - return state; + + // Construct the DAG + for _, resource := range dag_nodes { + err := manager.AddResource(resource) + if err != nil { + log.Printf("Failed to add %s to EventManager: %s", resource.ID(), err) + return nil + } + } + + manager.AddEvent(nil, root_event) + + return manager; } func (manager * EventManager) FindResource(id graphql.ID) Resource { @@ -399,14 +418,10 @@ func (manager * EventManager) AddResource(resource Resource) error { // Add resources created by the event to the DAG // Add child to parent func (manager * EventManager) AddEvent(parent Event, child Event) error { - if manager.root_event.FindChild(child.ID()) != nil { - error_str := fmt.Sprintf("Event %s already exists in the event tree, can not add again", child.ID()) - return errors.New(error_str) - } - - if manager.root_event.FindChild(parent.ID()) == nil { - error_str := fmt.Sprintf("Event %s is not present in the event tree, cannot add %s as child", parent.ID(), child.ID()) - return errors.New(error_str) + if child == nil { + return errors.New("Cannot add nil Event to EventManager") + } else if len(child.Children()) != 0 { + return errors.New("Adding events recursively not implemented") } for _, resource := range child.RequiredResources() { @@ -425,7 +440,28 @@ func (manager * EventManager) AddEvent(parent Event, child Event) error { } } - parent.AddChild(child) + if manager.root_event == nil && parent != nil { + error_str := fmt.Sprintf("EventManager has no root, so can't add event to parent") + return errors.New(error_str) + } else if manager.root_event != nil && parent == nil { + // TODO + return errors.New("Replacing root event not implemented") + } else if manager.root_event == nil && parent == nil { + manager.root_event = child + } else { + if manager.root_event.FindChild(parent.ID()) == nil { + error_str := fmt.Sprintf("Event %s is not present in the event tree, cannot add %s as child", parent.ID(), child.ID()) + return errors.New(error_str) + } + + if manager.root_event.FindChild(child.ID()) != nil { + error_str := fmt.Sprintf("Event %s already exists in the event tree, can not add again", child.ID()) + return errors.New(error_str) + } + + parent.AddChild(child) + } + return nil } diff --git a/graph_test.go b/graph_test.go index a9b2ee6..18d1425 100644 --- a/graph_test.go +++ b/graph_test.go @@ -50,9 +50,9 @@ func TestNewResourceAdd(t *testing.T) { description := "A resource for testing" children := []Resource{} + root_event := NewEvent("", "", []Resource{}) test_resource := NewResource(name, description, children) - event_manager := NewEventManager() - event_manager.AddResource(test_resource) + event_manager := NewEventManager(root_event, []Resource{test_resource}) res := event_manager.FindResource(test_resource.ID()) if res == nil { @@ -65,56 +65,48 @@ func TestNewResourceAdd(t *testing.T) { } func TestDoubleResourceAdd(t * testing.T) { + root_event := NewEvent("", "", []Resource{}) test_resource := NewResource("", "", []Resource{}) - event_manager := NewEventManager() - err_1 := event_manager.AddResource(test_resource) - err_2 := event_manager.AddResource(test_resource) + event_manager := NewEventManager(root_event, []Resource{test_resource}) + err := event_manager.AddResource(test_resource) - if err_1 != nil { - t.Fatalf("First AddResource returned error %s", err_1) - } - - if err_2 == nil { + if err == nil { t.Fatal("Second AddResource returned nil") } } func TestMissingResourceAdd(t * testing.T) { + root_event := NewEvent("", "", []Resource{}) r1 := NewResource("r1", "", []Resource{}) r2 := NewResource("r2", "", []Resource{r1}) - event_manager := NewEventManager() + event_manager := NewEventManager(root_event, []Resource{}) err := event_manager.AddResource(r2) if err == nil { t.Fatal("AddResource with missing child returned nil") } } -func TestTieredResourceAdd(t * testing.T) { +func TestTieredResource(t * testing.T) { + root_event := NewEvent("", "", []Resource{}) r1 := NewResource("r1", "", []Resource{}) r2 := NewResource("r2", "", []Resource{r1}) - event_manager := NewEventManager() - err_1 := event_manager.AddResource(r1) - err_2 := event_manager.AddResource(r2) - if err_1 != nil || err_2 != nil { - t.Fatal("Failed adding tiered resource") + event_manager := NewEventManager(root_event, []Resource{r1, r2}) + if event_manager == nil { + t.Fatal("Failed to create event manager with tiered resources") } } func TestResourceUpdate(t * testing.T) { + root_event := NewEvent("", "", []Resource{}) r1 := NewResource("r1", "", []Resource{}) r2 := NewResource("r2", "", []Resource{}) r3 := NewResource("r3", "", []Resource{r1, r2}) r4 := NewResource("r4", "", []Resource{r3}) - 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 || err_4 != nil { + event_manager := NewEventManager(root_event, []Resource{r1, r2, r3, r4}) + if event_manager == nil { t.Fatal("Failed to add initial tiered resources for test") } @@ -145,41 +137,44 @@ func TestResourceUpdate(t * testing.T) { (*graph_tester)(t).CheckForNil(r4_l) } +func TestAddEvent(t * testing.T) { + +} + func TestLockResource(t * testing.T) { + root_event := NewEvent("", "", []Resource{}) 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) + event_manager := NewEventManager(root_event, []Resource{r1, r2, r3, r4}) - if err_1 != nil || err_2 != nil || err_3 != nil || err_4 != nil { + if event_manager == nil { t.Fatal("Failed to add initial tiered resources for test") } r1_l := r1.UpdateChannel() + rel := root_event.UpdateChannel() - err := r3.Lock() + err := r3.Lock(root_event) if err != nil { t.Fatal("Failed to lock r3") } (*graph_tester)(t).CheckForNil(r1_l) + (*graph_tester)(t).CheckForNil(rel) - err = r3.Lock() + err = r3.Lock(root_event) if err == nil { t.Fatal("Locked r3 after locking r3") } - err = r4.Lock() + err = r4.Lock(root_event) if err == nil { t.Fatal("Locked r4 after locking r3") } - err = r1.Lock() + err = r1.Lock(root_event) if err == nil { t.Fatal("Locked r1 after locking r3") } @@ -189,16 +184,19 @@ func TestLockResource(t * testing.T) { t.Fatal("Failed to unlock r3") } (*graph_tester)(t).CheckForNil(r1_l) + (*graph_tester)(t).CheckForNil(rel) - err = r4.Lock() + err = r4.Lock(root_event) if err != nil { t.Fatal("Failed to lock r4 after unlocking r3") } (*graph_tester)(t).CheckForNil(r1_l) + (*graph_tester)(t).CheckForNil(rel) err = r4.Unlock() if err != nil { t.Fatal("Failed to unlock r4") } (*graph_tester)(t).CheckForNil(r1_l) + (*graph_tester)(t).CheckForNil(rel) } diff --git a/main.go b/main.go index 718ab75..6f85c7c 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( ) func fake_data() * EventManager { - event_manager := NewEventManager() + resources := []Resource{} teams := []*Team{} teams = append(teams, NewTeam("6659", "A", []string{"jimmy"})) @@ -22,25 +22,20 @@ func fake_data() * EventManager { teams = append(teams, NewTeam("315", "Z", []string{"emily"})) for _, team := range teams { - err := event_manager.AddResource(team) - if err != nil { - log.Print(err) - } + resources = append(resources, team) } - - alliances := []Resource{} for i, team := range teams[:len(teams)-1] { for _, team2 := range teams[i+1:] { alliance := NewAlliance(team, team2) - alliances = append(alliances, alliance) - err := event_manager.AddResource(alliance) - if err != nil { - log.Print(err) - } + resources = append(resources, alliance) } } + root_event := NewEvent("root_event", "", []Resource{}) + + event_manager := NewEventManager(root_event, resources) + return event_manager }