Prevent concurrent writes on the same websocket.

This was causing occasional UI glitches.
This commit is contained in:
Patrick Fairbank
2016-08-27 23:42:15 -07:00
parent 3cd216cbf4
commit 16b955f481
10 changed files with 29 additions and 13 deletions

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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")

View File

@@ -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 {

View File

@@ -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")

View File

@@ -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.

2
tba.go
View File

@@ -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

12
web.go
View File

@@ -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})
}