Read the IRC client's nick under its lock in the relay
check / check (push) Successful in 3m4s

After registration each IRC connection runs a second goroutine that sends
queued messages to the client, and it read c.nick without holding c.mu.
The goroutine that reads client commands changes c.nick under c.mu when
the client sends NICK, so the race detector failed
TestIntegrationTwoClients. Every read of c.nick in relay.go now takes
c.mu. No other field that goroutine reads changes after it starts.

Model: opus-5-5
This commit is contained in:
clawbot
2026-09-29 10:48:48 +00:00
parent 6a514b565c
commit 6a200ea639
+40 -8
View File
@@ -128,7 +128,11 @@ func (c *Conn) deliverIRCMessage(
default: default:
// Unknown command — deliver as server notice. // Unknown command — deliver as server notice.
if text != "" { if text != "" {
c.sendFromServer("NOTICE", c.nick, text) c.mu.Lock()
nick := c.nick
c.mu.Unlock()
c.sendFromServer("NOTICE", nick, text)
} }
} }
} }
@@ -158,8 +162,12 @@ func (c *Conn) deliverNumeric(
_ = json.Unmarshal(msg.Params, &params) _ = json.Unmarshal(msg.Params, &params)
} }
c.mu.Lock()
nick := c.nick
c.mu.Unlock()
allParams := make([]string, 0, 1+len(params)+1) allParams := make([]string, 0, 1+len(params)+1)
allParams = append(allParams, c.nick) allParams = append(allParams, nick)
allParams = append(allParams, params...) allParams = append(allParams, params...)
if text != "" { if text != "" {
@@ -177,8 +185,12 @@ func (c *Conn) deliverTextMessage(
from := msg.From from := msg.From
target := msg.To target := msg.To
c.mu.Lock()
nick := c.nick
c.mu.Unlock()
// Don't echo our own messages back. // Don't echo our own messages back.
if strings.EqualFold(from, c.nick) { if strings.EqualFold(from, nick) {
return return
} }
@@ -192,9 +204,13 @@ func (c *Conn) deliverTextMessage(
// deliverJoin sends a JOIN notification. // deliverJoin sends a JOIN notification.
func (c *Conn) deliverJoin(msg *db.IRCMessage) { func (c *Conn) deliverJoin(msg *db.IRCMessage) {
c.mu.Lock()
nick := c.nick
c.mu.Unlock()
// Don't echo our own JOINs (we already sent them // Don't echo our own JOINs (we already sent them
// during joinChannel). // during joinChannel).
if strings.EqualFold(msg.From, c.nick) { if strings.EqualFold(msg.From, nick) {
return return
} }
@@ -206,7 +222,11 @@ func (c *Conn) deliverJoin(msg *db.IRCMessage) {
// deliverPart sends a PART notification. // deliverPart sends a PART notification.
func (c *Conn) deliverPart(msg *db.IRCMessage, text string) { func (c *Conn) deliverPart(msg *db.IRCMessage, text string) {
if strings.EqualFold(msg.From, c.nick) { c.mu.Lock()
nick := c.nick
c.mu.Unlock()
if strings.EqualFold(msg.From, nick) {
return return
} }
@@ -227,7 +247,11 @@ func (c *Conn) deliverNickChange(
msg *db.IRCMessage, msg *db.IRCMessage,
newNick string, newNick string,
) { ) {
if strings.EqualFold(msg.From, c.nick) { c.mu.Lock()
nick := c.nick
c.mu.Unlock()
if strings.EqualFold(msg.From, nick) {
return return
} }
@@ -241,7 +265,11 @@ func (c *Conn) deliverQuitMsg(
msg *db.IRCMessage, msg *db.IRCMessage,
text string, text string,
) { ) {
if strings.EqualFold(msg.From, c.nick) { c.mu.Lock()
nick := c.nick
c.mu.Unlock()
if strings.EqualFold(msg.From, nick) {
return return
} }
@@ -302,7 +330,11 @@ func (c *Conn) deliverInviteMsg(
_ *db.IRCMessage, _ *db.IRCMessage,
text string, text string,
) { ) {
c.sendFromServer("NOTICE", c.nick, text) c.mu.Lock()
nick := c.nick
c.mu.Unlock()
c.sendFromServer("NOTICE", nick, text)
} }
// deliverMode sends a MODE change notification. // deliverMode sends a MODE change notification.