Skip to content

Commit 2c9008f

Browse files
authored
Fix some potential race conditions (#168)
* Fix some potential race conditions * more * more
1 parent a0b7595 commit 2c9008f

4 files changed

Lines changed: 16 additions & 12 deletions

File tree

internal/gameServer/server.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ func (g *GameServer) ManageBuffer() {
148148
}
149149
}
150150
}
151-
g.gameDataMutex.Unlock()
152151

153152
if leadPlayer > 0 {
154153
if bufferHealth > float32(g.BufferTarget)+0.75 && g.gameData.bufferSize > 0 {
@@ -159,6 +158,7 @@ func (g *GameServer) ManageBuffer() {
159158
g.Logger.Info("increased buffer size", "bufferHealth", bufferHealth, "bufferSize", g.gameData.bufferSize, "leadPlayer", leadPlayer)
160159
}
161160
}
161+
g.gameDataMutex.Unlock()
162162

163163
time.Sleep(time.Second)
164164
}
@@ -170,7 +170,9 @@ func (g *GameServer) ManagePlayers() {
170170
playersActive := false // used to check if anyone is still around
171171
var i byte
172172

173-
g.gameDataMutex.Lock() // PlayerAlive and Status can be modified by processUDP in a different thread
173+
// Lock registrations before gameData so TCP paths that take registrationsMutex then gameDataMutex cannot deadlock.
174+
g.registrationsMutex.Lock()
175+
g.gameDataMutex.Lock()
174176
for i = range 4 {
175177
_, ok := g.registrations[i]
176178
if ok {
@@ -181,9 +183,7 @@ func (g *GameServer) ManagePlayers() {
181183
g.Logger.Info("player disconnected UDP", "player", i, "regID", g.registrations[i].regID, "address", g.gameData.playerAddresses[i])
182184
g.gameData.status |= (0x1 << (i + 1))
183185

184-
g.registrationsMutex.Lock() // Registrations can be modified by processTCP
185186
delete(g.registrations, i)
186-
g.registrationsMutex.Unlock()
187187

188188
for k, v := range g.Players {
189189
if v.Number == int(i) {
@@ -199,6 +199,7 @@ func (g *GameServer) ManagePlayers() {
199199
g.gameData.playerAlive[i] = false
200200
}
201201
g.gameDataMutex.Unlock()
202+
g.registrationsMutex.Unlock()
202203

203204
if !playersActive {
204205
g.Logger.Info("no more players, closing room", "numPlayers", g.NumberOfPlayers, "clientSHA", g.ClientSha, "playTime", time.Since(g.StartTime).String())

internal/gameServer/tcp.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,13 +339,12 @@ func (g *GameServer) processTCP(conn *net.TCPConn) {
339339
if v.regID == regID {
340340
g.Logger.Info("player disconnected TCP", "regID", regID, "player", i, "address", conn.RemoteAddr().String())
341341

342-
g.gameDataMutex.Lock() // any player can modify this, which would be in a different thread
342+
// Same lock order as ManagePlayers / RegisterPlayer: registrationsMutex, then gameDataMutex.
343+
g.registrationsMutex.Lock()
344+
g.gameDataMutex.Lock()
343345
g.gameData.playerAlive[i] = false
344346
g.gameData.status |= (0x1 << (i + 1))
345-
346-
g.registrationsMutex.Lock() // any player can modify this, which would be in a different thread
347347
delete(g.registrations, i)
348-
g.registrationsMutex.Unlock()
349348

350349
for k, v := range g.Players {
351350
if v.Number == int(i) {
@@ -357,6 +356,7 @@ func (g *GameServer) processTCP(conn *net.TCPConn) {
357356
}
358357
g.gameData.bufferHealth[i] = g.gameData.bufferHealth[i][:0]
359358
g.gameDataMutex.Unlock()
359+
g.registrationsMutex.Unlock()
360360
}
361361
}
362362
}

internal/gameServer/udp.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func (g *GameServer) processUDP(addr *net.UDPAddr) {
125125
playerNumber := g.gameData.recvBuffer[1]
126126
switch g.gameData.recvBuffer[0] {
127127
case KeyInfoClient:
128+
g.gameDataMutex.Lock()
128129
g.gameData.playerAddresses[playerNumber] = addr
129130
count := binary.BigEndian.Uint32(g.gameData.recvBuffer[2:])
130131

@@ -138,19 +139,20 @@ func (g *GameServer) processUDP(addr *net.UDPAddr) {
138139
g.sendUDPInput(count, g.gameData.playerAddresses[i], playerNumber, true, NoRegID)
139140
}
140141
}
142+
g.gameDataMutex.Unlock()
141143
case PlayerInputRequest:
142144
regID := binary.BigEndian.Uint32(g.gameData.recvBuffer[2:])
143145
count := binary.BigEndian.Uint32(g.gameData.recvBuffer[6:])
144146
spectator := g.gameData.recvBuffer[10]
145-
if uintLarger(count, g.gameData.leadCount) && spectator == 0 {
146-
g.gameData.leadCount = count
147-
}
148147
sendingPlayerNumber, err := g.getPlayerNumberByID(regID)
149148
if err != nil {
150149
g.Logger.Error(err, "could not process request", "regID", regID)
151150
return
152151
}
153-
g.gameDataMutex.Lock() // playerAlive, countLag and bufferHealth can be modified in different threads
152+
g.gameDataMutex.Lock() // playerAlive, countLag, bufferHealth, leadCount; other threads touch these
153+
if uintLarger(count, g.gameData.leadCount) && spectator == 0 {
154+
g.gameData.leadCount = count
155+
}
154156
g.gameData.countLag[sendingPlayerNumber] = append(g.gameData.countLag[sendingPlayerNumber], g.sendUDPInput(count, addr, playerNumber, spectator != 0, sendingPlayerNumber))
155157
g.gameData.bufferHealth[sendingPlayerNumber] = append(g.gameData.bufferHealth[sendingPlayerNumber], g.gameData.recvBuffer[11])
156158
g.gameData.playerAlive[sendingPlayerNumber] = true

internal/lobbyServer/lobby.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ func (s *LobbyServer) publishDiscord(message string, channel string) {
198198
httpRequest, err := retryablehttp.NewRequest(http.MethodPost, channel, bodyJSON)
199199
if err != nil {
200200
s.Logger.Error(err, "could not create request")
201+
return
201202
}
202203
httpRequest.Header.Set("Content-Type", "application/json")
203204
httpRequest.Header.Set("User-Agent", "gopher64Bot (gopher64.github.io, 1)")

0 commit comments

Comments
 (0)