Fix race condition

This commit is contained in:
kayos@tcp.direct 2023-03-17 23:57:45 -07:00
parent 633a5dea16
commit 568e9652da
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
7 changed files with 58 additions and 24 deletions

View File

@ -177,7 +177,7 @@ func handleJOIN(c *Client, e Event) {
defer c.state.notify(c, UPDATE_STATE)
channel.addUser(user.Nick, user)
channel.addUser(user.Nick.Load().(string), user)
user.addChannel(channel.Name, channel)
// Assume extended-join (ircv3).
@ -345,8 +345,17 @@ func handleWHO(c *Client, e Event) {
}
user.Host = host
user.Ident = ident
user.Mask = user.Nick + "!" + user.Ident + "@" + user.Host
user.Ident.Store(ident)
str := strs.Get()
str.MustWriteString(user.Nick.Load().(string))
str.MustWriteString("!")
str.MustWriteString(user.Ident.Load().(string))
str.MustWriteString("@")
str.MustWriteString(user.Host)
user.Mask.Store(str.String())
strs.MustPut(str)
user.Extras.Name = realname
if account != "0" {

2
cap.go
View File

@ -307,7 +307,7 @@ func handleCHGHOST(c *Client, e Event) {
user := c.state.lookupUser(e.Source.Name)
if user != nil {
user.Ident = e.Params[0]
user.Ident.Store(e.Params[0])
user.Host = e.Params[1]
}

View File

@ -646,7 +646,7 @@ func (c *Client) UserList() []string {
if usr.Stale {
continue
}
users = append(users, usr.Nick)
users = append(users, usr.Nick.Load().(string))
}
sort.Strings(users)
@ -665,7 +665,7 @@ func (c *Client) Users() []*User {
}
sort.Slice(users, func(i, j int) bool {
return users[i].Nick < users[j].Nick
return users[i].Nick.Load().(string) < users[j].Nick.Load().(string)
})
return users
}

View File

@ -225,11 +225,7 @@ func (c *Caller) exec(command string, bg bool, client *Client, event *Event) {
if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
continue
}
hi, _ := hmap.Get(cuid)
hndlr, ok := hi.(Handler)
if !ok {
panic("improper handler type in map")
}
hndlr, _ := hmap.Get(cuid)
stack = append(stack, execStack{hndlr, cuid})
}
}

View File

@ -92,11 +92,11 @@ func (s *state) reset(initial bool) {
// User represents an IRC user and the state attached to them.
type User struct {
// Nick is the users current nickname. rfc1459 compliant.
Nick string `json:"nick"`
Nick *MarshalableAtomicValue `json:"nick"`
// Ident is the users username/ident. Ident is commonly prefixed with a
// "~", which indicates that they do not have a identd server setup for
// authentication.
Ident string `json:"ident"`
Ident *MarshalableAtomicValue `json:"ident"`
// Host is the visible host of the users connection that the server has
// provided to us for their connection. May not always be accurate due to
// many networks spoofing/hiding parts of the hostname for privacy
@ -104,7 +104,7 @@ type User struct {
Host string `json:"host"`
// Mask is the combined Nick!Ident@Host of the given user.
Mask string `json:"mask"`
Mask *MarshalableAtomicValue `json:"mask"`
// Network is the name of the IRC network where this user was found.
// This has been added for the purposes of girc being used in multi-client scenarios with data persistence.
@ -366,7 +366,7 @@ func (ch *Channel) Copy() *Channel {
*nc = *ch
for v := range ch.UserList.IterBuffered() {
nc.UserList.Set(v.Val.Nick, v.Val)
nc.UserList.Set(v.Val.Nick.Load().(string), v.Val)
}
// And modes.
@ -466,10 +466,10 @@ func (s *state) createUser(src *Source) (u *User, ok bool) {
mask.MustWriteString(src.Host)
u = &User{
Nick: src.Name,
Nick: NewAtomicString(src.Name),
Host: src.Host,
Ident: src.Ident,
Mask: mask.String(),
Ident: NewAtomicString(src.Ident),
Mask: NewAtomicString(mask.String()),
ChannelList: cmap.New[*Channel](),
FirstSeen: time.Now(),
LastActive: time.Now(),
@ -532,7 +532,7 @@ func (s *state) renameUser(from, to string) {
user = old
}
user.Nick = to
user.Nick.Store(to)
user.LastActive = time.Now()
s.users.Set(ToRFC1459(to), user)

View File

@ -123,7 +123,7 @@ func TestState(t *testing.T) {
fullUsers := c.Users()
for i := 0; i < len(fullUsers); i++ {
if fullUsers[i].Nick != users[i] {
if fullUsers[i].Nick.Load().(string) != users[i] {
t.Errorf("fullUsers nick doesn't map to same nick in UsersList: %q :: %#v", fullUsers[i].Nick, users)
return
}
@ -138,12 +138,12 @@ func TestState(t *testing.T) {
adm := ch.Admins(c)
var admList []string
for i := 0; i < len(adm); i++ {
admList = append(admList, adm[i].Nick)
admList = append(admList, adm[i].Nick.Load().(string))
}
trusted := ch.Trusted(c)
var trustedList []string
for i := 0; i < len(trusted); i++ {
trustedList = append(trustedList, trusted[i].Nick)
trustedList = append(trustedList, trusted[i].Nick.Load().(string))
}
if !reflect.DeepEqual(admList, []string{"nick2"}) {
@ -213,7 +213,7 @@ func TestState(t *testing.T) {
return
}
if user.Nick != "fhjones" {
if user.Nick.Load().(string) != "fhjones" {
t.Errorf("User.Nick == %q, wanted \"nick\"", user.Nick)
return
}
@ -228,7 +228,7 @@ func TestState(t *testing.T) {
return
}
if user.Ident != "~user" {
if user.Ident.Load().(string) != "~user" {
t.Errorf("User.Ident == %q, wanted \"~user\"", user.Ident)
return
}

29
value.go Normal file
View File

@ -0,0 +1,29 @@
package girc
import (
"fmt"
"sync/atomic"
)
type MarshalableAtomicValue struct {
*atomic.Value
}
func (m *MarshalableAtomicValue) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%v", m.Value.Load())), nil
}
func (m *MarshalableAtomicValue) UnmarshalJSON(b []byte) error {
m.Value.Store(string(b))
return nil
}
func (m *MarshalableAtomicValue) String() string {
return m.Value.Load().(string)
}
func NewAtomicString(s string) *MarshalableAtomicValue {
obj := &atomic.Value{}
obj.Store(s)
return &MarshalableAtomicValue{Value: obj}
}