Fix all found race conditions, and dont send on nil channel (#307)

This commit is contained in:
jonas747 2017-01-11 05:01:47 +01:00 committed by Chris Rhodes
parent 53842f8dfc
commit bdb31f245d
2 changed files with 49 additions and 7 deletions

View file

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

View file

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