diff --git a/irc/client.go b/irc/client.go index ad3009c0..3f4c1e8b 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1124,14 +1124,21 @@ func (client *Client) SetVHost(vhost string) (updated bool) { return } -// updateNick updates `nick` and `nickCasefolded`. -func (client *Client) updateNick(nick, nickCasefolded, skeleton string) { +// SetNick gives the client a nickname and marks it as registered, if necessary +func (client *Client) SetNick(nick, nickCasefolded, skeleton string) (success bool) { client.stateMutex.Lock() defer client.stateMutex.Unlock() + if client.destroyed { + return false + } else if !client.registered { + // XXX test this before setting it to avoid annoying the race detector + client.registered = true + } client.nick = nick client.nickCasefolded = nickCasefolded client.skeleton = skeleton client.updateNickMaskNoMutex() + return true } // updateNickMaskNoMutex updates the casefolded nickname and nickmask, not acquiring any mutexes. diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index 45737b90..1f4fbe82 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -243,10 +243,12 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick return "", errNicknameInUse, false } + if changeSuccess := client.SetNick(newNick, newCfNick, newSkeleton); !changeSuccess { + return "", errClientDestroyed, false + } clients.removeInternal(client) clients.byNick[newCfNick] = client clients.bySkeleton[newSkeleton] = client - client.updateNick(newNick, newCfNick, newSkeleton) return newNick, nil, false } diff --git a/irc/getters.go b/irc/getters.go index 6658ff2d..ec4dc8c8 100644 --- a/irc/getters.go +++ b/irc/getters.go @@ -235,20 +235,15 @@ func (client *Client) Oper() *Oper { return client.oper } -func (client *Client) Registered() bool { - client.stateMutex.RLock() - defer client.stateMutex.RUnlock() - return client.registered -} - -func (client *Client) SetRegistered() { +func (client *Client) Registered() (result bool) { // `registered` is only written from the client's own goroutine, but may be // read from other goroutines; therefore, the client's own goroutine may read // the value without synchronization, but must write it with synchronization, // and other goroutines must read it with synchronization - client.stateMutex.Lock() - client.registered = true - client.stateMutex.Unlock() + client.stateMutex.RLock() + result = client.registered + client.stateMutex.RUnlock() + return } func (client *Client) RawHostname() (result string) { diff --git a/irc/nickname.go b/irc/nickname.go index 7bf80e3d..c44d7635 100644 --- a/irc/nickname.go +++ b/irc/nickname.go @@ -91,13 +91,11 @@ func performNickChange(server *Server, client *Client, target *Client, session * channel.AddHistoryItem(histItem, details.account) } - if target.Registered() { - newCfnick := target.NickCasefolded() - if newCfnick != details.nickCasefolded { - client.server.monitorManager.AlertAbout(details.nick, details.nickCasefolded, false) - client.server.monitorManager.AlertAbout(assignedNickname, newCfnick, true) - } - } // else: these will be deferred to the end of registration (see #572) + newCfnick := target.NickCasefolded() + if newCfnick != details.nickCasefolded { + client.server.monitorManager.AlertAbout(details.nick, details.nickCasefolded, false) + client.server.monitorManager.AlertAbout(assignedNickname, newCfnick, true) + } return nil } diff --git a/irc/server.go b/irc/server.go index d12460da..40368fe2 100644 --- a/irc/server.go +++ b/irc/server.go @@ -283,12 +283,8 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) { c.SetMode(defaultMode, true) } - // registration has succeeded: - c.SetRegistered() - // count new user in statistics server.stats.Register(c.HasMode(modes.Invisible)) - server.monitorManager.AlertAbout(c.Nick(), c.NickCasefolded(), true) server.playRegistrationBurst(session) return false