Various race preventing locks

This commit is contained in:
Sergey Matveev 2016-03-26 17:10:04 +03:00 committed by User Mue
parent 5ca748d928
commit 51766fc90d
4 changed files with 130 additions and 32 deletions

View File

@ -52,7 +52,7 @@ type Client struct {
sync.Mutex sync.Mutex
} }
func (c Client) Host() string { func (c *Client) Host() string {
addr := c.conn.RemoteAddr().String() addr := c.conn.RemoteAddr().String()
if host, _, err := net.SplitHostPort(addr); err == nil { if host, _, err := net.SplitHostPort(addr); err == nil {
addr = host addr = host
@ -63,7 +63,7 @@ func (c Client) Host() string {
return addr return addr
} }
func (c Client) String() string { func (c *Client) String() string {
return *c.nickname + "!" + *c.username + "@" + c.Host() return *c.nickname + "!" + *c.username + "@" + c.Host()
} }

View File

@ -40,18 +40,23 @@ const (
var ( var (
RENickname = regexp.MustCompile("^[a-zA-Z0-9-]{1,24}$") RENickname = regexp.MustCompile("^[a-zA-Z0-9-]{1,24}$")
roomsGroup sync.WaitGroup
clients map[*Client]struct{} = make(map[*Client]struct{}) clients map[*Client]struct{} = make(map[*Client]struct{})
clientsM sync.RWMutex
rooms map[string]*Room = make(map[string]*Room)
roomsM sync.RWMutex
roomsGroup sync.WaitGroup
roomSinks map[*Room]chan ClientEvent = make(map[*Room]chan ClientEvent)
) )
func SendLusers(client *Client) { func SendLusers(client *Client) {
lusers := 0 lusers := 0
clientsM.RLock()
for client := range clients { for client := range clients {
if client.registered { if client.registered {
lusers++ lusers++
} }
} }
clientsM.RUnlock()
client.ReplyNicknamed("251", fmt.Sprintf("There are %d users and 0 invisible on 1 servers", lusers)) client.ReplyNicknamed("251", fmt.Sprintf("There are %d users and 0 invisible on 1 servers", lusers))
} }
@ -82,11 +87,14 @@ func SendWhois(client *Client, nicknames []string) {
var subscriber *Client var subscriber *Client
for _, nickname := range nicknames { for _, nickname := range nicknames {
nickname = strings.ToLower(nickname) nickname = strings.ToLower(nickname)
clientsM.RLock()
for c = range clients { for c = range clients {
if strings.ToLower(*c.nickname) == nickname { if strings.ToLower(*c.nickname) == nickname {
clientsM.RUnlock()
goto Found goto Found
} }
} }
clientsM.RUnlock()
client.ReplyNoNickChan(nickname) client.ReplyNoNickChan(nickname)
continue continue
Found: Found:
@ -101,6 +109,7 @@ func SendWhois(client *Client, nicknames []string) {
client.ReplyNicknamed("301", *c.nickname, *c.away) client.ReplyNicknamed("301", *c.nickname, *c.away)
} }
subscriptions = make([]string, 0) subscriptions = make([]string, 0)
roomsM.RLock()
for _, room = range rooms { for _, room = range rooms {
for subscriber = range room.members { for subscriber = range room.members {
if *subscriber.nickname == nickname { if *subscriber.nickname == nickname {
@ -108,6 +117,7 @@ func SendWhois(client *Client, nicknames []string) {
} }
} }
} }
roomsM.RUnlock()
sort.Strings(subscriptions) sort.Strings(subscriptions)
client.ReplyNicknamed("319", *c.nickname, strings.Join(subscriptions, " ")) client.ReplyNicknamed("319", *c.nickname, strings.Join(subscriptions, " "))
client.ReplyNicknamed("318", *c.nickname, "End of /WHOIS list") client.ReplyNicknamed("318", *c.nickname, "End of /WHOIS list")
@ -121,14 +131,17 @@ func SendList(client *Client, cols []string) {
rs = strings.Split(strings.Split(cols[1], " ")[0], ",") rs = strings.Split(strings.Split(cols[1], " ")[0], ",")
} else { } else {
rs = make([]string, 0) rs = make([]string, 0)
roomsM.RLock()
for r = range rooms { for r = range rooms {
rs = append(rs, r) rs = append(rs, r)
} }
roomsM.RUnlock()
} }
sort.Strings(rs) sort.Strings(rs)
var room *Room var room *Room
var found bool var found bool
for _, r = range rs { for _, r = range rs {
roomsM.RLock()
if room, found = rooms[r]; found { if room, found = rooms[r]; found {
client.ReplyNicknamed( client.ReplyNicknamed(
"322", "322",
@ -137,6 +150,7 @@ func SendList(client *Client, cols []string) {
*room.topic, *room.topic,
) )
} }
roomsM.RUnlock()
} }
client.ReplyNicknamed("323", "End of /LIST") client.ReplyNicknamed("323", "End of /LIST")
} }
@ -163,12 +177,15 @@ func ClientRegister(client *Client, cmd string, cols []string) {
// Compatibility with some clients prepending colons to nickname // Compatibility with some clients prepending colons to nickname
nickname = strings.TrimPrefix(nickname, ":") nickname = strings.TrimPrefix(nickname, ":")
nickname = strings.ToLower(nickname) nickname = strings.ToLower(nickname)
clientsM.RLock()
for existingClient := range clients { for existingClient := range clients {
if *existingClient.nickname == nickname { if *existingClient.nickname == nickname {
clientsM.RUnlock()
client.ReplyParts("433", "*", nickname, "Nickname is already in use") client.ReplyParts("433", "*", nickname, "Nickname is already in use")
return return
} }
} }
clientsM.RUnlock()
if !RENickname.MatchString(nickname) { if !RENickname.MatchString(nickname) {
client.ReplyParts("432", "*", cols[1], "Erroneous nickname") client.ReplyParts("432", "*", cols[1], "Erroneous nickname")
return return
@ -227,8 +244,10 @@ func ClientRegister(client *Client, cmd string, cols []string) {
func RoomRegister(name string) (*Room, chan ClientEvent) { func RoomRegister(name string) (*Room, chan ClientEvent) {
roomNew := NewRoom(name) roomNew := NewRoom(name)
roomSink := make(chan ClientEvent) roomSink := make(chan ClientEvent)
roomsM.Lock()
rooms[name] = roomNew rooms[name] = roomNew
roomSinks[roomNew] = roomSink roomSinks[roomNew] = roomSink
roomsM.Unlock()
go roomNew.Processor(roomSink) go roomNew.Processor(roomSink)
roomsGroup.Add(1) roomsGroup.Add(1)
return roomNew, roomSink return roomNew, roomSink
@ -257,8 +276,10 @@ func HandlerJoin(client *Client, cmd string) {
} else { } else {
key = "" key = ""
} }
roomsM.RLock()
for roomExisting, roomSink = range roomSinks { for roomExisting, roomSink = range roomSinks {
if room == *roomExisting.name { if room == *roomExisting.name {
roomsM.RUnlock()
if (*roomExisting.key != "") && (*roomExisting.key != key) { if (*roomExisting.key != "") && (*roomExisting.key != key) {
goto Denied goto Denied
} }
@ -266,6 +287,7 @@ func HandlerJoin(client *Client, cmd string) {
goto Joined goto Joined
} }
} }
roomsM.RUnlock()
roomNew, roomSink = RoomRegister(room) roomNew, roomSink = RoomRegister(room)
log.Println("Room", roomNew, "created") log.Println("Room", roomNew, "created")
if key != "" { if key != "" {
@ -293,6 +315,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
client := event.client client := event.client
switch event.eventType { switch event.eventType {
case EventTick: case EventTick:
clientsM.RLock()
for c := range clients { for c := range clients {
if c.recvTimestamp.Add(PingTimeout).Before(now) { if c.recvTimestamp.Add(PingTimeout).Before(now) {
log.Println(c, "ping timeout") log.Println(c, "ping timeout")
@ -309,6 +332,8 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} }
} }
} }
clientsM.RUnlock()
roomsM.Lock()
for rn, r := range rooms { for rn, r := range rooms {
if *statedir == "" && len(r.members) == 0 { if *statedir == "" && len(r.members) == 0 {
log.Println(rn, "emptied room") log.Println(rn, "emptied room")
@ -317,20 +342,29 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
delete(roomSinks, r) delete(roomSinks, r)
} }
} }
roomsM.Unlock()
case EventTerm: case EventTerm:
roomsM.RLock()
for _, sink := range roomSinks { for _, sink := range roomSinks {
sink <- ClientEvent{eventType: EventTerm} sink <- ClientEvent{eventType: EventTerm}
} }
roomsM.RUnlock()
roomsGroup.Wait() roomsGroup.Wait()
close(finished) close(finished)
return return
case EventNew: case EventNew:
clientsM.Lock()
clients[client] = struct{}{} clients[client] = struct{}{}
clientsM.Unlock()
case EventDel: case EventDel:
clientsM.Lock()
delete(clients, client) delete(clients, client)
clientsM.Unlock()
roomsM.RLock()
for _, roomSink := range roomSinks { for _, roomSink := range roomSinks {
roomSink <- event roomSink <- event
} }
roomsM.RUnlock()
case EventMsg: case EventMsg:
cols := strings.SplitN(event.text, " ", 2) cols := strings.SplitN(event.text, " ", 2)
cmd := strings.ToUpper(cols[0]) cmd := strings.ToUpper(cols[0])
@ -384,9 +418,11 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
continue continue
} }
room := cols[0] room := cols[0]
roomsM.RLock()
r, found := rooms[room] r, found := rooms[room]
if !found { if !found {
client.ReplyNoChannel(room) client.ReplyNoChannel(room)
roomsM.RUnlock()
continue continue
} }
if len(cols) == 1 { if len(cols) == 1 {
@ -394,6 +430,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} else { } else {
roomSinks[r] <- ClientEvent{client, EventMode, cols[1]} roomSinks[r] <- ClientEvent{client, EventMode, cols[1]}
} }
roomsM.RUnlock()
case "MOTD": case "MOTD":
SendMotd(client) SendMotd(client)
case "PART": case "PART":
@ -402,14 +439,17 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
continue continue
} }
rs := strings.Split(cols[1], " ")[0] rs := strings.Split(cols[1], " ")[0]
roomsM.RLock()
for _, room := range strings.Split(rs, ",") { for _, room := range strings.Split(rs, ",") {
if r, found := rooms[room]; found { if r, found := rooms[room]; found {
roomSinks[r] <- ClientEvent{client, EventDel, ""} roomSinks[r] <- ClientEvent{client, EventDel, ""}
} else { } else {
roomsM.RUnlock()
client.ReplyNoChannel(room) client.ReplyNoChannel(room)
continue continue
} }
} }
roomsM.RUnlock()
case "PING": case "PING":
if len(cols) == 1 { if len(cols) == 1 {
client.ReplyNicknamed("409", "No origin specified") client.ReplyNicknamed("409", "No origin specified")
@ -430,6 +470,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} }
msg := "" msg := ""
target := strings.ToLower(cols[0]) target := strings.ToLower(cols[0])
clientsM.RLock()
for c := range clients { for c := range clients {
if *c.nickname == target { if *c.nickname == target {
msg = fmt.Sprintf(":%s %s %s %s", client, cmd, *c.nickname, cols[1]) msg = fmt.Sprintf(":%s %s %s %s", client, cmd, *c.nickname, cols[1])
@ -440,9 +481,11 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
break break
} }
} }
clientsM.RUnlock()
if msg != "" { if msg != "" {
continue continue
} }
roomsM.RLock()
if r, found := rooms[target]; found { if r, found := rooms[target]; found {
roomSinks[r] <- ClientEvent{ roomSinks[r] <- ClientEvent{
client, client,
@ -452,13 +495,16 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} else { } else {
client.ReplyNoNickChan(target) client.ReplyNoNickChan(target)
} }
roomsM.RUnlock()
case "TOPIC": case "TOPIC":
if len(cols) == 1 { if len(cols) == 1 {
client.ReplyNotEnoughParameters("TOPIC") client.ReplyNotEnoughParameters("TOPIC")
continue continue
} }
cols = strings.SplitN(cols[1], " ", 2) cols = strings.SplitN(cols[1], " ", 2)
roomsM.RLock()
r, found := rooms[cols[0]] r, found := rooms[cols[0]]
roomsM.RUnlock()
if !found { if !found {
client.ReplyNoChannel(cols[0]) client.ReplyNoChannel(cols[0])
continue continue
@ -469,18 +515,22 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} else { } else {
change = "" change = ""
} }
roomsM.RLock()
roomSinks[r] <- ClientEvent{client, EventTopic, change} roomSinks[r] <- ClientEvent{client, EventTopic, change}
roomsM.RUnlock()
case "WHO": case "WHO":
if len(cols) == 1 || len(cols[1]) < 1 { if len(cols) == 1 || len(cols[1]) < 1 {
client.ReplyNotEnoughParameters("WHO") client.ReplyNotEnoughParameters("WHO")
continue continue
} }
room := strings.Split(cols[1], " ")[0] room := strings.Split(cols[1], " ")[0]
roomsM.RLock()
if r, found := rooms[room]; found { if r, found := rooms[room]; found {
roomSinks[r] <- ClientEvent{client, EventWho, ""} roomSinks[r] <- ClientEvent{client, EventWho, ""}
} else { } else {
client.ReplyNoChannel(room) client.ReplyNoChannel(room)
} }
roomsM.RUnlock()
case "WHOIS": case "WHOIS":
if len(cols) == 1 || len(cols[1]) < 1 { if len(cols) == 1 || len(cols[1]) < 1 {
client.ReplyNotEnoughParameters("WHOIS") client.ReplyNotEnoughParameters("WHOIS")
@ -495,9 +545,11 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
continue continue
} }
nicksKnown := make(map[string]struct{}) nicksKnown := make(map[string]struct{})
clientsM.RLock()
for c := range clients { for c := range clients {
nicksKnown[*c.nickname] = struct{}{} nicksKnown[*c.nickname] = struct{}{}
} }
clientsM.RUnlock()
var nicksExists []string var nicksExists []string
for _, nickname := range strings.Split(cols[1], " ") { for _, nickname := range strings.Split(cols[1], " ") {
if _, exists := nicksKnown[nickname]; exists { if _, exists := nicksKnown[nickname]; exists {

92
room.go
View File

@ -24,14 +24,11 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"sync"
) )
var ( var (
RERoom = regexp.MustCompile("^#[^\x00\x07\x0a\x0d ,:/]{1,200}$") RERoom = regexp.MustCompile("^#[^\x00\x07\x0a\x0d ,:/]{1,200}$")
rooms map[string]*Room = make(map[string]*Room)
roomSinks map[*Room]chan ClientEvent = make(map[*Room]chan ClientEvent)
) )
// Sanitize room's name. It can consist of 1 to 50 ASCII symbols // Sanitize room's name. It can consist of 1 to 50 ASCII symbols
@ -45,10 +42,14 @@ type Room struct {
topic *string topic *string
key *string key *string
members map[*Client]struct{} members map[*Client]struct{}
sync.RWMutex
} }
func (room Room) String() string { func (room *Room) String() (name string) {
return *room.name room.RLock()
name = *room.name
room.RUnlock()
return
} }
func NewRoom(name string) *Room { func NewRoom(name string) *Room {
@ -63,25 +64,31 @@ func NewRoom(name string) *Room {
} }
func (room *Room) SendTopic(client *Client) { func (room *Room) SendTopic(client *Client) {
room.RLock()
if *room.topic == "" { if *room.topic == "" {
client.ReplyNicknamed("331", *room.name, "No topic is set") client.ReplyNicknamed("331", room.String(), "No topic is set")
} else { } else {
client.ReplyNicknamed("332", *room.name, *room.topic) client.ReplyNicknamed("332", room.String(), *room.topic)
} }
room.RUnlock()
} }
// Send message to all room's subscribers, possibly excluding someone. // Send message to all room's subscribers, possibly excluding someone.
func (room *Room) Broadcast(msg string, clientToIgnore ...*Client) { func (room *Room) Broadcast(msg string, clientToIgnore ...*Client) {
room.RLock()
for member := range room.members { for member := range room.members {
if (len(clientToIgnore) > 0) && member == clientToIgnore[0] { if (len(clientToIgnore) > 0) && member == clientToIgnore[0] {
continue continue
} }
member.Msg(msg) member.Msg(msg)
} }
room.RUnlock()
} }
func (room *Room) StateSave() { func (room *Room) StateSave() {
stateSink <- StateEvent{*room.name, *room.topic, *room.key} room.RLock()
stateSink <- StateEvent{room.String(), *room.topic, *room.key}
room.RUnlock()
} }
func (room *Room) Processor(events <-chan ClientEvent) { func (room *Room) Processor(events <-chan ClientEvent) {
@ -93,54 +100,74 @@ func (room *Room) Processor(events <-chan ClientEvent) {
roomsGroup.Done() roomsGroup.Done()
return return
case EventNew: case EventNew:
room.Lock()
room.members[client] = struct{}{} room.members[client] = struct{}{}
if *verbose { if *verbose {
log.Println(client, "joined", room.name) log.Println(client, "joined", room.name)
} }
room.Unlock()
room.SendTopic(client) room.SendTopic(client)
room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, *room.name)) room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, room.String()))
logSink <- LogEvent{*room.name, *client.nickname, "joined", true} logSink <- LogEvent{room.String(), *client.nickname, "joined", true}
nicknames := make([]string, 0) nicknames := make([]string, 0)
room.RLock()
for member := range room.members { for member := range room.members {
nicknames = append(nicknames, *member.nickname) nicknames = append(nicknames, *member.nickname)
} }
room.RUnlock()
sort.Strings(nicknames) sort.Strings(nicknames)
client.ReplyNicknamed("353", "=", *room.name, strings.Join(nicknames, " ")) client.ReplyNicknamed("353", "=", room.String(), strings.Join(nicknames, " "))
client.ReplyNicknamed("366", *room.name, "End of NAMES list") client.ReplyNicknamed("366", room.String(), "End of NAMES list")
case EventDel: case EventDel:
room.RLock()
if _, subscribed := room.members[client]; !subscribed { if _, subscribed := room.members[client]; !subscribed {
client.ReplyNicknamed("442", *room.name, "You are not on that channel") client.ReplyNicknamed("442", room.String(), "You are not on that channel")
room.RUnlock()
continue continue
} }
room.RUnlock()
room.Lock()
delete(room.members, client) delete(room.members, client)
msg := fmt.Sprintf(":%s PART %s :%s", client, *room.name, *client.nickname) room.Unlock()
room.RLock()
msg := fmt.Sprintf(":%s PART %s :%s", client, room.String(), *client.nickname)
room.Broadcast(msg) room.Broadcast(msg)
logSink <- LogEvent{*room.name, *client.nickname, "left", true} logSink <- LogEvent{room.String(), *client.nickname, "left", true}
room.RUnlock()
case EventTopic: case EventTopic:
room.RLock()
if _, subscribed := room.members[client]; !subscribed { if _, subscribed := room.members[client]; !subscribed {
client.ReplyParts("442", *room.name, "You are not on that channel") client.ReplyParts("442", room.String(), "You are not on that channel")
room.RUnlock()
continue continue
} }
if event.text == "" { if event.text == "" {
room.SendTopic(client) room.SendTopic(client)
room.RUnlock()
continue continue
} }
room.RUnlock()
topic := strings.TrimLeft(event.text, ":") topic := strings.TrimLeft(event.text, ":")
room.Lock()
room.topic = &topic room.topic = &topic
msg := fmt.Sprintf(":%s TOPIC %s :%s", client, *room.name, *room.topic) room.Unlock()
room.RLock()
msg := fmt.Sprintf(":%s TOPIC %s :%s", client, room.String(), *room.topic)
room.Broadcast(msg) room.Broadcast(msg)
logSink <- LogEvent{ logSink <- LogEvent{
*room.name, room.String(),
*client.nickname, *client.nickname,
"set topic to " + *room.topic, "set topic to " + *room.topic,
true, true,
} }
room.RUnlock()
room.StateSave() room.StateSave()
case EventWho: case EventWho:
room.RLock()
for m := range room.members { for m := range room.members {
client.ReplyNicknamed( client.ReplyNicknamed(
"352", "352",
*room.name, room.String(),
*m.username, *m.username,
m.Host(), m.Host(),
*hostname, *hostname,
@ -149,29 +176,36 @@ func (room *Room) Processor(events <-chan ClientEvent) {
"0 "+*m.realname, "0 "+*m.realname,
) )
} }
client.ReplyNicknamed("315", *room.name, "End of /WHO list") client.ReplyNicknamed("315", room.String(), "End of /WHO list")
room.RUnlock()
case EventMode: case EventMode:
room.RLock()
if event.text == "" { if event.text == "" {
mode := "+" mode := "+"
if *room.key != "" { if *room.key != "" {
mode = mode + "k" mode = mode + "k"
} }
client.Msg(fmt.Sprintf("324 %s %s %s", *client.nickname, *room.name, mode)) client.Msg(fmt.Sprintf("324 %s %s %s", *client.nickname, room.String(), mode))
room.RUnlock()
continue continue
} }
if strings.HasPrefix(event.text, "b") { if strings.HasPrefix(event.text, "b") {
client.ReplyNicknamed("368", *room.name, "End of channel ban list") client.ReplyNicknamed("368", room.String(), "End of channel ban list")
room.RUnlock()
continue continue
} }
if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") { if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") {
if _, subscribed := room.members[client]; !subscribed { if _, subscribed := room.members[client]; !subscribed {
client.ReplyParts("442", *room.name, "You are not on that channel") client.ReplyParts("442", room.String(), "You are not on that channel")
room.RUnlock()
continue continue
} }
} else { } else {
client.ReplyNicknamed("472", event.text, "Unknown MODE flag") client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
room.RUnlock()
continue continue
} }
room.RUnlock()
var msg string var msg string
var msgLog string var msgLog string
if strings.HasPrefix(event.text, "+k") { if strings.HasPrefix(event.text, "+k") {
@ -180,17 +214,21 @@ func (room *Room) Processor(events <-chan ClientEvent) {
client.ReplyNotEnoughParameters("MODE") client.ReplyNotEnoughParameters("MODE")
continue continue
} }
room.Lock()
room.key = &cols[1] room.key = &cols[1]
msg = fmt.Sprintf(":%s MODE %s +k %s", client, *room.name, *room.key) msg = fmt.Sprintf(":%s MODE %s +k %s", client, *room.name, *room.key)
msgLog = "set channel key to " + *room.key msgLog = "set channel key to " + *room.key
room.Unlock()
} else { } else {
key := "" key := ""
room.Lock()
room.key = &key room.key = &key
msg = fmt.Sprintf(":%s MODE %s -k", client, *room.name) msg = fmt.Sprintf(":%s MODE %s -k", client, *room.name)
room.Unlock()
msgLog = "removed channel key" msgLog = "removed channel key"
} }
room.Broadcast(msg) room.Broadcast(msg)
logSink <- LogEvent{*room.name, *client.nickname, msgLog, true} logSink <- LogEvent{room.String(), *client.nickname, msgLog, true}
room.StateSave() room.StateSave()
case EventMsg: case EventMsg:
sep := strings.Index(event.text, " ") sep := strings.Index(event.text, " ")
@ -198,12 +236,12 @@ func (room *Room) Processor(events <-chan ClientEvent) {
":%s %s %s :%s", ":%s %s %s :%s",
client, client,
event.text[:sep], event.text[:sep],
*room.name, room.String(),
event.text[sep+1:]), event.text[sep+1:]),
client, client,
) )
logSink <- LogEvent{ logSink <- LogEvent{
*room.name, room.String(),
*client.nickname, *client.nickname,
event.text[sep+1:], event.text[sep+1:],
false, false,

View File

@ -47,9 +47,11 @@ func TestTwoUsers(t *testing.T) {
host := "foohost" host := "foohost"
hostname = &host hostname = &host
events := make(chan ClientEvent) events := make(chan ClientEvent)
roomsM.Lock()
rooms = make(map[string]*Room) rooms = make(map[string]*Room)
clients = make(map[*Client]struct{})
roomSinks = make(map[*Room]chan ClientEvent) roomSinks = make(map[*Room]chan ClientEvent)
roomsM.Unlock()
clients = make(map[*Client]struct{})
finished := make(chan struct{}) finished := make(chan struct{})
go Processor(events, finished) go Processor(events, finished)
defer func() { defer func() {
@ -182,12 +184,14 @@ func TestJoin(t *testing.T) {
for i := 0; i < 4*2; i++ { for i := 0; i < 4*2; i++ {
<-conn.outbound <-conn.outbound
} }
roomsM.RLock()
if _, ok := rooms["#bar"]; !ok { if _, ok := rooms["#bar"]; !ok {
t.Fatal("#bar does not exist") t.Fatal("#bar does not exist")
} }
if _, ok := rooms["#baz"]; !ok { if _, ok := rooms["#baz"]; !ok {
t.Fatal("#baz does not exist") t.Fatal("#baz does not exist")
} }
roomsM.RUnlock()
if r := <-logSink; (r.what != "joined") || (r.where != "#bar") || (r.who != "nick2") || (r.meta != true) { if r := <-logSink; (r.what != "joined") || (r.where != "#bar") || (r.who != "nick2") || (r.meta != true) {
t.Fatal("invalid join log event #bar", r) t.Fatal("invalid join log event #bar", r)
} }
@ -199,12 +203,14 @@ func TestJoin(t *testing.T) {
for i := 0; i < 4*2; i++ { for i := 0; i < 4*2; i++ {
<-conn.outbound <-conn.outbound
} }
roomsM.RLock()
if *rooms["#barenc"].key != "key1" { if *rooms["#barenc"].key != "key1" {
t.Fatal("no room with key1") t.Fatal("no room with key1")
} }
if *rooms["#bazenc"].key != "key2" { if *rooms["#bazenc"].key != "key2" {
t.Fatal("no room with key2") t.Fatal("no room with key2")
} }
roomsM.RUnlock()
if r := <-logSink; (r.what != "joined") || (r.where != "#barenc") || (r.who != "nick2") || (r.meta != true) { if r := <-logSink; (r.what != "joined") || (r.where != "#barenc") || (r.who != "nick2") || (r.meta != true) {
t.Fatal("invalid join log event #barenc", r) t.Fatal("invalid join log event #barenc", r)
} }
@ -222,9 +228,11 @@ func TestJoin(t *testing.T) {
if r := <-conn.outbound; r != ":nick2!foo2@someclient MODE #barenc -k\r\n" { if r := <-conn.outbound; r != ":nick2!foo2@someclient MODE #barenc -k\r\n" {
t.Fatal("remove #barenc key", r) t.Fatal("remove #barenc key", r)
} }
roomsM.RLock()
if *rooms["#barenc"].key != "" { if *rooms["#barenc"].key != "" {
t.Fatal("removing key from #barenc") t.Fatal("removing key from #barenc")
} }
roomsM.RUnlock()
if r := <-logSink; (r.what != "removed channel key") || (r.where != "#barenc") || (r.who != "nick2") || (r.meta != true) { if r := <-logSink; (r.what != "removed channel key") || (r.where != "#barenc") || (r.who != "nick2") || (r.meta != true) {
t.Fatal("removed channel key log", r) t.Fatal("removed channel key log", r)
} }