Parcourir la source

refactor synchronization for Channel

tags/v0.10.1
Shivaram Lingamneni il y a 6 ans
Parent
révision
fa83ccd82b
9 fichiers modifiés avec 431 ajouts et 339 suppressions
  1. 203
    180
      irc/channel.go
  2. 1
    4
      irc/chanserv.go
  3. 34
    8
      irc/client.go
  4. 50
    16
      irc/client_lookup_set.go
  5. 94
    0
      irc/getters.go
  6. 25
    51
      irc/modes.go
  7. 1
    3
      irc/roleplay.go
  8. 23
    67
      irc/server.go
  9. 0
    10
      irc/types.go

+ 203
- 180
irc/channel.go Voir le fichier

@@ -23,12 +23,13 @@ type Channel struct {
23 23
 	flags          ModeSet
24 24
 	lists          map[Mode]*UserMaskSet
25 25
 	key            string
26
-	membersMutex   sync.RWMutex
27 26
 	members        MemberSet
27
+	membersCache   []*Client // allow iteration over channel members without holding the lock
28 28
 	name           string
29 29
 	nameCasefolded string
30 30
 	server         *Server
31 31
 	createdTime    time.Time
32
+	stateMutex     sync.RWMutex
32 33
 	topic          string
33 34
 	topicSetBy     string
34 35
 	topicSetTime   time.Time
@@ -68,28 +69,29 @@ func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
68 69
 	return channel
69 70
 }
70 71
 
71
-// IsEmpty returns true if the channel has no clients.
72
-func (channel *Channel) IsEmpty() bool {
73
-	channel.membersMutex.RLock()
74
-	defer channel.membersMutex.RUnlock()
75
-
76
-	return channel.isEmptyNoMutex()
77
-}
72
+func (channel *Channel) regenerateMembersCache() {
73
+	// this is eventually consistent even without holding the writable Lock()
74
+	// throughout the update; all updates to `members` while holding Lock()
75
+	// have a serial order, so the call to `regenerateMembersCache` that
76
+	// happens-after the last one will see *all* the updates
77
+	channel.stateMutex.RLock()
78
+	result := make([]*Client, len(channel.members))
79
+	i := 0
80
+	for client := range channel.members {
81
+		result[i] = client
82
+		i++
83
+	}
84
+	channel.stateMutex.RUnlock()
85
+	channel.stateMutex.Lock()
86
+	channel.membersCache = result
87
+	channel.stateMutex.Unlock()
88
+	return
78 89
 
79
-func (channel *Channel) isEmptyNoMutex() bool {
80
-	return len(channel.members) == 0
81 90
 }
82 91
 
83 92
 // Names sends the list of users joined to the channel to the given client.
84 93
 func (channel *Channel) Names(client *Client) {
85
-	channel.membersMutex.RLock()
86
-	defer channel.membersMutex.RUnlock()
87
-
88
-	channel.namesNoMutex(client)
89
-}
90
-
91
-func (channel *Channel) namesNoMutex(client *Client) {
92
-	currentNicks := channel.nicksNoMutex(client)
94
+	currentNicks := channel.nicks(client)
93 95
 	// assemble and send replies
94 96
 	maxNamLen := 480 - len(client.server.name) - len(client.nick)
95 97
 	var buffer string
@@ -115,14 +117,8 @@ func (channel *Channel) namesNoMutex(client *Client) {
115 117
 
116 118
 // ClientIsAtLeast returns whether the client has at least the given channel privilege.
117 119
 func (channel *Channel) ClientIsAtLeast(client *Client, permission Mode) bool {
118
-	channel.membersMutex.RLock()
119
-	defer channel.membersMutex.RUnlock()
120
-
121
-	return channel.clientIsAtLeastNoMutex(client, permission)
122
-}
123
-
124
-func (channel *Channel) clientIsAtLeastNoMutex(client *Client, permission Mode) bool {
125
-	// requires RLock()
120
+	channel.stateMutex.RLock()
121
+	defer channel.stateMutex.RUnlock()
126 122
 
127 123
 	// get voice, since it's not a part of ChannelPrivModes
128 124
 	if channel.members.HasMode(client, permission) {
@@ -164,60 +160,119 @@ func (modes ModeSet) Prefixes(isMultiPrefix bool) string {
164 160
 	return prefixes
165 161
 }
166 162
 
167
-func (channel *Channel) nicksNoMutex(target *Client) []string {
163
+func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) string {
164
+	channel.stateMutex.RLock()
165
+	defer channel.stateMutex.RUnlock()
166
+	modes, present := channel.members[client]
167
+	if !present {
168
+		return ""
169
+	} else {
170
+		return modes.Prefixes(isMultiPrefix)
171
+	}
172
+}
173
+
174
+func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
175
+	channel.stateMutex.RLock()
176
+	defer channel.stateMutex.RUnlock()
177
+
178
+	clientModes := channel.members[client]
179
+	targetModes := channel.members[target]
180
+	result := false
181
+	for _, mode := range ChannelPrivModes {
182
+		if clientModes[mode] {
183
+			result = true
184
+			// admins cannot kick other admins
185
+			if mode == ChannelAdmin && targetModes[ChannelAdmin] {
186
+				result = false
187
+			}
188
+			break
189
+		} else if channel.members[target][mode] {
190
+			break
191
+		}
192
+	}
193
+	return result
194
+}
195
+
196
+func (channel *Channel) nicks(target *Client) []string {
168 197
 	isMultiPrefix := (target != nil) && target.capabilities.Has(caps.MultiPrefix)
169 198
 	isUserhostInNames := (target != nil) && target.capabilities.Has(caps.UserhostInNames)
170
-	nicks := make([]string, len(channel.members))
199
+
200
+	// slightly cumbersome: get the mutex and copy both the client pointers and
201
+	// the mode prefixes
202
+	channel.stateMutex.RLock()
203
+	length := len(channel.members)
204
+	clients := make([]*Client, length)
205
+	result := make([]string, length)
171 206
 	i := 0
172 207
 	for client, modes := range channel.members {
173
-		nicks[i] += modes.Prefixes(isMultiPrefix)
208
+		clients[i] = client
209
+		result[i] = modes.Prefixes(isMultiPrefix)
210
+		i++
211
+	}
212
+	channel.stateMutex.RUnlock()
213
+
214
+	i = 0
215
+	for i < length {
174 216
 		if isUserhostInNames {
175
-			nicks[i] += client.nickMaskString
217
+			result[i] += clients[i].getNickMaskString()
176 218
 		} else {
177
-			nicks[i] += client.nick
219
+			result[i] += clients[i].getNick()
178 220
 		}
179 221
 		i++
180 222
 	}
181
-	return nicks
223
+
224
+	return result
225
+}
226
+
227
+func (channel *Channel) hasClient(client *Client) bool {
228
+	channel.stateMutex.RLock()
229
+	defer channel.stateMutex.RUnlock()
230
+	_, present := channel.members[client]
231
+	return present
182 232
 }
183 233
 
184 234
 // <mode> <mode params>
185
-func (channel *Channel) modeStringNoLock(client *Client) (str string) {
186
-	// RLock()
187
-	isMember := client.flags[Operator] || channel.members.Has(client)
188
-	// RUnlock()
235
+func (channel *Channel) modeStrings(client *Client) (result []string) {
236
+	isMember := client.HasMode(Operator) || channel.hasClient(client)
189 237
 	showKey := isMember && (channel.key != "")
190 238
 	showUserLimit := channel.userLimit > 0
191 239
 
240
+	modes := "+"
241
+
192 242
 	// flags with args
193 243
 	if showKey {
194
-		str += Key.String()
244
+		modes += Key.String()
195 245
 	}
196 246
 	if showUserLimit {
197
-		str += UserLimit.String()
247
+		modes += UserLimit.String()
198 248
 	}
199 249
 
250
+	channel.stateMutex.RLock()
251
+	defer channel.stateMutex.RUnlock()
252
+
200 253
 	// flags
201 254
 	for mode := range channel.flags {
202
-		str += mode.String()
255
+		modes += mode.String()
203 256
 	}
204 257
 
205
-	str = "+" + str
258
+	result = []string{modes}
206 259
 
207 260
 	// args for flags with args: The order must match above to keep
208 261
 	// positional arguments in place.
209 262
 	if showKey {
210
-		str += " " + channel.key
263
+		result = append(result, channel.key)
211 264
 	}
212 265
 	if showUserLimit {
213
-		str += " " + strconv.FormatUint(channel.userLimit, 10)
266
+		result = append(result, strconv.FormatUint(channel.userLimit, 10))
214 267
 	}
215 268
 
216
-	return str
269
+	return
217 270
 }
218 271
 
219 272
 // IsFull returns true if this channel is at its' members limit.
220 273
 func (channel *Channel) IsFull() bool {
274
+	channel.stateMutex.RLock()
275
+	defer channel.stateMutex.RUnlock()
221 276
 	return (channel.userLimit > 0) && (uint64(len(channel.members)) >= channel.userLimit)
222 277
 }
223 278
 
@@ -229,9 +284,7 @@ func (channel *Channel) CheckKey(key string) bool {
229 284
 // Join joins the given client to this channel (if they can be joined).
230 285
 //TODO(dan): /SAJOIN and maybe a ForceJoin function?
231 286
 func (channel *Channel) Join(client *Client, key string) {
232
-	channel.membersMutex.Lock()
233
-	defer channel.membersMutex.Unlock()
234
-	if channel.members.Has(client) {
287
+	if channel.hasClient(client) {
235 288
 		// already joined, no message needs to be sent
236 289
 		return
237 290
 	}
@@ -261,7 +314,7 @@ func (channel *Channel) Join(client *Client, key string) {
261 314
 
262 315
 	client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", client.nick, channel.name))
263 316
 
264
-	for member := range channel.members {
317
+	for _, member := range channel.Members() {
265 318
 		if member.capabilities.Has(caps.ExtendedJoin) {
266 319
 			member.Send(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
267 320
 		} else {
@@ -269,8 +322,13 @@ func (channel *Channel) Join(client *Client, key string) {
269 322
 		}
270 323
 	}
271 324
 
272
-	client.channels.Add(channel)
325
+	channel.stateMutex.Lock()
273 326
 	channel.members.Add(client)
327
+	firstJoin := len(channel.members) == 1
328
+	channel.stateMutex.Unlock()
329
+	channel.regenerateMembersCache()
330
+
331
+	client.addChannel(channel)
274 332
 
275 333
 	// give channel mode if necessary
276 334
 	var newChannel bool
@@ -281,20 +339,25 @@ func (channel *Channel) Join(client *Client, key string) {
281 339
 		chanReg := client.server.loadChannelNoMutex(tx, channel.nameCasefolded)
282 340
 
283 341
 		if chanReg == nil {
284
-			if len(channel.members) == 1 {
342
+			if firstJoin {
343
+				channel.stateMutex.Lock()
285 344
 				channel.createdTime = time.Now()
286 345
 				channel.members[client][ChannelOperator] = true
346
+				channel.stateMutex.Unlock()
287 347
 				givenMode = &ChannelOperator
288 348
 				newChannel = true
289 349
 			}
290 350
 		} else {
291 351
 			// we should only do this on registered channels
292 352
 			if client.account != nil && client.account.Name == chanReg.Founder {
353
+				channel.stateMutex.Lock()
293 354
 				channel.members[client][ChannelFounder] = true
355
+				channel.stateMutex.Unlock()
294 356
 				givenMode = &ChannelFounder
295 357
 			}
296
-			if len(channel.members) == 1 {
358
+			if firstJoin {
297 359
 				// apply other details if new channel
360
+				channel.stateMutex.Lock()
298 361
 				channel.topic = chanReg.Topic
299 362
 				channel.topicSetBy = chanReg.TopicSetBy
300 363
 				channel.topicSetTime = chanReg.TopicSetTime
@@ -309,6 +372,7 @@ func (channel *Channel) Join(client *Client, key string) {
309 372
 				for _, mask := range chanReg.Invitelist {
310 373
 					channel.lists[InviteMask].Add(mask)
311 374
 				}
375
+				channel.stateMutex.Unlock()
312 376
 			}
313 377
 		}
314 378
 		return nil
@@ -321,11 +385,11 @@ func (channel *Channel) Join(client *Client, key string) {
321 385
 	}
322 386
 	// don't sent topic when it's an entirely new channel
323 387
 	if !newChannel {
324
-		channel.getTopicNoMutex(client) // we already have Lock
388
+		channel.SendTopic(client)
325 389
 	}
326
-	channel.namesNoMutex(client)
390
+	channel.Names(client)
327 391
 	if givenMode != nil {
328
-		for member := range channel.members {
392
+		for _, member := range channel.Members() {
329 393
 			member.Send(nil, client.server.name, "MODE", channel.name, fmt.Sprintf("+%v", *givenMode), client.nick)
330 394
 		}
331 395
 	}
@@ -333,58 +397,50 @@ func (channel *Channel) Join(client *Client, key string) {
333 397
 
334 398
 // Part parts the given client from this channel, with the given message.
335 399
 func (channel *Channel) Part(client *Client, message string) {
336
-	channel.membersMutex.Lock()
337
-	defer channel.membersMutex.Unlock()
338
-
339
-	if !channel.members.Has(client) {
400
+	if !channel.hasClient(client) {
340 401
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
341 402
 		return
342 403
 	}
343 404
 
344
-	for member := range channel.members {
405
+	for _, member := range channel.Members() {
345 406
 		member.Send(nil, client.nickMaskString, "PART", channel.name, message)
346 407
 	}
347
-	channel.quitNoMutex(client)
408
+	channel.Quit(client)
348 409
 
349 410
 	client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", client.nick, channel.name))
350 411
 }
351 412
 
352
-// GetTopic sends the channel topic to the given client.
353
-func (channel *Channel) GetTopic(client *Client) {
354
-	channel.membersMutex.RLock()
355
-	defer channel.membersMutex.RUnlock()
356
-
357
-	channel.getTopicNoMutex(client)
358
-}
359
-
360
-// GetTopic sends the channel topic to the given client without getting the membersMutex.
361
-// This is required because of channel joins.
362
-func (channel *Channel) getTopicNoMutex(client *Client) {
363
-	if !channel.members.Has(client) {
413
+// SendTopic sends the channel topic to the given client.
414
+func (channel *Channel) SendTopic(client *Client) {
415
+	if !channel.hasClient(client) {
364 416
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, "You're not on that channel")
365 417
 		return
366 418
 	}
367 419
 
368
-	if channel.topic == "" {
369
-		client.Send(nil, client.server.name, RPL_NOTOPIC, client.nick, channel.name, "No topic is set")
420
+	channel.stateMutex.RLock()
421
+	name := channel.name
422
+	topic := channel.topic
423
+	topicSetBy := channel.topicSetBy
424
+	topicSetTime := channel.topicSetTime
425
+	channel.stateMutex.RUnlock()
426
+
427
+	if topic == "" {
428
+		client.Send(nil, client.server.name, RPL_NOTOPIC, client.nick, name, "No topic is set")
370 429
 		return
371 430
 	}
372 431
 
373
-	client.Send(nil, client.server.name, RPL_TOPIC, client.nick, channel.name, channel.topic)
374
-	client.Send(nil, client.server.name, RPL_TOPICTIME, client.nick, channel.name, channel.topicSetBy, strconv.FormatInt(channel.topicSetTime.Unix(), 10))
432
+	client.Send(nil, client.server.name, RPL_TOPIC, client.nick, name, topic)
433
+	client.Send(nil, client.server.name, RPL_TOPICTIME, client.nick, name, topicSetBy, strconv.FormatInt(topicSetTime.Unix(), 10))
375 434
 }
376 435
 
377 436
 // SetTopic sets the topic of this channel, if the client is allowed to do so.
378 437
 func (channel *Channel) SetTopic(client *Client, topic string) {
379
-	channel.membersMutex.RLock()
380
-	defer channel.membersMutex.RUnlock()
381
-
382
-	if !(client.flags[Operator] || channel.members.Has(client)) {
438
+	if !(client.flags[Operator] || channel.hasClient(client)) {
383 439
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
384 440
 		return
385 441
 	}
386 442
 
387
-	if channel.flags[OpOnlyTopic] && !channel.ClientIsAtLeast(client, ChannelOperator) {
443
+	if channel.HasMode(OpOnlyTopic) && !channel.ClientIsAtLeast(client, ChannelOperator) {
388 444
 		client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
389 445
 		return
390 446
 	}
@@ -393,12 +449,14 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
393 449
 		topic = topic[:client.server.limits.TopicLen]
394 450
 	}
395 451
 
452
+	channel.stateMutex.Lock()
396 453
 	channel.topic = topic
397 454
 	channel.topicSetBy = client.nickMaskString
398 455
 	channel.topicSetTime = time.Now()
456
+	channel.stateMutex.Unlock()
399 457
 
400
-	for member := range channel.members {
401
-		member.Send(nil, client.nickMaskString, "TOPIC", channel.name, channel.topic)
458
+	for _, member := range channel.Members() {
459
+		member.Send(nil, client.nickMaskString, "TOPIC", channel.name, topic)
402 460
 	}
403 461
 
404 462
 	// update saved channel topic for registered chans
@@ -422,13 +480,14 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
422 480
 
423 481
 // CanSpeak returns true if the client can speak on this channel.
424 482
 func (channel *Channel) CanSpeak(client *Client) bool {
425
-	channel.membersMutex.RLock()
426
-	defer channel.membersMutex.RUnlock()
483
+	channel.stateMutex.RLock()
484
+	defer channel.stateMutex.RUnlock()
427 485
 
428
-	if channel.flags[NoOutside] && !channel.members.Has(client) {
486
+	_, hasClient := channel.members[client]
487
+	if channel.flags[NoOutside] && !hasClient {
429 488
 		return false
430 489
 	}
431
-	if channel.flags[Moderated] && !channel.clientIsAtLeastNoMutex(client, Voice) {
490
+	if channel.flags[Moderated] && !channel.ClientIsAtLeast(client, Voice) {
432 491
 		return false
433 492
 	}
434 493
 	if channel.flags[RegisteredOnly] && client.account == &NoAccount {
@@ -449,15 +508,12 @@ func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capab
449 508
 		return
450 509
 	}
451 510
 
452
-	channel.membersMutex.RLock()
453
-	defer channel.membersMutex.RUnlock()
454
-
455 511
 	// for STATUSMSG
456 512
 	var minPrefixMode Mode
457 513
 	if minPrefix != nil {
458 514
 		minPrefixMode = *minPrefix
459 515
 	}
460
-	for member := range channel.members {
516
+	for _, member := range channel.Members() {
461 517
 		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
462 518
 			// STATUSMSG
463 519
 			continue
@@ -505,15 +561,12 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *Mode, cli
505 561
 		return
506 562
 	}
507 563
 
508
-	channel.membersMutex.RLock()
509
-	defer channel.membersMutex.RUnlock()
510
-
511 564
 	// for STATUSMSG
512 565
 	var minPrefixMode Mode
513 566
 	if minPrefix != nil {
514 567
 		minPrefixMode = *minPrefix
515 568
 	}
516
-	for member := range channel.members {
569
+	for _, member := range channel.Members() {
517 570
 		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
518 571
 			// STATUSMSG
519 572
 			continue
@@ -534,35 +587,8 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *Mode, cli
534 587
 	}
535 588
 }
536 589
 
537
-func (channel *Channel) applyModeFlag(client *Client, mode Mode,
538
-	op ModeOp) bool {
539
-	if !channel.ClientIsAtLeast(client, ChannelOperator) {
540
-		client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
541
-		return false
542
-	}
543
-
544
-	switch op {
545
-	case Add:
546
-		if channel.flags[mode] {
547
-			return false
548
-		}
549
-		channel.flags[mode] = true
550
-		return true
551
-
552
-	case Remove:
553
-		if !channel.flags[mode] {
554
-			return false
555
-		}
556
-		delete(channel.flags, mode)
557
-		return true
558
-	}
559
-	return false
560
-}
561
-
562 590
 func (channel *Channel) applyModeMemberNoMutex(client *Client, mode Mode,
563 591
 	op ModeOp, nick string) *ModeChange {
564
-	// requires Lock()
565
-
566 592
 	if nick == "" {
567 593
 		//TODO(dan): shouldn't this be handled before it reaches this function?
568 594
 		client.Send(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
@@ -576,35 +602,28 @@ func (channel *Channel) applyModeMemberNoMutex(client *Client, mode Mode,
576 602
 		return nil
577 603
 	}
578 604
 
579
-	if !channel.members.Has(target) {
580
-		client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
581
-		return nil
605
+	channel.stateMutex.Lock()
606
+	modeset, exists := channel.members[target]
607
+	var already bool
608
+	if exists {
609
+		enable := op == Add
610
+		already = modeset[mode] == enable
611
+		modeset[mode] = enable
582 612
 	}
613
+	channel.stateMutex.Unlock()
583 614
 
584
-	switch op {
585
-	case Add:
586
-		if channel.members[target][mode] {
587
-			return nil
588
-		}
589
-		channel.members[target][mode] = true
590
-		return &ModeChange{
591
-			op:   Add,
592
-			mode: mode,
593
-			arg:  nick,
594
-		}
595
-
596
-	case Remove:
597
-		if !channel.members[target][mode] {
598
-			return nil
599
-		}
600
-		channel.members[target][mode] = false
615
+	if !exists {
616
+		client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
617
+		return nil
618
+	} else if already {
619
+		return nil
620
+	} else {
601 621
 		return &ModeChange{
602
-			op:   Remove,
622
+			op:   op,
603 623
 			mode: mode,
604 624
 			arg:  nick,
605 625
 		}
606 626
 	}
607
-	return nil
608 627
 }
609 628
 
610 629
 // ShowMaskList shows the given list to the client.
@@ -622,11 +641,15 @@ func (channel *Channel) ShowMaskList(client *Client, mode Mode) {
622 641
 		rplendoflist = RPL_ENDOFINVITELIST
623 642
 	}
624 643
 
625
-	// send out responses
644
+	nick := client.getNick()
645
+	channel.stateMutex.RLock()
646
+	// XXX don't acquire any new locks in this section, besides Socket.Write
626 647
 	for mask := range channel.lists[mode].masks {
627
-		client.Send(nil, client.server.name, rpllist, client.nick, channel.name, mask)
648
+		client.Send(nil, client.server.name, rpllist, nick, channel.name, mask)
628 649
 	}
629
-	client.Send(nil, client.server.name, rplendoflist, client.nick, channel.name, "End of list")
650
+	channel.stateMutex.RUnlock()
651
+
652
+	client.Send(nil, client.server.name, rplendoflist, nick, channel.name, "End of list")
630 653
 }
631 654
 
632 655
 func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask string) bool {
@@ -657,51 +680,52 @@ func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask
657 680
 	return false
658 681
 }
659 682
 
660
-// Quit removes the given client from the channel, and also updates friends with the latest client list.
661
-func (channel *Channel) Quit(client *Client, friends *ClientSet) {
662
-	channel.membersMutex.Lock()
663
-	defer channel.membersMutex.Unlock()
664
-
665
-	channel.quitNoMutex(client)
666
-
667
-	for friend := range channel.members {
668
-		friends.Add(friend)
669
-	}
670
-}
671
-
672
-func (channel *Channel) quitNoMutex(client *Client) {
683
+// Quit removes the given client from the channel
684
+func (channel *Channel) Quit(client *Client) {
685
+	channel.stateMutex.Lock()
673 686
 	channel.members.Remove(client)
674
-	client.channels.Remove(channel)
687
+	empty := len(channel.members) == 0
688
+	channel.stateMutex.Unlock()
689
+	channel.regenerateMembersCache()
690
+
691
+	client.removeChannel(channel)
675 692
 
676
-	if channel.isEmptyNoMutex() {
693
+	//TODO(slingamn) fold this operation into a channelmanager type
694
+	if empty {
677 695
 		channel.server.channels.Remove(channel)
678 696
 	}
679 697
 }
680 698
 
681
-func (channel *Channel) kickNoMutex(client *Client, target *Client, comment string) {
682
-	// needs a Lock()
683
-
684
-	if !(client.flags[Operator] || channel.members.Has(client)) {
699
+func (channel *Channel) Kick(client *Client, target *Client, comment string) {
700
+	if !(client.flags[Operator] || channel.hasClient(client)) {
685 701
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
686 702
 		return
687 703
 	}
688
-	if !channel.clientIsAtLeastNoMutex(client, ChannelOperator) {
704
+	if !channel.ClientIsAtLeast(client, ChannelOperator) {
689 705
 		client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
690 706
 		return
691 707
 	}
692
-	if !channel.members.Has(target) {
708
+	if !channel.hasClient(target) {
693 709
 		client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
694 710
 		return
695 711
 	}
712
+	if !channel.ClientHasPrivsOver(client, target) {
713
+		client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
714
+		return
715
+	}
696 716
 
697
-	if len(comment) > client.server.limits.KickLen {
698
-		comment = comment[:client.server.limits.KickLen]
717
+	kicklimit := client.server.getLimits().KickLen
718
+	if len(comment) > kicklimit {
719
+		comment = comment[:kicklimit]
699 720
 	}
700 721
 
701
-	for member := range channel.members {
702
-		member.Send(nil, client.nickMaskString, "KICK", channel.name, target.nick, comment)
722
+	clientMask := client.getNickMaskString()
723
+	targetNick := target.getNick()
724
+	for _, member := range channel.Members() {
725
+		member.Send(nil, clientMask, "KICK", channel.name, targetNick, comment)
703 726
 	}
704
-	channel.quitNoMutex(target)
727
+
728
+	channel.Quit(target)
705 729
 }
706 730
 
707 731
 // Invite invites the given client to the channel, if the inviter can do so.
@@ -711,23 +735,22 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
711 735
 		return
712 736
 	}
713 737
 
714
-	channel.membersMutex.RLock()
715
-	defer channel.membersMutex.RUnlock()
716
-
717
-	if !channel.members.Has(inviter) {
738
+	if !channel.hasClient(inviter) {
718 739
 		inviter.Send(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
719 740
 		return
720 741
 	}
721 742
 
722 743
 	//TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list?
723 744
 	if channel.flags[InviteOnly] {
724
-		channel.lists[InviteMask].Add(invitee.nickMaskCasefolded)
745
+		nmc := invitee.getNickCasefolded()
746
+		channel.stateMutex.Lock()
747
+		channel.lists[InviteMask].Add(nmc)
748
+		channel.stateMutex.Unlock()
725 749
 	}
726 750
 
727
-	// send invite-notify
728
-	for member := range channel.members {
751
+	for _, member := range channel.Members() {
729 752
 		if member.capabilities.Has(caps.InviteNotify) && member != inviter && member != invitee && channel.ClientIsAtLeast(member, Halfop) {
730
-			member.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
753
+			member.Send(nil, inviter.getNickMaskString(), "INVITE", invitee.getNick(), channel.name)
731 754
 		}
732 755
 	}
733 756
 

+ 1
- 4
irc/chanserv.go Voir le fichier

@@ -105,16 +105,13 @@ func (server *Server) chanservReceivePrivmsg(client *Client, message string) {
105 105
 			server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
106 106
 			server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
107 107
 
108
-			channelInfo.membersMutex.Lock()
109
-			defer channelInfo.membersMutex.Unlock()
110
-
111 108
 			// give them founder privs
112 109
 			change := channelInfo.applyModeMemberNoMutex(client, ChannelFounder, Add, client.nickCasefolded)
113 110
 			if change != nil {
114 111
 				//TODO(dan): we should change the name of String and make it return a slice here
115 112
 				//TODO(dan): unify this code with code in modes.go
116 113
 				args := append([]string{channelName}, strings.Split(change.String(), " ")...)
117
-				for member := range channelInfo.members {
114
+				for _, member := range channelInfo.Members() {
118 115
 					member.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "MODE", args...)
119 116
 				}
120 117
 			}

+ 34
- 8
irc/client.go Voir le fichier

@@ -14,6 +14,7 @@ import (
14 14
 	"strconv"
15 15
 	"strings"
16 16
 	"sync"
17
+	"sync/atomic"
17 18
 	"time"
18 19
 
19 20
 	"github.com/goshuirc/irc-go/ircfmt"
@@ -55,6 +56,8 @@ type Client struct {
55 56
 	idletimer          *IdleTimer
56 57
 	isDestroyed        bool
57 58
 	isQuitting         bool
59
+	maxlenTags         uint32
60
+	maxlenRest         uint32
58 61
 	nick               string
59 62
 	nickCasefolded     string
60 63
 	nickMaskCasefolded string
@@ -162,7 +165,7 @@ func (client *Client) IPString() string {
162 165
 // command goroutine
163 166
 //
164 167
 
165
-func (client *Client) maxlens() (int, int) {
168
+func (client *Client) recomputeMaxlens() (int, int) {
166 169
 	maxlenTags := 512
167 170
 	maxlenRest := 512
168 171
 	if client.capabilities.Has(caps.MessageTags) {
@@ -175,9 +178,19 @@ func (client *Client) maxlens() (int, int) {
175 178
 		}
176 179
 		maxlenRest = limits.LineLen.Rest
177 180
 	}
181
+
182
+	atomic.StoreUint32(&client.maxlenTags, uint32(maxlenTags))
183
+	atomic.StoreUint32(&client.maxlenRest, uint32(maxlenRest))
184
+
178 185
 	return maxlenTags, maxlenRest
179 186
 }
180 187
 
188
+// allow these negotiated length limits to be read without locks; this is a convenience
189
+// so that Client.Send doesn't have to acquire any Client locks
190
+func (client *Client) maxlens() (int, int) {
191
+	return int(atomic.LoadUint32(&client.maxlenTags)), int(atomic.LoadUint32(&client.maxlenRest))
192
+}
193
+
181 194
 func (client *Client) run() {
182 195
 	var err error
183 196
 	var isExiting bool
@@ -192,14 +205,14 @@ func (client *Client) run() {
192 205
 	client.rawHostname = utils.AddrLookupHostname(client.socket.conn.RemoteAddr())
193 206
 
194 207
 	for {
208
+		maxlenTags, maxlenRest := client.recomputeMaxlens()
209
+
195 210
 		line, err = client.socket.Read()
196 211
 		if err != nil {
197 212
 			client.Quit("connection closed")
198 213
 			break
199 214
 		}
200 215
 
201
-		maxlenTags, maxlenRest := client.maxlens()
202
-
203 216
 		client.server.logger.Debug("userinput ", client.nick, "<- ", line)
204 217
 
205 218
 		msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
@@ -338,9 +351,8 @@ func (client *Client) Friends(capabs ...caps.Capability) ClientSet {
338 351
 		friends.Add(client)
339 352
 	}
340 353
 
341
-	for channel := range client.channels {
342
-		channel.membersMutex.RLock()
343
-		for member := range channel.members {
354
+	for _, channel := range client.Channels() {
355
+		for _, member := range channel.Members() {
344 356
 			// make sure they have all the required caps
345 357
 			hasCaps = true
346 358
 			for _, capab := range capabs {
@@ -353,7 +365,6 @@ func (client *Client) Friends(capabs ...caps.Capability) ClientSet {
353 365
 				friends.Add(member)
354 366
 			}
355 367
 		}
356
-		channel.membersMutex.RUnlock()
357 368
 	}
358 369
 	return friends
359 370
 }
@@ -527,7 +538,10 @@ func (client *Client) destroy() {
527 538
 	// clean up channels
528 539
 	client.server.channelJoinPartMutex.Lock()
529 540
 	for channel := range client.channels {
530
-		channel.Quit(client, &friends)
541
+		channel.Quit(client)
542
+		for _, member := range channel.Members() {
543
+			friends.Add(member)
544
+		}
531 545
 	}
532 546
 	client.server.channelJoinPartMutex.Unlock()
533 547
 
@@ -663,3 +677,15 @@ func (client *Client) Notice(text string) {
663 677
 		client.Send(nil, client.server.name, "NOTICE", client.nick, line)
664 678
 	}
665 679
 }
680
+
681
+func (client *Client) addChannel(channel *Channel) {
682
+	client.stateMutex.Lock()
683
+	client.channels[channel] = true
684
+	client.stateMutex.Unlock()
685
+}
686
+
687
+func (client *Client) removeChannel(channel *Channel) {
688
+	client.stateMutex.Lock()
689
+	delete(client.channels, channel)
690
+	client.stateMutex.Unlock()
691
+}

+ 50
- 16
irc/client_lookup_set.go Voir le fichier

@@ -227,6 +227,7 @@ func (clients *ClientLookupSet) Find(userhost string) *Client {
227 227
 
228 228
 // UserMaskSet holds a set of client masks and lets you match  hostnames to them.
229 229
 type UserMaskSet struct {
230
+	sync.RWMutex
230 231
 	masks  map[string]bool
231 232
 	regexp *regexp.Regexp
232 233
 }
@@ -245,16 +246,25 @@ func (set *UserMaskSet) Add(mask string) bool {
245 246
 		log.Println(fmt.Sprintf("ERROR: Could not add mask to usermaskset: [%s]", mask))
246 247
 		return false
247 248
 	}
248
-	if set.masks[casefoldedMask] {
249
+
250
+	set.Lock()
251
+	already := set.masks[casefoldedMask]
252
+	set.masks[casefoldedMask] = true
253
+	set.Unlock()
254
+
255
+	if already {
249 256
 		return false
257
+	} else {
258
+		set.setRegexp()
259
+		return true
250 260
 	}
251
-	set.masks[casefoldedMask] = true
252
-	set.setRegexp()
253
-	return true
254 261
 }
255 262
 
256 263
 // AddAll adds the given masks to this set.
257 264
 func (set *UserMaskSet) AddAll(masks []string) (added bool) {
265
+	set.Lock()
266
+	defer set.Unlock()
267
+
258 268
 	for _, mask := range masks {
259 269
 		if !added && !set.masks[mask] {
260 270
 			added = true
@@ -267,33 +277,50 @@ func (set *UserMaskSet) AddAll(masks []string) (added bool) {
267 277
 
268 278
 // Remove removes the given mask from this set.
269 279
 func (set *UserMaskSet) Remove(mask string) bool {
270
-	if !set.masks[mask] {
280
+	set.Lock()
281
+	already := !set.masks[mask]
282
+	delete(set.masks, mask)
283
+	set.Unlock()
284
+
285
+	if !already {
271 286
 		return false
287
+	} else {
288
+		set.setRegexp()
289
+		return true
272 290
 	}
273
-	delete(set.masks, mask)
274
-	set.setRegexp()
275
-	return true
276 291
 }
277 292
 
278 293
 // Match matches the given n!u@h.
279 294
 func (set *UserMaskSet) Match(userhost string) bool {
280
-	if set.regexp == nil {
295
+	set.RLock()
296
+	regexp := set.regexp
297
+	set.RUnlock()
298
+
299
+	if regexp == nil {
281 300
 		return false
282 301
 	}
283
-	return set.regexp.MatchString(userhost)
302
+	return regexp.MatchString(userhost)
284 303
 }
285 304
 
286 305
 // String returns the masks in this set.
287 306
 func (set *UserMaskSet) String() string {
307
+	set.RLock()
288 308
 	masks := make([]string, len(set.masks))
289 309
 	index := 0
290 310
 	for mask := range set.masks {
291 311
 		masks[index] = mask
292 312
 		index++
293 313
 	}
314
+	set.RUnlock()
294 315
 	return strings.Join(masks, " ")
295 316
 }
296 317
 
318
+func (set *UserMaskSet) Length() int {
319
+	set.RLock()
320
+	defer set.RUnlock()
321
+	return len(set.masks)
322
+}
323
+
297 324
 // setRegexp generates a regular expression from the set of user mask
298 325
 // strings. Masks are split at the two types of wildcards, `*` and
299 326
 // `?`. All the pieces are meta-escaped. `*` is replaced with `.*`,
@@ -301,11 +328,9 @@ func (set *UserMaskSet) String() string {
301 328
 // parts are re-joined and finally all masks are joined into a big
302 329
 // or-expression.
303 330
 func (set *UserMaskSet) setRegexp() {
304
-	if len(set.masks) == 0 {
305
-		set.regexp = nil
306
-		return
307
-	}
331
+	var re *regexp.Regexp
308 332
 
333
+	set.RLock()
309 334
 	maskExprs := make([]string, len(set.masks))
310 335
 	index := 0
311 336
 	for mask := range set.masks {
@@ -320,7 +345,16 @@ func (set *UserMaskSet) setRegexp() {
320 345
 			manyExprs[mindex] = strings.Join(oneExprs, ".")
321 346
 		}
322 347
 		maskExprs[index] = strings.Join(manyExprs, ".*")
348
+		index++
323 349
 	}
324
-	expr := "^" + strings.Join(maskExprs, "|") + "$"
325
-	set.regexp, _ = regexp.Compile(expr)
350
+	set.RUnlock()
351
+
352
+	if index > 0 {
353
+		expr := "^" + strings.Join(maskExprs, "|") + "$"
354
+		re, _ = regexp.Compile(expr)
355
+	}
356
+
357
+	set.Lock()
358
+	set.regexp = re
359
+	set.Unlock()
326 360
 }

+ 94
- 0
irc/getters.go Voir le fichier

@@ -53,6 +53,24 @@ func (client *Client) getNickCasefolded() string {
53 53
 	return client.nickCasefolded
54 54
 }
55 55
 
56
+func (client *Client) Username() string {
57
+	client.stateMutex.RLock()
58
+	defer client.stateMutex.RUnlock()
59
+	return client.username
60
+}
61
+
62
+func (client *Client) Hostname() string {
63
+	client.stateMutex.RLock()
64
+	defer client.stateMutex.RUnlock()
65
+	return client.hostname
66
+}
67
+
68
+func (client *Client) Realname() string {
69
+	client.stateMutex.RLock()
70
+	defer client.stateMutex.RUnlock()
71
+	return client.realname
72
+}
73
+
56 74
 func (client *Client) Registered() bool {
57 75
 	client.stateMutex.RLock()
58 76
 	defer client.stateMutex.RUnlock()
@@ -64,3 +82,79 @@ func (client *Client) Destroyed() bool {
64 82
 	defer client.stateMutex.RUnlock()
65 83
 	return client.isDestroyed
66 84
 }
85
+
86
+func (client *Client) HasMode(mode Mode) bool {
87
+	client.stateMutex.RLock()
88
+	defer client.stateMutex.RUnlock()
89
+	return client.flags[mode]
90
+}
91
+
92
+func (client *Client) Channels() (result []*Channel) {
93
+	client.stateMutex.RLock()
94
+	defer client.stateMutex.RUnlock()
95
+	length := len(client.channels)
96
+	result = make([]*Channel, length)
97
+	i := 0
98
+	for channel := range client.channels {
99
+		result[i] = channel
100
+		i++
101
+	}
102
+	return
103
+}
104
+
105
+func (channel *Channel) Name() string {
106
+	channel.stateMutex.RLock()
107
+	defer channel.stateMutex.RUnlock()
108
+	return channel.name
109
+}
110
+
111
+func (channel *Channel) Members() (result []*Client) {
112
+	channel.stateMutex.RLock()
113
+	defer channel.stateMutex.RUnlock()
114
+	return channel.membersCache
115
+}
116
+
117
+func (channel *Channel) UserLimit() uint64 {
118
+	channel.stateMutex.RLock()
119
+	defer channel.stateMutex.RUnlock()
120
+	return channel.userLimit
121
+}
122
+
123
+func (channel *Channel) setUserLimit(limit uint64) {
124
+	channel.stateMutex.Lock()
125
+	channel.userLimit = limit
126
+	channel.stateMutex.Unlock()
127
+}
128
+
129
+func (channel *Channel) Key() string {
130
+	channel.stateMutex.RLock()
131
+	defer channel.stateMutex.RUnlock()
132
+	return channel.key
133
+}
134
+
135
+func (channel *Channel) setKey(key string) {
136
+	channel.stateMutex.Lock()
137
+	channel.key = key
138
+	channel.stateMutex.Unlock()
139
+}
140
+
141
+func (channel *Channel) HasMode(mode Mode) bool {
142
+	channel.stateMutex.RLock()
143
+	defer channel.stateMutex.RUnlock()
144
+	return channel.flags[mode]
145
+}
146
+
147
+// set a channel mode, return whether it was already set
148
+func (channel *Channel) setMode(mode Mode, enable bool) (already bool) {
149
+	channel.stateMutex.Lock()
150
+	already = (channel.flags[mode] == enable)
151
+	if !already {
152
+		if enable {
153
+			channel.flags[mode] = true
154
+		} else {
155
+			delete(channel.flags, mode)
156
+		}
157
+	}
158
+	channel.stateMutex.Unlock()
159
+	return
160
+}

+ 25
- 51
irc/modes.go Voir le fichier

@@ -475,11 +475,11 @@ func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
475 475
 }
476 476
 
477 477
 // ApplyChannelModeChanges applies a given set of mode changes.
478
-func ApplyChannelModeChanges(channel *Channel, client *Client, isSamode bool, changes ModeChanges) ModeChanges {
478
+func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes ModeChanges) ModeChanges {
479 479
 	// so we only output one warning for each list type when full
480 480
 	listFullWarned := make(map[Mode]bool)
481 481
 
482
-	clientIsOp := channel.clientIsAtLeastNoMutex(client, ChannelOperator)
482
+	clientIsOp := channel.ClientIsAtLeast(client, ChannelOperator)
483 483
 	var alreadySentPrivError bool
484 484
 
485 485
 	applied := make(ModeChanges, 0)
@@ -498,12 +498,6 @@ func ApplyChannelModeChanges(channel *Channel, client *Client, isSamode bool, ch
498 498
 		switch change.mode {
499 499
 		case BanMask, ExceptMask, InviteMask:
500 500
 			mask := change.arg
501
-			list := channel.lists[change.mode]
502
-			if list == nil {
503
-				// This should never happen, but better safe than panicky.
504
-				client.Send(nil, client.server.name, ERR_UNKNOWNERROR, client.nick, "MODE", "Could not complete MODE command")
505
-				return changes
506
-			}
507 501
 
508 502
 			if (change.op == List) || (mask == "") {
509 503
 				channel.ShowMaskList(client, change.mode)
@@ -518,19 +512,19 @@ func ApplyChannelModeChanges(channel *Channel, client *Client, isSamode bool, ch
518 512
 
519 513
 			switch change.op {
520 514
 			case Add:
521
-				if len(list.masks) >= client.server.limits.ChanListModes {
515
+				if channel.lists[change.mode].Length() >= client.server.getLimits().ChanListModes {
522 516
 					if !listFullWarned[change.mode] {
523
-						client.Send(nil, client.server.name, ERR_BANLISTFULL, client.nick, channel.name, change.mode.String(), "Channel list is full")
517
+						client.Send(nil, client.server.name, ERR_BANLISTFULL, client.getNick(), channel.Name(), change.mode.String(), "Channel list is full")
524 518
 						listFullWarned[change.mode] = true
525 519
 					}
526 520
 					continue
527 521
 				}
528 522
 
529
-				list.Add(mask)
523
+				channel.lists[change.mode].Add(mask)
530 524
 				applied = append(applied, change)
531 525
 
532 526
 			case Remove:
533
-				list.Remove(mask)
527
+				channel.lists[change.mode].Remove(mask)
534 528
 				applied = append(applied, change)
535 529
 			}
536 530
 
@@ -539,62 +533,46 @@ func ApplyChannelModeChanges(channel *Channel, client *Client, isSamode bool, ch
539 533
 			case Add:
540 534
 				val, err := strconv.ParseUint(change.arg, 10, 64)
541 535
 				if err == nil {
542
-					channel.userLimit = val
536
+					channel.setUserLimit(val)
543 537
 					applied = append(applied, change)
544 538
 				}
545 539
 
546 540
 			case Remove:
547
-				channel.userLimit = 0
541
+				channel.setUserLimit(0)
548 542
 				applied = append(applied, change)
549 543
 			}
550 544
 
551 545
 		case Key:
552 546
 			switch change.op {
553 547
 			case Add:
554
-				channel.key = change.arg
548
+				channel.setKey(change.arg)
555 549
 
556 550
 			case Remove:
557
-				channel.key = ""
551
+				channel.setKey("")
558 552
 			}
559 553
 			applied = append(applied, change)
560 554
 
561 555
 		case InviteOnly, Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, Secret, ChanRoleplaying:
562
-			switch change.op {
563
-			case Add:
564
-				if channel.flags[change.mode] {
565
-					continue
566
-				}
567
-				channel.flags[change.mode] = true
568
-				applied = append(applied, change)
556
+			if change.op == List {
557
+				continue
558
+			}
569 559
 
570
-			case Remove:
571
-				if !channel.flags[change.mode] {
572
-					continue
573
-				}
574
-				delete(channel.flags, change.mode)
560
+			already := channel.setMode(change.mode, change.op == Add)
561
+			if !already {
575 562
 				applied = append(applied, change)
576 563
 			}
577 564
 
578 565
 		case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
566
+			if change.op == List {
567
+				continue
568
+			}
579 569
 			// make sure client has privs to edit the given prefix
580 570
 			hasPrivs := isSamode
581 571
 
582
-			if !hasPrivs {
583
-				for _, mode := range ChannelPrivModes {
584
-					if channel.members[client][mode] {
585
-						hasPrivs = true
586
-
587
-						// Admins can't give other people Admin or remove it from others,
588
-						// standard for that channel mode, we worry about this later
589
-						if mode == ChannelAdmin && change.mode == ChannelAdmin {
590
-							hasPrivs = false
591
-						}
592
-
593
-						break
594
-					} else if mode == change.mode {
595
-						break
596
-					}
597
-				}
572
+			// Admins can't give other people Admin or remove it from others,
573
+			// standard for that channel mode, we worry about this later
574
+			if !hasPrivs && change.mode != ChannelAdmin {
575
+				hasPrivs = channel.ClientIsAtLeast(client, change.mode)
598 576
 			}
599 577
 
600 578
 			casefoldedName, err := CasefoldName(change.arg)
@@ -634,9 +612,6 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
634 612
 		return false
635 613
 	}
636 614
 
637
-	channel.membersMutex.Lock()
638
-	defer channel.membersMutex.Unlock()
639
-
640 615
 	// applied mode changes
641 616
 	applied := make(ModeChanges, 0)
642 617
 
@@ -654,7 +629,7 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
654 629
 		}
655 630
 
656 631
 		// apply mode changes
657
-		applied = ApplyChannelModeChanges(channel, client, msg.Command == "SAMODE", changes)
632
+		applied = channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes)
658 633
 	}
659 634
 
660 635
 	// save changes to banlist/exceptlist/invexlist
@@ -707,12 +682,11 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
707 682
 	if len(applied) > 0 {
708 683
 		//TODO(dan): we should change the name of String and make it return a slice here
709 684
 		args := append([]string{channel.name}, strings.Split(applied.String(), " ")...)
710
-		for member := range channel.members {
685
+		for _, member := range channel.Members() {
711 686
 			member.Send(nil, client.nickMaskString, "MODE", args...)
712 687
 		}
713 688
 	} else {
714
-		//TODO(dan): we should just make ModeString return a slice here
715
-		args := append([]string{client.nick, channel.name}, strings.Split(channel.modeStringNoLock(client), " ")...)
689
+		args := append([]string{client.nick, channel.name}, channel.modeStrings(client)...)
716 690
 		client.Send(nil, client.nickMaskString, RPL_CHANNELMODEIS, args...)
717 691
 		client.Send(nil, client.nickMaskString, RPL_CHANNELCREATED, client.nick, channel.name, strconv.FormatInt(channel.createdTime.Unix(), 10))
718 692
 	}

+ 1
- 3
irc/roleplay.go Voir le fichier

@@ -88,14 +88,12 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
88 88
 			return
89 89
 		}
90 90
 
91
-		channel.membersMutex.RLock()
92
-		for member := range channel.members {
91
+		for _, member := range channel.Members() {
93 92
 			if member == client && !client.capabilities.Has(caps.EchoMessage) {
94 93
 				continue
95 94
 			}
96 95
 			member.Send(nil, source, "PRIVMSG", channel.name, message)
97 96
 		}
98
-		channel.membersMutex.RUnlock()
99 97
 	} else {
100 98
 		target, err := CasefoldName(targetString)
101 99
 		user := server.clients.Get(target)

+ 23
- 67
irc/server.go Voir le fichier

@@ -587,9 +587,6 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
587 587
 		return false
588 588
 	}
589 589
 
590
-	channel.membersMutex.Lock()
591
-	defer channel.membersMutex.Unlock()
592
-
593 590
 	casefoldedNewName, err := CasefoldChannel(newName)
594 591
 	if err != nil {
595 592
 		//TODO(dan): Change this to ERR_CANNOTRENAME
@@ -646,7 +643,7 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
646 643
 	})
647 644
 
648 645
 	// send RENAME messages
649
-	for mcl := range channel.members {
646
+	for _, mcl := range channel.Members() {
650 647
 		if mcl.capabilities.Has(caps.Rename) {
651 648
 			mcl.Send(nil, client.nickMaskString, "RENAME", oldName, newName, reason)
652 649
 		} else {
@@ -755,7 +752,7 @@ func topicHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
755 752
 	if len(msg.Params) > 1 {
756 753
 		channel.SetTopic(client, msg.Params[1])
757 754
 	} else {
758
-		channel.GetTopic(client)
755
+		channel.SendTopic(client)
759 756
 	}
760 757
 	return false
761 758
 }
@@ -964,17 +961,12 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
964 961
 func (client *Client) WhoisChannelsNames(target *Client) []string {
965 962
 	isMultiPrefix := target.capabilities.Has(caps.MultiPrefix)
966 963
 	var chstrs []string
967
-	index := 0
968
-	for channel := range client.channels {
969
-		channel.membersMutex.RLock()
970
-		defer channel.membersMutex.RUnlock()
971
-
964
+	for _, channel := range client.Channels() {
972 965
 		// channel is secret and the target can't see it
973
-		if !target.flags[Operator] && channel.flags[Secret] && !channel.members.Has(target) {
966
+		if !target.flags[Operator] && channel.HasMode(Secret) && !channel.hasClient(target) {
974 967
 			continue
975 968
 		}
976
-		chstrs = append(chstrs, channel.members[client].Prefixes(isMultiPrefix)+channel.name)
977
-		index++
969
+		chstrs = append(chstrs, channel.ClientPrefixes(client, isMultiPrefix)+channel.name)
978 970
 	}
979 971
 	return chstrs
980 972
 }
@@ -1050,36 +1042,33 @@ func (client *Client) getWhoisOf(target *Client) {
1050 1042
 	client.Send(nil, client.server.name, RPL_WHOISIDLE, client.nick, target.nick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), "seconds idle, signon time")
1051 1043
 }
1052 1044
 
1053
-// RplWhoReplyNoMutex returns the WHO reply between one user and another channel/user.
1045
+// rplWhoReply returns the WHO reply between one user and another channel/user.
1054 1046
 // <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
1055 1047
 // :<hopcount> <real name>
1056
-func (target *Client) RplWhoReplyNoMutex(channel *Channel, client *Client) {
1048
+func (target *Client) rplWhoReply(channel *Channel, client *Client) {
1057 1049
 	channelName := "*"
1058 1050
 	flags := ""
1059 1051
 
1060
-	if client.flags[Away] {
1052
+	if client.HasMode(Away) {
1061 1053
 		flags = "G"
1062 1054
 	} else {
1063 1055
 		flags = "H"
1064 1056
 	}
1065
-	if client.flags[Operator] {
1057
+	if client.HasMode(Operator) {
1066 1058
 		flags += "*"
1067 1059
 	}
1068 1060
 
1069 1061
 	if channel != nil {
1070
-		flags += channel.members[client].Prefixes(target.capabilities.Has(caps.MultiPrefix))
1062
+		flags += channel.ClientPrefixes(client, target.capabilities.Has(caps.MultiPrefix))
1071 1063
 		channelName = channel.name
1072 1064
 	}
1073
-	target.Send(nil, target.server.name, RPL_WHOREPLY, target.nick, channelName, client.username, client.hostname, client.server.name, client.nick, flags, strconv.Itoa(client.hops)+" "+client.realname)
1065
+	target.Send(nil, target.server.name, RPL_WHOREPLY, target.nick, channelName, client.Username(), client.Hostname(), client.server.name, client.getNick(), flags, strconv.Itoa(client.hops)+" "+client.Realname())
1074 1066
 }
1075 1067
 
1076 1068
 func whoChannel(client *Client, channel *Channel, friends ClientSet) {
1077
-	channel.membersMutex.RLock()
1078
-	defer channel.membersMutex.RUnlock()
1079
-
1080
-	for member := range channel.members {
1069
+	for _, member := range channel.Members() {
1081 1070
 		if !client.flags[Invisible] || friends[client] {
1082
-			client.RplWhoReplyNoMutex(channel, member)
1071
+			client.rplWhoReply(channel, member)
1083 1072
 		}
1084 1073
 	}
1085 1074
 }
@@ -1120,7 +1109,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1120 1109
 		}
1121 1110
 	} else {
1122 1111
 		for mclient := range server.clients.FindAll(mask) {
1123
-			client.RplWhoReplyNoMutex(nil, mclient)
1112
+			client.rplWhoReply(nil, mclient)
1124 1113
 		}
1125 1114
 	}
1126 1115
 
@@ -1792,37 +1781,10 @@ func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1792 1781
 			continue
1793 1782
 		}
1794 1783
 
1795
-		// make sure client has privs to kick the given user
1796
-		//TODO(dan): split this into a separate function that checks if users have privs
1797
-		// over other users, useful for things like -aoh as well
1798
-		channel.membersMutex.Lock()
1799
-
1800
-		var hasPrivs bool
1801
-		for _, mode := range ChannelPrivModes {
1802
-			if channel.members[client][mode] {
1803
-				hasPrivs = true
1804
-
1805
-				// admins cannot kick other admins
1806
-				if mode == ChannelAdmin && channel.members[target][ChannelAdmin] {
1807
-					hasPrivs = false
1808
-				}
1809
-
1810
-				break
1811
-			} else if channel.members[target][mode] {
1812
-				break
1813
-			}
1784
+		if comment == "" {
1785
+			comment = nickname
1814 1786
 		}
1815
-
1816
-		if hasPrivs {
1817
-			if comment == "" {
1818
-				comment = nickname
1819
-			}
1820
-			channel.kickNoMutex(client, target, comment)
1821
-		} else {
1822
-			client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, chname, "You're not a channel operator")
1823
-		}
1824
-
1825
-		channel.membersMutex.Unlock()
1787
+		channel.Kick(client, target, comment)
1826 1788
 	}
1827 1789
 	return false
1828 1790
 }
@@ -1837,17 +1799,14 @@ type elistMatcher struct {
1837 1799
 
1838 1800
 // Matches checks whether the given channel matches our matches.
1839 1801
 func (matcher *elistMatcher) Matches(channel *Channel) bool {
1840
-	channel.membersMutex.RLock()
1841
-	defer channel.membersMutex.RUnlock()
1842
-
1843 1802
 	if matcher.MinClientsActive {
1844
-		if len(channel.members) < matcher.MinClients {
1803
+		if len(channel.Members()) < matcher.MinClients {
1845 1804
 			return false
1846 1805
 		}
1847 1806
 	}
1848 1807
 
1849 1808
 	if matcher.MaxClientsActive {
1850
-		if matcher.MaxClients < len(channel.members) {
1809
+		if len(channel.Members()) < len(channel.members) {
1851 1810
 			return false
1852 1811
 		}
1853 1812
 	}
@@ -1933,16 +1892,13 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1933 1892
 
1934 1893
 // RplList returns the RPL_LIST numeric for the given channel.
1935 1894
 func (target *Client) RplList(channel *Channel) {
1936
-	channel.membersMutex.RLock()
1937
-	defer channel.membersMutex.RUnlock()
1938
-
1939 1895
 	// get the correct number of channel members
1940 1896
 	var memberCount int
1941
-	if target.flags[Operator] || channel.members.Has(target) {
1942
-		memberCount = len(channel.members)
1897
+	if target.flags[Operator] || channel.hasClient(target) {
1898
+		memberCount = len(channel.Members())
1943 1899
 	} else {
1944
-		for member := range channel.members {
1945
-			if !member.flags[Invisible] {
1900
+		for _, member := range channel.Members() {
1901
+			if !member.HasMode(Invisible) {
1946 1902
 				memberCount++
1947 1903
 			}
1948 1904
 		}

+ 0
- 10
irc/types.go Voir le fichier

@@ -139,13 +139,3 @@ func (members MemberSet) AnyHasMode(mode Mode) bool {
139 139
 
140 140
 // ChannelSet is a set of channels.
141 141
 type ChannelSet map[*Channel]bool
142
-
143
-// Add adds the given channel to this set.
144
-func (channels ChannelSet) Add(channel *Channel) {
145
-	channels[channel] = true
146
-}
147
-
148
-// Remove removes the given channel from this set.
149
-func (channels ChannelSet) Remove(channel *Channel) {
150
-	delete(channels, channel)
151
-}

Chargement…
Annuler
Enregistrer