fix(channelserver): eliminate data races in shutdown and session lifecycle

The channel server had several concurrency issues found by the race
detector during isolation testing:

- acceptClients could send on a closed acceptConns channel during
  shutdown, causing a panic. Replace close(acceptConns) with a done
  channel and select-based shutdown signaling in both acceptClients
  and manageSessions.
- invalidateSessions read isShuttingDown and iterated sessions without
  holding the lock. Rewrite with ticker + done channel select and
  snapshot sessions under lock before processing timeouts.
- sendLoop/recvLoop accessed global _config.ErupeConfig.LoopDelay
  which races with tests modifying the global. Use the per-server
  erupeConfig instead.
- logoutPlayer panicked on DB errors and crashed on nil DB (no-db
  test scenarios). Guard with nil check and log errors instead.
- Shutdown was not idempotent, double-calling caused double-close
  panic on done channel.

Add 5 channel isolation tests verifying independent shutdown,
listener failure, session panic recovery, cross-channel registry
after shutdown, and stage isolation.
This commit is contained in:
Houmgaor
2026-02-20 14:36:37 +01:00
parent 486be65a38
commit eab7d1fc4f
4 changed files with 260 additions and 30 deletions

View File

@@ -293,14 +293,16 @@ func logoutPlayer(s *Session) {
}
// Update sign sessions and server player count
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
if err != nil {
panic(err)
}
if s.server.db != nil {
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
if err != nil {
s.logger.Error("Failed to clear sign session", zap.Error(err))
}
_, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
if err != nil {
panic(err)
_, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
if err != nil {
s.logger.Error("Failed to update player count", zap.Error(err))
}
}
if s.stage == nil {