mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-25 09:03:10 +01:00
fix(channelserver): consolidate stages map locking to prevent data race
The stages map was protected by two incompatible locks: the embedded Server.Mutex and Server.stagesLock (RWMutex). Since these are separate mutexes they don't exclude each other, and many handlers accessed the map with no lock at all. Route all stages map access through stagesLock: read-only lookups use RLock, writes (create/delete) use Lock. Per-stage field mutations continue to use each stage's own RWMutex. Restructure handleMsgSysUnlockStage to avoid holding stagesLock nested inside a stage RLock, preventing potential deadlock with destructEmptyStages.
This commit is contained in:
@@ -292,11 +292,20 @@ func logoutPlayer(s *Session) {
|
||||
_ = s.rawConn.Close()
|
||||
s.server.Unlock()
|
||||
|
||||
// Stage cleanup
|
||||
// Stage cleanup — snapshot sessions first under server mutex, then iterate stages under stagesLock
|
||||
s.server.Lock()
|
||||
sessionSnapshot := make([]*Session, 0, len(s.server.sessions))
|
||||
for _, sess := range s.server.sessions {
|
||||
sessionSnapshot = append(sessionSnapshot, sess)
|
||||
}
|
||||
s.server.Unlock()
|
||||
|
||||
s.server.stagesLock.RLock()
|
||||
for _, stage := range s.server.stages {
|
||||
// Tell sessions registered to disconnecting players quest to unregister
|
||||
stage.Lock()
|
||||
// Tell sessions registered to disconnecting player's quest to unregister
|
||||
if stage.host != nil && stage.host.charID == s.charID {
|
||||
for _, sess := range s.server.sessions {
|
||||
for _, sess := range sessionSnapshot {
|
||||
for rSlot := range stage.reservedClientSlots {
|
||||
if sess.charID == rSlot && sess.stage != nil && sess.stage.id[3:5] != "Qs" {
|
||||
sess.QueueSendMHFNonBlocking(&mhfpacket.MsgSysStageDestruct{})
|
||||
@@ -309,7 +318,9 @@ func logoutPlayer(s *Session) {
|
||||
delete(stage.clients, session)
|
||||
}
|
||||
}
|
||||
stage.Unlock()
|
||||
}
|
||||
s.server.stagesLock.RUnlock()
|
||||
|
||||
// Update sign sessions and server player count
|
||||
if s.server.db != nil {
|
||||
@@ -339,11 +350,13 @@ func logoutPlayer(s *Session) {
|
||||
CharID: s.charID,
|
||||
}, s)
|
||||
|
||||
s.server.Lock()
|
||||
s.server.stagesLock.RLock()
|
||||
for _, stage := range s.server.stages {
|
||||
stage.Lock()
|
||||
delete(stage.reservedClientSlots, s.charID)
|
||||
stage.Unlock()
|
||||
}
|
||||
s.server.Unlock()
|
||||
s.server.stagesLock.RUnlock()
|
||||
|
||||
removeSessionFromSemaphore(s)
|
||||
removeSessionFromStage(s)
|
||||
|
||||
Reference in New Issue
Block a user