1
0
Fork 0
forked from External/ergo

tweak member caching

This commit is contained in:
Shivaram Lingamneni 2023-04-02 06:52:33 -04:00
parent 780116b0c2
commit a6664459f6
2 changed files with 31 additions and 25 deletions

View file

@ -54,8 +54,8 @@ type Channel struct {
settings ChannelSettings settings ChannelSettings
uuid utils.UUID uuid utils.UUID
// these caches are paired to allow iteration over channel members without holding the lock // these caches are paired to allow iteration over channel members without holding the lock
membersCache []*Client membersCache []*Client
memberModesCache []*modes.ModeSet memberDataCache []*memberData
} }
// NewChannel creates a new channel from a `Server` and a `name` // NewChannel creates a new channel from a `Server` and a `name`
@ -424,18 +424,18 @@ func (channel *Channel) AcceptTransfer(client *Client) (err error) {
func (channel *Channel) regenerateMembersCache() { func (channel *Channel) regenerateMembersCache() {
channel.stateMutex.RLock() channel.stateMutex.RLock()
membersCache := make([]*Client, len(channel.members)) membersCache := make([]*Client, len(channel.members))
modesCache := make([]*modes.ModeSet, len(channel.members)) dataCache := make([]*memberData, len(channel.members))
i := 0 i := 0
for client, info := range channel.members { for client, info := range channel.members {
membersCache[i] = client membersCache[i] = client
modesCache[i] = info.modes dataCache[i] = info
i++ i++
} }
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
channel.stateMutex.Lock() channel.stateMutex.Lock()
channel.membersCache = membersCache channel.membersCache = membersCache
channel.memberModesCache = modesCache channel.memberDataCache = dataCache
channel.stateMutex.Unlock() channel.stateMutex.Unlock()
} }
@ -444,7 +444,7 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
channel.stateMutex.RLock() channel.stateMutex.RLock()
clientData, isJoined := channel.members[client] clientData, isJoined := channel.members[client]
chname := channel.name chname := channel.name
membersCache, memberModesCache := channel.membersCache, channel.memberModesCache membersCache, memberDataCache := channel.membersCache, channel.memberDataCache
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
isOper := client.HasRoleCapabs("sajoin") isOper := client.HasRoleCapabs("sajoin")
respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper && respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper &&
@ -457,27 +457,27 @@ func (channel *Channel) Names(client *Client, rb *ResponseBuffer) {
tl.Initialize(maxNamLen, " ") tl.Initialize(maxNamLen, " ")
if isJoined || !channel.flags.HasMode(modes.Secret) || isOper { if isJoined || !channel.flags.HasMode(modes.Secret) || isOper {
for i, target := range membersCache { for i, target := range membersCache {
if !isJoined && target.HasMode(modes.Invisible) && !isOper {
continue
}
var nick string var nick string
if isUserhostInNames { if isUserhostInNames {
nick = target.NickMaskString() nick = target.NickMaskString()
} else { } else {
nick = target.Nick() nick = target.Nick()
} }
modeSet := memberModesCache[i] memberData := memberDataCache[i]
if !isJoined && target.HasMode(modes.Invisible) && !isOper { if respectAuditorium && memberData.modes.HighestChannelUserMode() == modes.Mode(0) {
continue continue
} }
if respectAuditorium && modeSet.HighestChannelUserMode() == modes.Mode(0) { tl.AddParts(memberData.modes.Prefixes(isMultiPrefix), nick)
continue
}
tl.AddParts(modeSet.Prefixes(isMultiPrefix), nick)
} }
} }
for _, line := range tl.Lines() { for _, line := range tl.Lines() {
rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", chname, line) rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", chname, line)
} }
rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list")) rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, chname, client.t("End of NAMES list"))
} }
// does `clientMode` give you privileges to grant/remove `targetMode` to/from people, // does `clientMode` give you privileges to grant/remove `targetMode` to/from people,
@ -501,7 +501,7 @@ func channelUserModeHasPrivsOver(clientMode modes.Mode, targetMode modes.Mode) b
// ClientIsAtLeast returns whether the client has at least the given channel privilege. // ClientIsAtLeast returns whether the client has at least the given channel privilege.
func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) bool { func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) bool {
channel.stateMutex.RLock() channel.stateMutex.RLock()
memberData := channel.members[client] memberData, present := channel.members[client]
founder := channel.registeredFounder founder := channel.registeredFounder
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
@ -509,6 +509,10 @@ func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) b
return true return true
} }
if !present {
return false
}
for _, mode := range modes.ChannelUserModes { for _, mode := range modes.ChannelUserModes {
if memberData.modes.HasMode(mode) { if memberData.modes.HasMode(mode) {
return true return true
@ -564,15 +568,14 @@ func (channel *Channel) setMemberStatus(client *Client, status alwaysOnChannelSt
mData.modes.SetMode(modes.Mode(mode), true) mData.modes.SetMode(modes.Mode(mode), true)
} }
mData.joinTime = status.JoinTime mData.joinTime = status.JoinTime
channel.members[client] = mData
} }
} }
func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool { func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
channel.stateMutex.RLock() channel.stateMutex.RLock()
founder := channel.registeredFounder founder := channel.registeredFounder
clientModes := channel.members[client].modes clientData, clientOK := channel.members[client]
targetModes := channel.members[target].modes targetData, targetOK := channel.members[target]
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
if founder != "" { if founder != "" {
@ -583,7 +586,11 @@ func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool
} }
} }
return channelUserModeHasPrivsOver(clientModes.HighestChannelUserMode(), targetModes.HighestChannelUserMode()) return clientOK && targetOK &&
channelUserModeHasPrivsOver(
clientData.modes.HighestChannelUserMode(),
targetData.modes.HighestChannelUserMode(),
)
} }
func (channel *Channel) hasClient(client *Client) bool { func (channel *Channel) hasClient(client *Client) bool {
@ -1307,9 +1314,9 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod
if channel.flags.HasMode(modes.OpModerated) { if channel.flags.HasMode(modes.OpModerated) {
channel.stateMutex.RLock() channel.stateMutex.RLock()
cuData := channel.members[client] cuData, ok := channel.members[client]
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
if cuData.modes.HighestChannelUserMode() == modes.Mode(0) { if !ok || cuData.modes.HighestChannelUserMode() == modes.Mode(0) {
// max(statusmsg_minmode, halfop) // max(statusmsg_minmode, halfop)
if minPrefixMode == modes.Mode(0) || minPrefixMode == modes.Voice { if minPrefixMode == modes.Mode(0) || minPrefixMode == modes.Voice {
minPrefixMode = modes.Halfop minPrefixMode = modes.Halfop
@ -1478,7 +1485,7 @@ func (channel *Channel) Purge(source string) {
chname := channel.name chname := channel.name
members := channel.membersCache members := channel.membersCache
channel.membersCache = nil channel.membersCache = nil
channel.memberModesCache = nil channel.memberDataCache = nil
channel.members = make(MemberSet) channel.members = make(MemberSet)
// TODO try to prevent Purge racing against (pending) Join? // TODO try to prevent Purge racing against (pending) Join?
channel.stateMutex.Unlock() channel.stateMutex.Unlock()

View file

@ -16,17 +16,16 @@ import (
type ClientSet = utils.HashSet[*Client] type ClientSet = utils.HashSet[*Client]
type memberData struct { type memberData struct {
modes *modes.ModeSet modes modes.ModeSet
joinTime int64 joinTime int64
} }
// MemberSet is a set of members with modes. // MemberSet is a set of members with modes.
type MemberSet map[*Client]memberData type MemberSet map[*Client]*memberData
// Add adds the given client to this set. // Add adds the given client to this set.
func (members MemberSet) Add(member *Client) { func (members MemberSet) Add(member *Client) {
members[member] = memberData{ members[member] = &memberData{
modes: modes.NewModeSet(),
joinTime: time.Now().UnixNano(), joinTime: time.Now().UnixNano(),
} }
} }