diff --git a/go.mod b/go.mod index 142fa5e..e39d012 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module graphvent +module git.metznet.ca/MetzNet/graphvent go 1.20 diff --git a/graph.go b/graph.go index 8b3b46b..f5e4a1a 100644 --- a/graph.go +++ b/graph.go @@ -75,6 +75,7 @@ type GraphSignal interface { Time() time.Time Last() string Trace(id string) GraphSignal + String() string } type BaseSignal struct { @@ -85,6 +86,15 @@ type BaseSignal struct { last_id string } +func (signal BaseSignal) String() string { + source_name := "nil" + source := signal.source + if source != nil { + source_name = source.Name() + } + return fmt.Sprintf("{type: %s, description: %s, source: %s, last: %s}", signal.signal_type, signal.description, source_name, signal.last_id) +} + func (signal BaseSignal) Time() time.Time { return signal.time } @@ -136,7 +146,7 @@ func NewBaseNode(name string, description string, id string) BaseNode { name: name, description: description, id: id, - signal: make(chan GraphSignal, 1000), + signal: make(chan GraphSignal, 512), listeners: map[chan GraphSignal]chan GraphSignal{}, } log.Logf("graph", "NEW_NODE: %s - %s", node.ID(), node.Name()) @@ -225,12 +235,17 @@ func (node * BaseNode) update(signal GraphSignal) { } func SendUpdate(node GraphNode, signal GraphSignal) { - if signal.Source() != nil { - log.Logf("update", "UPDATE %s -> %s: %+v", signal.Source().Name(), node.Name(), signal) - } else { - log.Logf("update", "UPDATE %s: %+v", node.Name(), signal) + source := signal.Source() + source_name := "nil" + if source != nil { + source_name = source.Name() + } + node_name := "nil" + if node != nil { + node_name = node.Name() } + log.Logf("update", "UPDATE %s -> %s: %+v", source_name, node_name, signal) node.UpdateListeners(signal) node.update(signal) } diff --git a/main.go b/main.go index 5d3b889..36502c2 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,8 @@ package main import ( "time" + "runtime/pprof" + "os" ) func fake_team(org string, id string, names []string) (*Team, []*Member) { @@ -13,7 +15,7 @@ func fake_team(org string, id string, names []string) (*Team, []*Member) { return team, members } -func fake_data() (* EventManager, []*Arena, []*Arena) { +func fake_data() (* EventManager, []Arena, []Arena) { resources := []Resource{} teams_div1 := []*Team{} @@ -69,10 +71,10 @@ func fake_data() (* EventManager, []*Arena, []*Arena) { resources = append(resources, m15[0]) resources = append(resources, m16[0]) - arenas_div1 := []*Arena{} + arenas_div1 := []Arena{} arenas_div1 = append(arenas_div1, NewVirtualArena("Arena 1")) arenas_div1 = append(arenas_div1, NewVirtualArena("Arena 2")) - arenas_div2 := []*Arena{} + arenas_div2 := []Arena{} arenas_div2 = append(arenas_div2, NewVirtualArena("Arena 3")) arenas_div2 = append(arenas_div2, NewVirtualArena("Arena 4")) @@ -167,12 +169,12 @@ func fake_data() (* EventManager, []*Arena, []*Arena) { type FakeClient struct { state string start time.Time - arena * Arena + arena Arena update chan GraphSignal games_done int } -func NewFakeClient(arena *Arena) * FakeClient { +func NewFakeClient(arena Arena) * FakeClient { client := &FakeClient{ state: "init", start: time.Now(), @@ -187,7 +189,7 @@ func NewFakeClient(arena *Arena) * FakeClient { func (client * FakeClient) process_update(update GraphSignal) { arena := client.arena if update.Source() != nil { - log.Logf("test", "FAKE_CLIENT_UPDATE: %s -> %+v", update.Source().ID(), update) + log.Logf("test", "FAKE_CLIENT_UPDATE: %s -> %+v", update.Source().Name(), update) } else { log.Logf("test", "FAKE_CLIENT_UPDATE: nil -> %+v", update) } @@ -236,12 +238,27 @@ func (client * FakeClient) process_update(update GraphSignal) { func main() { event_manager, arenas_div1, arenas_div2 := fake_data() + + go func() { + cpufile, err := os.OpenFile("graphvent.cpu", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + panic("Failed to open cpu profile file") + } + memfile, err := os.OpenFile("graphvent.mem", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + panic("Failed to open mem profile file") + } + + pprof.StartCPUProfile(cpufile) + time.Sleep(3000 * time.Millisecond) + pprof.WriteHeapProfile(memfile) + }() + // Fake arena clients arena_1_client := NewFakeClient(arenas_div1[0]) arena_2_client := NewFakeClient(arenas_div1[1]) arena_3_client := NewFakeClient(arenas_div2[0]) arena_4_client := NewFakeClient(arenas_div2[1]) - go func() { for true { select { @@ -267,4 +284,5 @@ func main() { log.Logf("test", "Client 3 games: %d", arena_3_client.games_done) log.Logf("test", "Client 4 games: %d", arena_4_client.games_done) } + pprof.StopCPUProfile() } diff --git a/resource.go b/resource.go index ad75cf9..0d7f9a1 100644 --- a/resource.go +++ b/resource.go @@ -14,11 +14,17 @@ func (resource * BaseResource) update(signal GraphSignal) { for _, parent := range resource.Parents() { SendUpdate(parent, new_signal) } - + resource.lock_holder_lock.Lock() if resource.lock_holder != nil { if resource.lock_holder.ID() != signal.Last() { - SendUpdate(resource.lock_holder, new_signal) + lock_holder := resource.lock_holder + resource.lock_holder_lock.Unlock() + SendUpdate(lock_holder, new_signal) + } else { + resource.lock_holder_lock.Unlock() } + } else { + resource.lock_holder_lock.Unlock() } } @@ -29,7 +35,7 @@ func (resource * BaseResource) update(signal GraphSignal) { // The device connection should be maintained as much as possible(requiring some reconnection behaviour in the background) type Resource interface { GraphNode - Owner() Event + Owner() GraphNode Children() []Resource Parents() []Resource @@ -37,12 +43,12 @@ type Resource interface { LockParents() UnlockParents() - SetOwner(owner Event) + SetOwner(owner GraphNode) LockState() UnlockState() - lock(event Event) error - unlock(event Event) error + lock(node GraphNode) error + unlock(node GraphNode) error Connect(abort chan error) bool } @@ -106,7 +112,7 @@ func UnlockResource(resource Resource, event Event) error { return nil } -func LockResource(resource Resource, event Event) error { +func LockResource(resource Resource, node GraphNode) error { resource.LockState() if resource.Owner() != nil { resource.UnlockState() @@ -114,7 +120,7 @@ func LockResource(resource Resource, event Event) error { return errors.New(err_str) } - err := resource.lock(event) + err := resource.lock(node) if err != nil { resource.UnlockState() err_str := fmt.Sprintf("Failed to lock resource: %s", err) @@ -124,7 +130,7 @@ func LockResource(resource Resource, event Event) error { var lock_err error = nil locked_resources := []Resource{} for _, child := range resource.Children() { - err := LockResource(child, event) + err := LockResource(child, node) if err != nil{ lock_err = err break @@ -138,7 +144,7 @@ func LockResource(resource Resource, event Event) error { return errors.New(err_str) } - resource.SetOwner(event) + resource.SetOwner(node) resource.UnlockState() @@ -175,12 +181,15 @@ type BaseResource struct { parents_lock sync.Mutex children []Resource children_lock sync.Mutex - lock_holder Event + lock_holder GraphNode + lock_holder_lock sync.Mutex state_lock sync.Mutex } -func (resource * BaseResource) SetOwner(owner Event) { +func (resource * BaseResource) SetOwner(owner GraphNode) { + resource.lock_holder_lock.Lock() resource.lock_holder = owner + resource.lock_holder_lock.Unlock() } func (resource * BaseResource) LockState() { @@ -195,16 +204,16 @@ func (resource * BaseResource) Connect(abort chan error) bool { return false } -func (resource * BaseResource) Owner() Event { +func (resource * BaseResource) Owner() GraphNode { return resource.lock_holder } //BaseResources don't check anything special when locking/unlocking -func (resource * BaseResource) lock(event Event) error { +func (resource * BaseResource) lock(node GraphNode) error { return nil } -func (resource * BaseResource) unlock(event Event) error { +func (resource * BaseResource) unlock(node GraphNode) error { return nil } diff --git a/vex.go b/vex.go index 6aba5d1..f8195dc 100644 --- a/vex.go +++ b/vex.go @@ -63,13 +63,17 @@ func NewAlliance(team0 * Team, team1 * Team) * Alliance { return resource } -type Arena struct { +type Arena interface { + Resource +} + +type VirtualArena struct { BaseResource connected bool } -func NewVirtualArena(name string) * Arena { - arena := &Arena{ +func NewVirtualArena(name string) * VirtualArena { + arena := &VirtualArena{ BaseResource: NewBaseResource(name, "A virtual vex arena", []Resource{}), connected: false, } @@ -77,7 +81,7 @@ func NewVirtualArena(name string) * Arena { return arena } -func (arena * Arena) lock(event Event) error { +func (arena * VirtualArena) lock(node GraphNode) error { if arena.connected == false { log.Logf("vex", "ARENA NOT CONNECTED: %s", arena.Name()) error_str := fmt.Sprintf("%s is not connected, cannot lock", arena.Name()) @@ -86,15 +90,15 @@ func (arena * Arena) lock(event Event) error { return nil } -func (arena * Arena) update(signal GraphSignal) { +func (arena * VirtualArena) update(signal GraphSignal) { log.Logf("vex", "ARENA_UPDATE: %s", arena.Name()) arena.signal <- signal arena.BaseResource.update(signal) } -func (arena * Arena) Connect(abort chan error) bool { +func (arena * VirtualArena) Connect(abort chan error) bool { log.Logf("vex", "Connecting %s", arena.Name()) - go func(arena * Arena, abort chan error) { + go func(arena * VirtualArena, abort chan error) { update_str := fmt.Sprintf("VIRTUAL_ARENA connected: %s", arena.Name()) signal := NewSignal(arena, "resource_connected") signal.description = update_str @@ -114,13 +118,31 @@ func (arena * Arena) Connect(abort chan error) bool { return true } +type VexEvent struct { + BaseEvent +} + +func NewVexEvent(name string, description string) * VexEvent { + event := &VexEvent{ + BaseEvent: NewBaseEvent(name, description, []Resource{}), + } + + event.actions["wait"] = EventWait(event) + event.actions["start"] = func() (string, error) { + log.Logf("vex", "STARTING_VEX_TOURNAMENT %s", event.Name()) + return "wait", nil + } + + return event +} + const start_slack = 250 * time.Millisecond -const TEMP_AUTON_TIME = time.Second * 1 -const TEMP_DRIVE_TIME = time.Second * 1 +const TEMP_AUTON_TIME = 250 * time.Millisecond +const TEMP_DRIVE_TIME = 250 * time.Millisecond type Match struct { BaseEvent - arena * Arena + arena Arena state string control string control_start time.Time @@ -131,7 +153,7 @@ func (match * Match) update(signal GraphSignal) { match.BaseEvent.update(new_signal) } -func NewMatch(alliance0 * Alliance, alliance1 * Alliance, arena * Arena) * Match { +func NewMatch(alliance0 * Alliance, alliance1 * Alliance, arena Arena) * Match { name := fmt.Sprintf("Match: %s vs. %s on %s", alliance0.Name(), alliance1.Name(), arena.Name()) description := "A vex match" @@ -239,6 +261,8 @@ func NewMatch(alliance0 * Alliance, alliance1 * Alliance, arena * Arena) * Match } match.actions["driver_done"] = func() (string, error) { + new_signal := NewSignal(match, "driver_done") + new_signal.time = time.Now() SendUpdate(match, NewSignal(match, "driver_done")) return "wait", nil }