From 16b955f4818936ddbc779051c0c404ead3292305 Mon Sep 17 00:00:00 2001 From: Patrick Fairbank Date: Sat, 27 Aug 2016 23:42:15 -0700 Subject: [PATCH] Prevent concurrent writes on the same websocket. This was causing occasional UI glitches. --- alliance_station_display_test.go | 3 ++- announcer_display_test.go | 3 ++- audience_display_test.go | 3 ++- match_play_test.go | 5 +++-- pit_display_test.go | 3 ++- referee_display_test.go | 3 ++- scoring_display_test.go | 5 +++-- setup_lower_thirds_test.go | 3 ++- tba.go | 2 +- web.go | 12 ++++++++++-- 10 files changed, 29 insertions(+), 13 deletions(-) diff --git a/alliance_station_display_test.go b/alliance_station_display_test.go index 0edc56d..3ac9d03 100644 --- a/alliance_station_display_test.go +++ b/alliance_station_display_test.go @@ -6,6 +6,7 @@ package main import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "sync" "testing" "time" ) @@ -40,7 +41,7 @@ func TestAllianceStationDisplayWebsocket(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/alliance_station/websocket?displayId=1", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} // Should get a few status updates right after connection. readWebsocketType(t, ws, "setAllianceStationDisplay") diff --git a/announcer_display_test.go b/announcer_display_test.go index 76250aa..e869f26 100644 --- a/announcer_display_test.go +++ b/announcer_display_test.go @@ -6,6 +6,7 @@ package main import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "sync" "testing" "time" ) @@ -40,7 +41,7 @@ func TestAnnouncerDisplayWebsocket(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/announcer/websocket", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} // Should get a few status updates right after connection. readWebsocketType(t, ws, "setMatch") diff --git a/audience_display_test.go b/audience_display_test.go index 3e0d901..4b6d9e4 100644 --- a/audience_display_test.go +++ b/audience_display_test.go @@ -6,6 +6,7 @@ package main import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "sync" "testing" ) @@ -39,7 +40,7 @@ func TestAudienceDisplayWebsocket(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/audience/websocket", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} // Should get a few status updates right after connection. readWebsocketType(t, ws, "matchTiming") diff --git a/match_play_test.go b/match_play_test.go index 708e8cd..3a673d7 100644 --- a/match_play_test.go +++ b/match_play_test.go @@ -10,6 +10,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/stretchr/testify/assert" "log" + "sync" "testing" "time" ) @@ -277,7 +278,7 @@ func TestMatchPlayWebsocketCommands(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/match_play/websocket", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} // Should get a few status updates right after connection. readWebsocketType(t, ws, "status") @@ -372,7 +373,7 @@ func TestMatchPlayWebsocketNotifications(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/match_play/websocket", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} // Should get a few status updates right after connection. readWebsocketType(t, ws, "status") diff --git a/pit_display_test.go b/pit_display_test.go index 1973e3d..c078d38 100644 --- a/pit_display_test.go +++ b/pit_display_test.go @@ -6,6 +6,7 @@ package main import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "sync" "testing" ) @@ -38,7 +39,7 @@ func TestPitDisplayWebsocket(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/pit/websocket", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} // Check forced reloading as that is the only purpose the pit websocket serves. recorder := getHttpResponse("/setup/field/reload_displays") diff --git a/referee_display_test.go b/referee_display_test.go index 062c8ad..0e0bfd8 100644 --- a/referee_display_test.go +++ b/referee_display_test.go @@ -6,6 +6,7 @@ package main import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "sync" "testing" "time" ) @@ -40,7 +41,7 @@ func TestRefereeDisplayWebsocket(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/referee/websocket", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} // Test foul addition. foulData := struct { diff --git a/scoring_display_test.go b/scoring_display_test.go index 4f3f45d..0e39314 100644 --- a/scoring_display_test.go +++ b/scoring_display_test.go @@ -6,6 +6,7 @@ package main import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "sync" "testing" ) @@ -46,11 +47,11 @@ func TestScoringDisplayWebsocket(t *testing.T) { redConn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/red/websocket", nil) assert.Nil(t, err) defer redConn.Close() - redWs := &Websocket{redConn} + redWs := &Websocket{redConn, new(sync.Mutex)} blueConn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/displays/scoring/blue/websocket", nil) assert.Nil(t, err) defer blueConn.Close() - blueWs := &Websocket{blueConn} + blueWs := &Websocket{blueConn, new(sync.Mutex)} // Should receive a score update right after connection. readWebsocketType(t, redWs, "score") diff --git a/setup_lower_thirds_test.go b/setup_lower_thirds_test.go index 1870f9c..a2f3cc7 100644 --- a/setup_lower_thirds_test.go +++ b/setup_lower_thirds_test.go @@ -6,6 +6,7 @@ package main import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "sync" "testing" "time" ) @@ -34,7 +35,7 @@ func TestSetupLowerThirds(t *testing.T) { conn, _, err := websocket.DefaultDialer.Dial(wsUrl+"/setup/lower_thirds/websocket", nil) assert.Nil(t, err) defer conn.Close() - ws := &Websocket{conn} + ws := &Websocket{conn, new(sync.Mutex)} ws.Write("saveLowerThird", LowerThird{1, "Top Text 4", "Bottom Text 1", 0}) time.Sleep(time.Millisecond * 10) // Allow some time for the command to be processed. diff --git a/tba.go b/tba.go index b7eb844..d48de03 100644 --- a/tba.go +++ b/tba.go @@ -279,7 +279,7 @@ func PublishMatches() error { tbaMatches[i] = TbaMatch{"qm", 0, matchNumber, map[string]interface{}{"red": redAlliance, "blue": blueAlliance}, scoreBreakdown, match.Time.Local().Format("3:04 PM"), - match.Time.Format("2006-01-02T15:04:05")} + match.Time.UTC().Format("2006-01-02T15:04:05")} if match.Type == "elimination" { tbaMatches[i].CompLevel = map[int]string{1: "f", 2: "sf", 4: "qf", 8: "ef"}[match.ElimRound] tbaMatches[i].SetNumber = match.ElimGroup diff --git a/web.go b/web.go index c3ddf3d..f35b5bc 100644 --- a/web.go +++ b/web.go @@ -12,6 +12,7 @@ import ( "github.com/gorilla/websocket" "log" "net/http" + "sync" "text/template" ) @@ -44,7 +45,8 @@ var templateHelpers = template.FuncMap{ // Wraps the Gorilla Websocket module so that we can define additional functions on it. type Websocket struct { - conn *websocket.Conn + conn *websocket.Conn + writeMutex *sync.Mutex } type WebsocketMessage struct { @@ -58,7 +60,7 @@ func NewWebsocket(w http.ResponseWriter, r *http.Request) (*Websocket, error) { if err != nil { return nil, err } - return &Websocket{conn}, nil + return &Websocket{conn, new(sync.Mutex)}, nil } func (websocket *Websocket) Close() { @@ -72,14 +74,20 @@ func (websocket *Websocket) Read() (string, interface{}, error) { } func (websocket *Websocket) Write(messageType string, data interface{}) error { + websocket.writeMutex.Lock() + defer websocket.writeMutex.Unlock() return websocket.conn.WriteJSON(WebsocketMessage{messageType, data}) } func (websocket *Websocket) WriteError(errorMessage string) error { + websocket.writeMutex.Lock() + defer websocket.writeMutex.Unlock() return websocket.conn.WriteJSON(WebsocketMessage{"error", errorMessage}) } func (websocket *Websocket) ShowDialog(message string) error { + websocket.writeMutex.Lock() + defer websocket.writeMutex.Unlock() return websocket.conn.WriteJSON(WebsocketMessage{"dialog", message}) }