3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 18:52:41 +01:00

channel: RWMutex for members access

This commit is contained in:
Daniel Oaks 2017-01-11 02:09:08 +10:00
parent efb3000717
commit 6f7c683247
5 changed files with 69 additions and 0 deletions

View File

@ -11,6 +11,8 @@ import (
"strconv" "strconv"
"time" "time"
"sync"
"github.com/DanielOaks/girc-go/ircmsg" "github.com/DanielOaks/girc-go/ircmsg"
) )
@ -18,6 +20,7 @@ type Channel struct {
flags ChannelModeSet flags ChannelModeSet
lists map[ChannelMode]*UserMaskSet lists map[ChannelMode]*UserMaskSet
key string key string
membersMutex sync.RWMutex
members MemberSet members MemberSet
name string name string
nameCasefolded string nameCasefolded string
@ -63,6 +66,8 @@ func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
} }
func (channel *Channel) IsEmpty() bool { func (channel *Channel) IsEmpty() bool {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
return len(channel.members) == 0 return len(channel.members) == 0
} }
@ -93,6 +98,9 @@ func (channel *Channel) Names(client *Client) {
// 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 ChannelMode) bool { func (channel *Channel) ClientIsAtLeast(client *Client, permission ChannelMode) bool {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
// get voice, since it's not a part of ChannelPrivModes // get voice, since it's not a part of ChannelPrivModes
if channel.members.HasMode(client, permission) { if channel.members.HasMode(client, permission) {
return true return true
@ -134,6 +142,9 @@ func (modes ChannelModeSet) Prefixes(isMultiPrefix bool) string {
} }
func (channel *Channel) Nicks(target *Client) []string { func (channel *Channel) Nicks(target *Client) []string {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
isMultiPrefix := (target != nil) && target.capabilities[MultiPrefix] isMultiPrefix := (target != nil) && target.capabilities[MultiPrefix]
isUserhostInNames := (target != nil) && target.capabilities[UserhostInNames] isUserhostInNames := (target != nil) && target.capabilities[UserhostInNames]
nicks := make([]string, len(channel.members)) nicks := make([]string, len(channel.members))
@ -160,7 +171,9 @@ func (channel *Channel) Nick() string {
// <mode> <mode params> // <mode> <mode params>
func (channel *Channel) ModeString(client *Client) (str string) { func (channel *Channel) ModeString(client *Client) (str string) {
channel.membersMutex.RLock()
isMember := client.flags[Operator] || channel.members.Has(client) isMember := client.flags[Operator] || channel.members.Has(client)
channel.membersMutex.RUnlock()
showKey := isMember && (channel.key != "") showKey := isMember && (channel.key != "")
showUserLimit := channel.userLimit > 0 showUserLimit := channel.userLimit > 0
@ -192,6 +205,9 @@ func (channel *Channel) ModeString(client *Client) (str string) {
} }
func (channel *Channel) IsFull() bool { func (channel *Channel) IsFull() bool {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
return (channel.userLimit > 0) && return (channel.userLimit > 0) &&
(uint64(len(channel.members)) >= channel.userLimit) (uint64(len(channel.members)) >= channel.userLimit)
} }
@ -201,6 +217,9 @@ func (channel *Channel) CheckKey(key string) bool {
} }
func (channel *Channel) Join(client *Client, key string) { func (channel *Channel) Join(client *Client, key string) {
channel.membersMutex.Lock()
defer channel.membersMutex.Unlock()
if channel.members.Has(client) { if channel.members.Has(client) {
// already joined, no message? // already joined, no message?
return return
@ -256,6 +275,9 @@ func (channel *Channel) Join(client *Client, key string) {
} }
func (channel *Channel) Part(client *Client, message string) { func (channel *Channel) Part(client *Client, message string) {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
if !channel.members.Has(client) { if !channel.members.Has(client) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel") client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
return return
@ -268,6 +290,9 @@ func (channel *Channel) Part(client *Client, message string) {
} }
func (channel *Channel) GetTopic(client *Client) { func (channel *Channel) GetTopic(client *Client) {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
if !channel.members.Has(client) { if !channel.members.Has(client) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, "You're not on that channel") client.Send(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, "You're not on that channel")
return return
@ -283,6 +308,9 @@ func (channel *Channel) GetTopic(client *Client) {
} }
func (channel *Channel) SetTopic(client *Client, topic string) { func (channel *Channel) SetTopic(client *Client, topic string) {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
if !(client.flags[Operator] || channel.members.Has(client)) { if !(client.flags[Operator] || channel.members.Has(client)) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel") client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
return return
@ -307,6 +335,9 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
} }
func (channel *Channel) CanSpeak(client *Client) bool { func (channel *Channel) CanSpeak(client *Client) bool {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
if client.flags[Operator] { if client.flags[Operator] {
return true return true
} }
@ -335,6 +366,10 @@ func (channel *Channel) sendMessage(cmd string, minPrefix *ChannelMode, clientOn
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel") client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
return return
} }
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
// for STATUSMSG // for STATUSMSG
var minPrefixMode ChannelMode var minPrefixMode ChannelMode
if minPrefix != nil { if minPrefix != nil {
@ -383,6 +418,9 @@ func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode,
func (channel *Channel) applyModeMember(client *Client, mode ChannelMode, func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
op ModeOp, nick string) *ChannelModeChange { op ModeOp, nick string) *ChannelModeChange {
channel.membersMutex.Lock()
defer channel.membersMutex.Unlock()
if nick == "" { if nick == "" {
//TODO(dan): shouldn't this be handled before it reaches this function? //TODO(dan): shouldn't this be handled before it reaches this function?
client.Send(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters") client.Send(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
@ -466,6 +504,9 @@ func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeO
} }
func (channel *Channel) Quit(client *Client) { func (channel *Channel) Quit(client *Client) {
channel.membersMutex.Lock()
defer channel.membersMutex.Unlock()
channel.members.Remove(client) channel.members.Remove(client)
client.channels.Remove(channel) client.channels.Remove(channel)
@ -475,6 +516,9 @@ func (channel *Channel) Quit(client *Client) {
} }
func (channel *Channel) Kick(client *Client, target *Client, comment string) { func (channel *Channel) Kick(client *Client, target *Client, comment string) {
channel.membersMutex.Lock()
defer channel.membersMutex.Unlock()
if !(client.flags[Operator] || channel.members.Has(client)) { if !(client.flags[Operator] || channel.members.Has(client)) {
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel") client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
return return
@ -504,6 +548,9 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
return return
} }
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
if !channel.members.Has(inviter) { if !channel.members.Has(inviter) {
inviter.Send(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel") inviter.Send(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
return return

View File

@ -308,6 +308,8 @@ func (client *Client) Friends(Capabilities ...Capability) ClientSet {
} }
for channel := range client.channels { for channel := range client.channels {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
for member := range channel.members { for member := range channel.members {
// make sure they have all the required caps // make sure they have all the required caps
for _, Cap := range Capabilities { for _, Cap := range Capabilities {

View File

@ -345,6 +345,9 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
channelName, err := CasefoldChannel(msg.Params[0]) channelName, err := CasefoldChannel(msg.Params[0])
channel := server.channels.Get(channelName) channel := server.channels.Get(channelName)
channel.membersMutex.Lock()
defer channel.membersMutex.Unlock()
if err != nil || channel == nil { if err != nil || channel == nil {
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, msg.Params[0], "No such channel") client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, msg.Params[0], "No such channel")
return false return false

View File

@ -87,12 +87,14 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
return return
} }
channel.membersMutex.RLock()
for member := range channel.members { for member := range channel.members {
if member == client && !client.capabilities[EchoMessage] { if member == client && !client.capabilities[EchoMessage] {
continue continue
} }
member.Send(nil, source, "PRIVMSG", channel.name, message) member.Send(nil, source, "PRIVMSG", channel.name, message)
} }
channel.membersMutex.RUnlock()
} else { } else {
target, err := CasefoldName(targetString) target, err := CasefoldName(targetString)
user := server.clients.Get(target) user := server.clients.Get(target)

View File

@ -843,6 +843,9 @@ func (client *Client) WhoisChannelsNames(target *Client) []string {
var chstrs []string var chstrs []string
index := 0 index := 0
for channel := range client.channels { for channel := range client.channels {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
// channel is secret and the target can't see it // channel is secret and the target can't see it
if !target.flags[Operator] && channel.flags[Secret] && !channel.members.Has(target) { if !target.flags[Operator] && channel.flags[Secret] && !channel.members.Has(target) {
continue continue
@ -933,6 +936,9 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
} }
if channel != nil { if channel != nil {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
flags += channel.members[client].Prefixes(target.capabilities[MultiPrefix]) flags += channel.members[client].Prefixes(target.capabilities[MultiPrefix])
channelName = channel.name channelName = channel.name
} }
@ -940,6 +946,9 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
} }
func whoChannel(client *Client, channel *Channel, friends ClientSet) { func whoChannel(client *Client, channel *Channel, friends ClientSet) {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
for member := range channel.members { for member := range channel.members {
if !client.flags[Invisible] || friends[client] { if !client.flags[Invisible] || friends[client] {
client.RplWhoReply(channel, member) client.RplWhoReply(channel, member)
@ -1372,6 +1381,9 @@ func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
// make sure client has privs to kick the given user // make sure client has privs to kick the given user
//TODO(dan): split this into a separate function that checks if users have privs //TODO(dan): split this into a separate function that checks if users have privs
// over other users, useful for things like -aoh as well // over other users, useful for things like -aoh as well
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
var hasPrivs bool var hasPrivs bool
for _, mode := range ChannelPrivModes { for _, mode := range ChannelPrivModes {
if channel.members[client][mode] { if channel.members[client][mode] {
@ -1448,6 +1460,9 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
} }
func (target *Client) RplList(channel *Channel) { func (target *Client) RplList(channel *Channel) {
channel.membersMutex.RLock()
defer channel.membersMutex.RUnlock()
// get the correct number of channel members // get the correct number of channel members
var memberCount int var memberCount int
if target.flags[Operator] || channel.members.Has(target) { if target.flags[Operator] || channel.members.Has(target) {