fix race data related to reading bridge state

This commit is contained in:
Tyler Stiene 2021-04-07 01:24:17 -04:00
parent 177553f3a4
commit 480fa533a2
6 changed files with 43 additions and 11 deletions

View File

@ -6,6 +6,9 @@ mumble-discord-bridge: $(GOFILES)
dev: $(GOFILES)
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge
dev-race: $(GOFILES)
go run -race *.go
dev-profile: $(GOFILES)
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge -cpuprofile cpu.prof

View File

@ -35,6 +35,9 @@ type BridgeState struct {
// Wait for bridge to exit cleanly
WaitExit *sync.WaitGroup
// Bridge State Mutex
BridgeMutex sync.Mutex
// Bridge connection
Connected bool
@ -189,7 +192,9 @@ func (b *BridgeState) startBridge() {
}
}()
b.BridgeMutex.Lock()
b.Connected = true
b.BridgeMutex.Unlock()
// Hold until cancelled or external die request
select {
@ -200,7 +205,10 @@ func (b *BridgeState) startBridge() {
cancel()
}
b.BridgeMutex.Lock()
b.Connected = false
b.BridgeMutex.Unlock()
wg.Wait()
log.Println("Terminating Bridge")
b.MumbleUsersMutex.Lock()
@ -221,6 +229,7 @@ func (b *BridgeState) discordStatusUpdate() {
b.DiscordSession.UpdateListeningStatus("an error pinging mumble")
} else {
b.MumbleUsersMutex.Lock()
b.BridgeMutex.Lock()
b.MumbleUserCount = resp.ConnectedUsers
if b.Connected {
b.MumbleUserCount = b.MumbleUserCount - 1
@ -234,6 +243,7 @@ func (b *BridgeState) discordStatusUpdate() {
status = fmt.Sprintf("%v users in Mumble\n", b.MumbleUserCount)
}
}
b.BridgeMutex.Unlock()
b.MumbleUsersMutex.Unlock()
b.DiscordSession.UpdateListeningStatus(status)
}
@ -257,6 +267,7 @@ func (b *BridgeState) AutoBridge() {
b.MumbleUsersMutex.Lock()
b.DiscordUsersMutex.Lock()
b.BridgeMutex.Lock()
if !b.Connected && b.MumbleUserCount > 0 && len(b.DiscordUsers) > 0 {
log.Println("users detected in mumble and discord, bridging")
@ -267,6 +278,7 @@ func (b *BridgeState) AutoBridge() {
b.BridgeDie <- true
}
b.BridgeMutex.Unlock()
b.MumbleUsersMutex.Unlock()
b.DiscordUsersMutex.Unlock()
}

View File

@ -49,11 +49,13 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
l.Bridge.DiscordUsersMutex.Unlock()
// If connected to mumble inform users of Discord users
l.Bridge.BridgeMutex.Lock()
if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText {
l.Bridge.MumbleClient.Do(func() {
l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has joined Discord\n", u.Username), false)
})
}
l.Bridge.BridgeMutex.Unlock()
}
}
@ -85,10 +87,14 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
return
}
l.Bridge.BridgeMutex.Lock()
bridgeConnected := l.Bridge.Connected
l.Bridge.BridgeMutex.Unlock()
if strings.HasPrefix(m.Content, prefix+" link") {
// Look for the message sender in that guild's current voice states.
for _, vs := range g.VoiceStates {
if l.Bridge.Connected {
if bridgeConnected {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Bridge already running, unlink first")
return
}
@ -103,7 +109,7 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
if strings.HasPrefix(m.Content, prefix+" unlink") {
// Look for the message sender in that guild's current voice states.
if !l.Bridge.Connected {
if !bridgeConnected {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Bridge is not currently running")
return
}
@ -118,7 +124,7 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
if strings.HasPrefix(m.Content, prefix+" refresh") {
// Look for the message sender in that guild's current voice states.
if !l.Bridge.Connected {
if !bridgeConnected {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Bridge is not currently running")
return
}
@ -195,11 +201,13 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
seen: true,
dm: dm,
}
l.Bridge.BridgeMutex.Lock()
if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText {
l.Bridge.MumbleClient.Do(func() {
l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has joined Discord\n", u.Username), false)
})
}
l.Bridge.BridgeMutex.Unlock()
} else {
du := l.Bridge.DiscordUsers[vs.UserID]
du.seen = true
@ -213,11 +221,13 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
for id := range l.Bridge.DiscordUsers {
if !l.Bridge.DiscordUsers[id].seen {
log.Println("User left Discord channel " + l.Bridge.DiscordUsers[id].username)
l.Bridge.BridgeMutex.Lock()
if l.Bridge.Connected && !l.Bridge.BridgeConfig.MumbleDisableText {
l.Bridge.MumbleClient.Do(func() {
l.Bridge.MumbleClient.Self.Channel.Send(fmt.Sprintf("%v has left Discord channel\n", l.Bridge.DiscordUsers[id].username), false)
})
}
l.Bridge.BridgeMutex.Unlock()
delete(l.Bridge.DiscordUsers, id)
}
}

View File

@ -23,9 +23,8 @@ type fromDiscord struct {
type DiscordDuplex struct {
Bridge *BridgeState
discordMutex sync.Mutex
discordMixerMutex sync.Mutex
fromDiscordMap map[uint32]fromDiscord
discordMutex sync.Mutex
fromDiscordMap map[uint32]fromDiscord
}
// OnError gets called by dgvoice when an error is encountered.
@ -87,6 +86,7 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
continue
}
dd.Bridge.DiscordVoice.RWMutex.RLock()
if !dd.Bridge.DiscordVoice.Ready || dd.Bridge.DiscordVoice.OpusSend == nil {
if lastReady {
OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
@ -96,13 +96,15 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
})
lastReady = false
}
continue
} else if !lastReady {
fmt.Println("Discordgo ready to send opus packets")
lastReady = true
readyTimeout.Stop()
} else {
dd.Bridge.DiscordVoice.OpusSend <- opus
}
dd.Bridge.DiscordVoice.OpusSend <- opus
dd.Bridge.DiscordVoice.RWMutex.RUnlock()
} else {
if streaming {
dd.Bridge.DiscordVoice.Speaking(false)
@ -123,6 +125,7 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
wg.Add(1)
for {
dd.Bridge.DiscordVoice.RWMutex.RLock()
if !dd.Bridge.DiscordVoice.Ready || dd.Bridge.DiscordVoice.OpusRecv == nil {
if lastReady {
OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
@ -138,6 +141,7 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
lastReady = true
readyTimeout.Stop()
}
dd.Bridge.DiscordVoice.RWMutex.RUnlock()
var ok bool
var p *discordgo.Packet

View File

@ -40,7 +40,7 @@ func main() {
mumblePassword := flag.String("mumble-password", lookupEnvOrString("MUMBLE_PASSWORD", ""), "MUMBLE_PASSWORD, mumble password, optional")
mumbleInsecure := flag.Bool("mumble-insecure", lookupEnvOrBool("MUMBLE_INSECURE", false), " MUMBLE_INSECURE, mumble insecure, optional")
mumbleCertificate := flag.String("mumble-certificate", lookupEnvOrString("MUMBLE_CERTIFICATE", ""), "MUMBLE_CERTIFICATE, client certificate to use when connecting to the Mumble server")
mumbleChannel := flag.String("mumble-channel", lookupEnvOrString("MUMBLE_CHANNEL", ""), "MUMBLE_CHANNEL, mumble channel to start in, using '/' to seperate nested channels, optional")
mumbleChannel := flag.String("mumble-channel", lookupEnvOrString("MUMBLE_CHANNEL", ""), "MUMBLE_CHANNEL, mumble channel to start in, using '/' to separate nested channels, optional")
mumbleDisableText := flag.Bool("mumble-disable-text", lookupEnvOrBool("MUMBLE_DISABLE_TEXT", false), "MUMBLE_DISABLE_TEXT, disable sending text to mumble, (default false)")
discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token, required")
discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid, required")
@ -197,9 +197,11 @@ func main() {
log.Println("OS Signal. Bot shutting down")
// Wait or the bridge to exit cleanly
Bridge.BridgeMutex.Lock()
if Bridge.Connected {
//TODO BridgeDie occasionally panics on send to closed channel
Bridge.BridgeDie <- true
Bridge.WaitExit.Wait()
}
Bridge.BridgeMutex.Unlock()
}

View File

@ -43,22 +43,23 @@ func (l *MumbleListener) mumbleUserChange(e *gumble.UserChangeEvent) {
e.User.Send("Mumble-Discord-Bridge v" + version)
// Tell the user who is connected to discord
l.Bridge.DiscordUsersMutex.Lock()
if len(l.Bridge.DiscordUsers) == 0 {
e.User.Send("No users connected to Discord")
} else {
s := "Connected to Discord: "
arr := []string{}
l.Bridge.DiscordUsersMutex.Lock()
for u := range l.Bridge.DiscordUsers {
arr = append(arr, l.Bridge.DiscordUsers[u].username)
}
s = s + strings.Join(arr[:], ",")
l.Bridge.DiscordUsersMutex.Unlock()
e.User.Send(s)
}
l.Bridge.DiscordUsersMutex.Unlock()
}
// Send discord a notice