Fix all found race conditions, and dont send on nil channel (#307)
This commit is contained in:
parent
53842f8dfc
commit
bdb31f245d
2 changed files with 49 additions and 7 deletions
38
voice.go
38
voice.go
|
@ -100,11 +100,15 @@ func (v *VoiceConnection) Speaking(b bool) (err error) {
|
||||||
v.wsMutex.Lock()
|
v.wsMutex.Lock()
|
||||||
err = v.wsConn.WriteJSON(data)
|
err = v.wsConn.WriteJSON(data)
|
||||||
v.wsMutex.Unlock()
|
v.wsMutex.Unlock()
|
||||||
|
|
||||||
|
v.Lock()
|
||||||
|
defer v.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.speaking = false
|
v.speaking = false
|
||||||
log.Println("Speaking() write json error:", err)
|
log.Println("Speaking() write json error:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v.speaking = b
|
v.speaking = b
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -139,9 +143,9 @@ func (v *VoiceConnection) Disconnect() (err error) {
|
||||||
// Send a OP4 with a nil channel to disconnect
|
// Send a OP4 with a nil channel to disconnect
|
||||||
if v.sessionID != "" {
|
if v.sessionID != "" {
|
||||||
data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}}
|
data := voiceChannelJoinOp{4, voiceChannelJoinData{&v.GuildID, nil, true, true}}
|
||||||
v.wsMutex.Lock()
|
v.session.wsMutex.Lock()
|
||||||
err = v.session.wsConn.WriteJSON(data)
|
err = v.session.wsConn.WriteJSON(data)
|
||||||
v.wsMutex.Unlock()
|
v.session.wsMutex.Unlock()
|
||||||
v.sessionID = ""
|
v.sessionID = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +153,10 @@ func (v *VoiceConnection) Disconnect() (err error) {
|
||||||
v.Close()
|
v.Close()
|
||||||
|
|
||||||
v.log(LogInformational, "Deleting VoiceConnection %s", v.GuildID)
|
v.log(LogInformational, "Deleting VoiceConnection %s", v.GuildID)
|
||||||
|
|
||||||
|
v.session.Lock()
|
||||||
delete(v.session.VoiceConnections, v.GuildID)
|
delete(v.session.VoiceConnections, v.GuildID)
|
||||||
|
v.session.Unlock()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -185,7 +192,9 @@ func (v *VoiceConnection) Close() {
|
||||||
|
|
||||||
// To cleanly close a connection, a client should send a close
|
// To cleanly close a connection, a client should send a close
|
||||||
// frame and wait for the server to close the connection.
|
// frame and wait for the server to close the connection.
|
||||||
|
v.wsMutex.Lock()
|
||||||
err := v.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
err := v.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||||
|
v.wsMutex.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.log(LogError, "error closing websocket, %s", err)
|
v.log(LogError, "error closing websocket, %s", err)
|
||||||
}
|
}
|
||||||
|
@ -246,7 +255,10 @@ func (v *VoiceConnection) waitUntilConnected() error {
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
for {
|
for {
|
||||||
if v.Ready {
|
v.RLock()
|
||||||
|
ready := v.Ready
|
||||||
|
v.RUnlock()
|
||||||
|
if ready {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,8 +421,6 @@ func (v *VoiceConnection) onEvent(message []byte) {
|
||||||
go v.opusReceiver(v.udpConn, v.close, v.OpusRecv)
|
go v.opusReceiver(v.udpConn, v.close, v.OpusRecv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the ready event
|
|
||||||
v.connected <- true
|
|
||||||
return
|
return
|
||||||
|
|
||||||
case 3: // HEARTBEAT response
|
case 3: // HEARTBEAT response
|
||||||
|
@ -418,6 +428,9 @@ func (v *VoiceConnection) onEvent(message []byte) {
|
||||||
return
|
return
|
||||||
|
|
||||||
case 4: // udp encryption secret key
|
case 4: // udp encryption secret key
|
||||||
|
v.Lock()
|
||||||
|
defer v.Unlock()
|
||||||
|
|
||||||
v.op4 = voiceOP4{}
|
v.op4 = voiceOP4{}
|
||||||
if err := json.Unmarshal(e.RawData, &v.op4); err != nil {
|
if err := json.Unmarshal(e.RawData, &v.op4); err != nil {
|
||||||
v.log(LogError, "OP4 unmarshall error, %s, %s", err, string(e.RawData))
|
v.log(LogError, "OP4 unmarshall error, %s, %s", err, string(e.RawData))
|
||||||
|
@ -648,8 +661,14 @@ func (v *VoiceConnection) opusSender(udpConn *net.UDPConn, close <-chan struct{}
|
||||||
|
|
||||||
// VoiceConnection is now ready to receive audio packets
|
// VoiceConnection is now ready to receive audio packets
|
||||||
// TODO: this needs reviewed as I think there must be a better way.
|
// TODO: this needs reviewed as I think there must be a better way.
|
||||||
|
v.Lock()
|
||||||
v.Ready = true
|
v.Ready = true
|
||||||
defer func() { v.Ready = false }()
|
v.Unlock()
|
||||||
|
defer func() {
|
||||||
|
v.Lock()
|
||||||
|
v.Ready = false
|
||||||
|
v.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
var sequence uint16
|
var sequence uint16
|
||||||
var timestamp uint32
|
var timestamp uint32
|
||||||
|
@ -678,7 +697,10 @@ func (v *VoiceConnection) opusSender(udpConn *net.UDPConn, close <-chan struct{}
|
||||||
// else, continue loop
|
// else, continue loop
|
||||||
}
|
}
|
||||||
|
|
||||||
if !v.speaking {
|
v.RLock()
|
||||||
|
speaking := v.speaking
|
||||||
|
v.RUnlock()
|
||||||
|
if !speaking {
|
||||||
err := v.Speaking(true)
|
err := v.Speaking(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.log(LogError, "error sending speaking packet, %s", err)
|
v.log(LogError, "error sending speaking packet, %s", err)
|
||||||
|
@ -691,7 +713,9 @@ func (v *VoiceConnection) opusSender(udpConn *net.UDPConn, close <-chan struct{}
|
||||||
|
|
||||||
// encrypt the opus data
|
// encrypt the opus data
|
||||||
copy(nonce[:], udpHeader)
|
copy(nonce[:], udpHeader)
|
||||||
|
v.RLock()
|
||||||
sendbuf := secretbox.Seal(udpHeader, recvbuf, &nonce, &v.op4.SecretKey)
|
sendbuf := secretbox.Seal(udpHeader, recvbuf, &nonce, &v.op4.SecretKey)
|
||||||
|
v.RUnlock()
|
||||||
|
|
||||||
// block here until we're exactly at the right time :)
|
// block here until we're exactly at the right time :)
|
||||||
// Then send rtp audio packet to Discord over UDP
|
// Then send rtp audio packet to Discord over UDP
|
||||||
|
|
18
wsapi.go
18
wsapi.go
|
@ -475,18 +475,24 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
|
||||||
|
|
||||||
s.log(LogInformational, "called")
|
s.log(LogInformational, "called")
|
||||||
|
|
||||||
|
s.RLock()
|
||||||
voice, _ = s.VoiceConnections[gID]
|
voice, _ = s.VoiceConnections[gID]
|
||||||
|
s.RUnlock()
|
||||||
|
|
||||||
if voice == nil {
|
if voice == nil {
|
||||||
voice = &VoiceConnection{}
|
voice = &VoiceConnection{}
|
||||||
|
s.Lock()
|
||||||
s.VoiceConnections[gID] = voice
|
s.VoiceConnections[gID] = voice
|
||||||
|
s.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
voice.Lock()
|
||||||
voice.GuildID = gID
|
voice.GuildID = gID
|
||||||
voice.ChannelID = cID
|
voice.ChannelID = cID
|
||||||
voice.deaf = deaf
|
voice.deaf = deaf
|
||||||
voice.mute = mute
|
voice.mute = mute
|
||||||
voice.session = s
|
voice.session = s
|
||||||
|
voice.Unlock()
|
||||||
|
|
||||||
// Send the request to Discord that we want to join the voice channel
|
// Send the request to Discord that we want to join the voice channel
|
||||||
data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}}
|
data := voiceChannelJoinOp{4, voiceChannelJoinData{&gID, &cID, mute, deaf}}
|
||||||
|
@ -517,7 +523,9 @@ func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have a voice connection to update
|
// Check if we have a voice connection to update
|
||||||
|
s.RLock()
|
||||||
voice, exists := s.VoiceConnections[st.GuildID]
|
voice, exists := s.VoiceConnections[st.GuildID]
|
||||||
|
s.RUnlock()
|
||||||
if !exists {
|
if !exists {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -528,8 +536,10 @@ func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the SessionID for later use.
|
// Store the SessionID for later use.
|
||||||
|
voice.Lock()
|
||||||
voice.UserID = st.UserID
|
voice.UserID = st.UserID
|
||||||
voice.sessionID = st.SessionID
|
voice.sessionID = st.SessionID
|
||||||
|
voice.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// onVoiceServerUpdate handles the Voice Server Update data websocket event.
|
// onVoiceServerUpdate handles the Voice Server Update data websocket event.
|
||||||
|
@ -541,7 +551,9 @@ func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
|
||||||
|
|
||||||
s.log(LogInformational, "called")
|
s.log(LogInformational, "called")
|
||||||
|
|
||||||
|
s.RLock()
|
||||||
voice, exists := s.VoiceConnections[st.GuildID]
|
voice, exists := s.VoiceConnections[st.GuildID]
|
||||||
|
s.RUnlock()
|
||||||
|
|
||||||
// If no VoiceConnection exists, just skip this
|
// If no VoiceConnection exists, just skip this
|
||||||
if !exists {
|
if !exists {
|
||||||
|
@ -553,9 +565,11 @@ func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
|
||||||
voice.Close()
|
voice.Close()
|
||||||
|
|
||||||
// Store values for later use
|
// Store values for later use
|
||||||
|
voice.Lock()
|
||||||
voice.token = st.Token
|
voice.token = st.Token
|
||||||
voice.endpoint = st.Endpoint
|
voice.endpoint = st.Endpoint
|
||||||
voice.GuildID = st.GuildID
|
voice.GuildID = st.GuildID
|
||||||
|
voice.Unlock()
|
||||||
|
|
||||||
// Open a conenction to the voice server
|
// Open a conenction to the voice server
|
||||||
err := voice.open()
|
err := voice.open()
|
||||||
|
@ -645,6 +659,8 @@ func (s *Session) reconnect() {
|
||||||
// However, there seems to be cases where something "weird"
|
// However, there seems to be cases where something "weird"
|
||||||
// happens. So we're doing this for now just to improve
|
// happens. So we're doing this for now just to improve
|
||||||
// stability in those edge cases.
|
// stability in those edge cases.
|
||||||
|
s.RLock()
|
||||||
|
defer s.RUnlock()
|
||||||
for _, v := range s.VoiceConnections {
|
for _, v := range s.VoiceConnections {
|
||||||
|
|
||||||
s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID)
|
s.log(LogInformational, "reconnecting voice connection to guild %s", v.GuildID)
|
||||||
|
@ -692,7 +708,9 @@ func (s *Session) Close() (err error) {
|
||||||
s.log(LogInformational, "sending close frame")
|
s.log(LogInformational, "sending close frame")
|
||||||
// To cleanly close a connection, a client should send a close
|
// To cleanly close a connection, a client should send a close
|
||||||
// frame and wait for the server to close the connection.
|
// frame and wait for the server to close the connection.
|
||||||
|
s.wsMutex.Lock()
|
||||||
err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||||
|
s.wsMutex.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log(LogInformational, "error closing websocket, %s", err)
|
s.log(LogInformational, "error closing websocket, %s", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue