Added constructor for EventManger that requires a single event with no children and a list of Resources ordered by dependency. Updated main and tests to match.

graph-rework
noah metz 2023-04-08 16:51:34 -06:00
parent d4da75d6a1
commit 81b3ab65bf
3 changed files with 96 additions and 67 deletions

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"log"
"errors" "errors"
"sync" "sync"
graphql "github.com/graph-gophers/graphql-go" 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 return nil
} }
@ -133,7 +138,7 @@ type Resource interface {
AddParent(parent Resource) error AddParent(parent Resource) error
Children() []Resource Children() []Resource
Parents() []Resource Parents() []Resource
Lock() error Lock(event Event) error
Unlock() error Unlock() error
} }
@ -141,30 +146,30 @@ type BaseResource struct {
BaseNode BaseNode
parents []Resource parents []Resource
children []Resource children []Resource
locked bool lock_holder Event
state_lock sync.Mutex 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 // 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 // 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 var err error = nil
locked := false locked := false
resource.state_lock.Lock() resource.state_lock.Lock()
if resource.locked == true { if resource.lock_holder != nil {
err = errors.New("Resource already locked") err = errors.New("Resource already locked")
} else { } else {
all_children_locked := true all_children_locked := true
for _, child := range resource.Children() { for _, child := range resource.Children() {
err = child.Lock() err = child.Lock(event)
if err != nil { if err != nil {
all_children_locked = false all_children_locked = false
break break
} }
} }
if all_children_locked == true { if all_children_locked == true {
resource.locked = true resource.lock_holder = event
locked = true locked = true
} }
} }
@ -183,7 +188,7 @@ func (resource * BaseResource) Unlock() error {
unlocked := false unlocked := false
resource.state_lock.Lock() resource.state_lock.Lock()
if resource.locked == false { if resource.lock_holder == nil {
err = errors.New("Resource already unlocked") err = errors.New("Resource already unlocked")
} else { } else {
all_children_unlocked := true all_children_unlocked := true
@ -195,7 +200,7 @@ func (resource * BaseResource) Unlock() error {
} }
} }
if all_children_unlocked == true{ if all_children_unlocked == true{
resource.locked = false resource.lock_holder = nil
unlocked = true unlocked = true
} }
} }
@ -348,12 +353,26 @@ type EventManager struct {
root_event Event root_event Event
} }
func NewEventManager() * EventManager { // root_event's requirements must be in dag_nodes, and dag_nodes must be ordered by dependency(no children first)
state := &EventManager{ func NewEventManager(root_event Event, dag_nodes []Resource) * EventManager {
manager := &EventManager{
dag_nodes: map[graphql.ID]Resource{}, dag_nodes: map[graphql.ID]Resource{},
root_event: nil, 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 { 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 resources created by the event to the DAG
// Add child to parent // Add child to parent
func (manager * EventManager) AddEvent(parent Event, child Event) error { func (manager * EventManager) AddEvent(parent Event, child Event) error {
if manager.root_event.FindChild(child.ID()) != nil { if child == nil {
error_str := fmt.Sprintf("Event %s already exists in the event tree, can not add again", child.ID()) return errors.New("Cannot add nil Event to EventManager")
return errors.New(error_str) } else if len(child.Children()) != 0 {
} return errors.New("Adding events recursively not implemented")
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)
} }
for _, resource := range child.RequiredResources() { for _, resource := range child.RequiredResources() {
@ -425,7 +440,28 @@ func (manager * EventManager) AddEvent(parent Event, child Event) error {
} }
} }
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) parent.AddChild(child)
}
return nil return nil
} }

@ -50,9 +50,9 @@ func TestNewResourceAdd(t *testing.T) {
description := "A resource for testing" description := "A resource for testing"
children := []Resource{} children := []Resource{}
root_event := NewEvent("", "", []Resource{})
test_resource := NewResource(name, description, children) test_resource := NewResource(name, description, children)
event_manager := NewEventManager() event_manager := NewEventManager(root_event, []Resource{test_resource})
event_manager.AddResource(test_resource)
res := event_manager.FindResource(test_resource.ID()) res := event_manager.FindResource(test_resource.ID())
if res == nil { if res == nil {
@ -65,56 +65,48 @@ func TestNewResourceAdd(t *testing.T) {
} }
func TestDoubleResourceAdd(t * testing.T) { func TestDoubleResourceAdd(t * testing.T) {
root_event := NewEvent("", "", []Resource{})
test_resource := NewResource("", "", []Resource{}) test_resource := NewResource("", "", []Resource{})
event_manager := NewEventManager() event_manager := NewEventManager(root_event, []Resource{test_resource})
err_1 := event_manager.AddResource(test_resource) err := event_manager.AddResource(test_resource)
err_2 := event_manager.AddResource(test_resource)
if err_1 != nil { if err == nil {
t.Fatalf("First AddResource returned error %s", err_1)
}
if err_2 == nil {
t.Fatal("Second AddResource returned nil") t.Fatal("Second AddResource returned nil")
} }
} }
func TestMissingResourceAdd(t * testing.T) { func TestMissingResourceAdd(t * testing.T) {
root_event := NewEvent("", "", []Resource{})
r1 := NewResource("r1", "", []Resource{}) r1 := NewResource("r1", "", []Resource{})
r2 := NewResource("r2", "", []Resource{r1}) r2 := NewResource("r2", "", []Resource{r1})
event_manager := NewEventManager() event_manager := NewEventManager(root_event, []Resource{})
err := event_manager.AddResource(r2) err := event_manager.AddResource(r2)
if err == nil { if err == nil {
t.Fatal("AddResource with missing child returned 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{}) r1 := NewResource("r1", "", []Resource{})
r2 := NewResource("r2", "", []Resource{r1}) r2 := NewResource("r2", "", []Resource{r1})
event_manager := NewEventManager() event_manager := NewEventManager(root_event, []Resource{r1, r2})
err_1 := event_manager.AddResource(r1) if event_manager == nil {
err_2 := event_manager.AddResource(r2) t.Fatal("Failed to create event manager with tiered resources")
if err_1 != nil || err_2 != nil {
t.Fatal("Failed adding tiered resource")
} }
} }
func TestResourceUpdate(t * testing.T) { func TestResourceUpdate(t * testing.T) {
root_event := NewEvent("", "", []Resource{})
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("r4", "", []Resource{r3}) r4 := NewResource("r4", "", []Resource{r3})
event_manager := NewEventManager() event_manager := NewEventManager(root_event, []Resource{r1, r2, r3, r4})
err_1 := event_manager.AddResource(r1) if event_manager == nil {
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 {
t.Fatal("Failed to add initial tiered resources for test") 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) (*graph_tester)(t).CheckForNil(r4_l)
} }
func TestAddEvent(t * testing.T) {
}
func TestLockResource(t * testing.T) { func TestLockResource(t * testing.T) {
root_event := NewEvent("", "", []Resource{})
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}) r4 := NewResource("r3", "", []Resource{r1, r2})
event_manager := NewEventManager() event_manager := NewEventManager(root_event, []Resource{r1, r2, r3, r4})
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 { if event_manager == nil {
t.Fatal("Failed to add initial tiered resources for test") t.Fatal("Failed to add initial tiered resources for test")
} }
r1_l := r1.UpdateChannel() r1_l := r1.UpdateChannel()
rel := root_event.UpdateChannel()
err := r3.Lock() err := r3.Lock(root_event)
if err != nil { if err != nil {
t.Fatal("Failed to lock r3") t.Fatal("Failed to lock r3")
} }
(*graph_tester)(t).CheckForNil(r1_l) (*graph_tester)(t).CheckForNil(r1_l)
(*graph_tester)(t).CheckForNil(rel)
err = r3.Lock() err = r3.Lock(root_event)
if err == nil { if err == nil {
t.Fatal("Locked r3 after locking r3") t.Fatal("Locked r3 after locking r3")
} }
err = r4.Lock() err = r4.Lock(root_event)
if err == nil { if err == nil {
t.Fatal("Locked r4 after locking r3") t.Fatal("Locked r4 after locking r3")
} }
err = r1.Lock() err = r1.Lock(root_event)
if err == nil { if err == nil {
t.Fatal("Locked r1 after locking r3") t.Fatal("Locked r1 after locking r3")
} }
@ -189,16 +184,19 @@ func TestLockResource(t * testing.T) {
t.Fatal("Failed to unlock r3") t.Fatal("Failed to unlock r3")
} }
(*graph_tester)(t).CheckForNil(r1_l) (*graph_tester)(t).CheckForNil(r1_l)
(*graph_tester)(t).CheckForNil(rel)
err = r4.Lock() err = r4.Lock(root_event)
if err != nil { if err != nil {
t.Fatal("Failed to lock r4 after unlocking r3") t.Fatal("Failed to lock r4 after unlocking r3")
} }
(*graph_tester)(t).CheckForNil(r1_l) (*graph_tester)(t).CheckForNil(r1_l)
(*graph_tester)(t).CheckForNil(rel)
err = r4.Unlock() err = r4.Unlock()
if err != nil { if err != nil {
t.Fatal("Failed to unlock r4") t.Fatal("Failed to unlock r4")
} }
(*graph_tester)(t).CheckForNil(r1_l) (*graph_tester)(t).CheckForNil(r1_l)
(*graph_tester)(t).CheckForNil(rel)
} }

@ -5,7 +5,7 @@ import (
) )
func fake_data() * EventManager { func fake_data() * EventManager {
event_manager := NewEventManager() resources := []Resource{}
teams := []*Team{} teams := []*Team{}
teams = append(teams, NewTeam("6659", "A", []string{"jimmy"})) teams = append(teams, NewTeam("6659", "A", []string{"jimmy"}))
@ -22,25 +22,20 @@ func fake_data() * EventManager {
teams = append(teams, NewTeam("315", "Z", []string{"emily"})) teams = append(teams, NewTeam("315", "Z", []string{"emily"}))
for _, team := range teams { for _, team := range teams {
err := event_manager.AddResource(team) resources = append(resources, team)
if err != nil {
log.Print(err)
} }
}
alliances := []Resource{}
for i, team := range teams[:len(teams)-1] { for i, team := range teams[:len(teams)-1] {
for _, team2 := range teams[i+1:] { for _, team2 := range teams[i+1:] {
alliance := NewAlliance(team, team2) alliance := NewAlliance(team, team2)
alliances = append(alliances, alliance) resources = append(resources, alliance)
err := event_manager.AddResource(alliance)
if err != nil {
log.Print(err)
}
} }
} }
root_event := NewEvent("root_event", "", []Resource{})
event_manager := NewEventManager(root_event, resources)
return event_manager return event_manager
} }