瀏覽代碼

remove channelJoinPartMutex

tags/v0.10.1
Shivaram Lingamneni 6 年之前
父節點
當前提交
94cf438f51
共有 7 個檔案被更改,包括 260 行新增162 行删除
  1. 8
    11
      irc/channel.go
  2. 162
    0
      irc/channelmanager.go
  3. 1
    3
      irc/client.go
  4. 24
    0
      irc/getters.go
  5. 3
    6
      irc/monitor.go
  6. 62
    87
      irc/server.go
  7. 0
    55
      irc/types.go

+ 8
- 11
irc/channel.go 查看文件

@@ -7,7 +7,6 @@ package irc
7 7
 
8 8
 import (
9 9
 	"fmt"
10
-	"log"
11 10
 	"strconv"
12 11
 	"time"
13 12
 
@@ -41,7 +40,7 @@ type Channel struct {
41 40
 func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
42 41
 	casefoldedName, err := CasefoldChannel(name)
43 42
 	if err != nil {
44
-		log.Println(fmt.Sprintf("ERROR: Channel name is bad: [%s]", name), err.Error())
43
+		s.logger.Error("internal", fmt.Sprintf("Bad channel name %s: %v", name, err))
45 44
 		return nil
46 45
 	}
47 46
 
@@ -59,13 +58,11 @@ func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
59 58
 	}
60 59
 
61 60
 	if addDefaultModes {
62
-		for _, mode := range s.GetDefaultChannelModes() {
61
+		for _, mode := range s.DefaultChannelModes() {
63 62
 			channel.flags[mode] = true
64 63
 		}
65 64
 	}
66 65
 
67
-	s.channels.Add(channel)
68
-
69 66
 	return channel
70 67
 }
71 68
 
@@ -281,6 +278,12 @@ func (channel *Channel) CheckKey(key string) bool {
281 278
 	return (channel.key == "") || (channel.key == key)
282 279
 }
283 280
 
281
+func (channel *Channel) IsEmpty() bool {
282
+	channel.stateMutex.RLock()
283
+	defer channel.stateMutex.RUnlock()
284
+	return len(channel.members) == 0
285
+}
286
+
284 287
 // Join joins the given client to this channel (if they can be joined).
285 288
 //TODO(dan): /SAJOIN and maybe a ForceJoin function?
286 289
 func (channel *Channel) Join(client *Client, key string) {
@@ -684,16 +687,10 @@ func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask
684 687
 func (channel *Channel) Quit(client *Client) {
685 688
 	channel.stateMutex.Lock()
686 689
 	channel.members.Remove(client)
687
-	empty := len(channel.members) == 0
688 690
 	channel.stateMutex.Unlock()
689 691
 	channel.regenerateMembersCache()
690 692
 
691 693
 	client.removeChannel(channel)
692
-
693
-	//TODO(slingamn) fold this operation into a channelmanager type
694
-	if empty {
695
-		channel.server.channels.Remove(channel)
696
-	}
697 694
 }
698 695
 
699 696
 func (channel *Channel) Kick(client *Client, target *Client, comment string) {

+ 162
- 0
irc/channelmanager.go 查看文件

@@ -0,0 +1,162 @@
1
+// Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
2
+// released under the MIT license
3
+
4
+package irc
5
+
6
+import (
7
+	"errors"
8
+	"sync"
9
+)
10
+
11
+var (
12
+	InvalidChannelName = errors.New("Invalid channel name")
13
+	NoSuchChannel      = errors.New("No such channel")
14
+	ChannelNameInUse   = errors.New("Channel name in use")
15
+)
16
+
17
+type channelManagerEntry struct {
18
+	channel *Channel
19
+	// this is a refcount for joins, so we can avoid a race where we incorrectly
20
+	// think the channel is empty (without holding a lock across the entire Channel.Join()
21
+	// call)
22
+	pendingJoins int
23
+}
24
+
25
+// ChannelManager keeps track of all the channels on the server,
26
+// providing synchronization for creation of new channels on first join,
27
+// cleanup of empty channels on last part, and renames.
28
+type ChannelManager struct {
29
+	sync.RWMutex // tier 2
30
+	chans        map[string]*channelManagerEntry
31
+}
32
+
33
+// NewChannelManager returns a new ChannelManager.
34
+func NewChannelManager() *ChannelManager {
35
+	return &ChannelManager{
36
+		chans: make(map[string]*channelManagerEntry),
37
+	}
38
+}
39
+
40
+// Get returns an existing channel with name equivalent to `name`, or nil
41
+func (cm *ChannelManager) Get(name string) *Channel {
42
+	name, err := CasefoldChannel(name)
43
+	if err == nil {
44
+		cm.RLock()
45
+		defer cm.RUnlock()
46
+		return cm.chans[name].channel
47
+	}
48
+	return nil
49
+}
50
+
51
+// Join causes `client` to join the channel named `name`, creating it if necessary.
52
+func (cm *ChannelManager) Join(client *Client, name string, key string) error {
53
+	server := client.server
54
+	casefoldedName, err := CasefoldChannel(name)
55
+	if err != nil || len(casefoldedName) > server.getLimits().ChannelLen {
56
+		return NoSuchChannel
57
+	}
58
+
59
+	cm.Lock()
60
+	entry := cm.chans[casefoldedName]
61
+	if entry == nil {
62
+		entry = &channelManagerEntry{
63
+			channel:      NewChannel(server, name, true),
64
+			pendingJoins: 0,
65
+		}
66
+		cm.chans[casefoldedName] = entry
67
+	}
68
+	entry.pendingJoins += 1
69
+	cm.Unlock()
70
+
71
+	entry.channel.Join(client, key)
72
+
73
+	cm.maybeCleanup(entry, true)
74
+
75
+	return nil
76
+}
77
+
78
+func (cm *ChannelManager) maybeCleanup(entry *channelManagerEntry, afterJoin bool) {
79
+	cm.Lock()
80
+	defer cm.Unlock()
81
+
82
+	if entry.channel == nil {
83
+		return
84
+	}
85
+	if afterJoin {
86
+		entry.pendingJoins -= 1
87
+	}
88
+	if entry.channel.IsEmpty() && entry.pendingJoins == 0 {
89
+		// reread the name, handling the case where the channel was renamed
90
+		casefoldedName := entry.channel.NameCasefolded()
91
+		delete(cm.chans, casefoldedName)
92
+		// invalidate the entry (otherwise, a subsequent cleanup attempt could delete
93
+		// a valid, distinct entry under casefoldedName):
94
+		entry.channel = nil
95
+	}
96
+}
97
+
98
+// Part parts `client` from the channel named `name`, deleting it if it's empty.
99
+func (cm *ChannelManager) Part(client *Client, name string, message string) error {
100
+	casefoldedName, err := CasefoldChannel(name)
101
+	if err != nil {
102
+		return NoSuchChannel
103
+	}
104
+
105
+	cm.RLock()
106
+	entry := cm.chans[casefoldedName]
107
+	cm.RUnlock()
108
+
109
+	if entry == nil {
110
+		return NoSuchChannel
111
+	}
112
+	entry.channel.Part(client, message)
113
+	cm.maybeCleanup(entry, false)
114
+	return nil
115
+}
116
+
117
+// Rename renames a channel (but does not notify the members)
118
+func (cm *ChannelManager) Rename(name string, newname string) error {
119
+	cfname, err := CasefoldChannel(name)
120
+	if err != nil {
121
+		return NoSuchChannel
122
+	}
123
+
124
+	cfnewname, err := CasefoldChannel(newname)
125
+	if err != nil {
126
+		return InvalidChannelName
127
+	}
128
+
129
+	cm.Lock()
130
+	defer cm.Unlock()
131
+
132
+	if cm.chans[cfnewname] != nil {
133
+		return ChannelNameInUse
134
+	}
135
+	entry := cm.chans[cfname]
136
+	if entry == nil {
137
+		return NoSuchChannel
138
+	}
139
+	delete(cm.chans, cfname)
140
+	cm.chans[cfnewname] = entry
141
+	entry.channel.setName(newname)
142
+	entry.channel.setNameCasefolded(cfnewname)
143
+	return nil
144
+
145
+}
146
+
147
+// Len returns the number of channels
148
+func (cm *ChannelManager) Len() int {
149
+	cm.RLock()
150
+	defer cm.RUnlock()
151
+	return len(cm.chans)
152
+}
153
+
154
+// Channels returns a slice containing all current channels
155
+func (cm *ChannelManager) Channels() (result []*Channel) {
156
+	cm.RLock()
157
+	defer cm.RUnlock()
158
+	for _, entry := range cm.chans {
159
+		result = append(result, entry.channel)
160
+	}
161
+	return
162
+}

+ 1
- 3
irc/client.go 查看文件

@@ -548,14 +548,12 @@ func (client *Client) destroy() {
548 548
 	client.server.monitorManager.RemoveAll(client)
549 549
 
550 550
 	// clean up channels
551
-	client.server.channelJoinPartMutex.Lock()
552
-	for channel := range client.channels {
551
+	for _, channel := range client.Channels() {
553 552
 		channel.Quit(client)
554 553
 		for _, member := range channel.Members() {
555 554
 			friends.Add(member)
556 555
 		}
557 556
 	}
558
-	client.server.channelJoinPartMutex.Unlock()
559 557
 
560 558
 	// clean up server
561 559
 	client.server.clients.Remove(client)

+ 24
- 0
irc/getters.go 查看文件

@@ -41,6 +41,12 @@ func (server *Server) WebIRCConfig() []webircConfig {
41 41
 	return server.webirc
42 42
 }
43 43
 
44
+func (server *Server) DefaultChannelModes() Modes {
45
+	server.configurableStateMutex.RLock()
46
+	defer server.configurableStateMutex.RUnlock()
47
+	return server.defaultChannelModes
48
+}
49
+
44 50
 func (client *Client) getNick() string {
45 51
 	client.stateMutex.RLock()
46 52
 	defer client.stateMutex.RUnlock()
@@ -114,6 +120,24 @@ func (channel *Channel) Name() string {
114 120
 	return channel.name
115 121
 }
116 122
 
123
+func (channel *Channel) setName(name string) {
124
+	channel.stateMutex.Lock()
125
+	defer channel.stateMutex.Unlock()
126
+	channel.name = name
127
+}
128
+
129
+func (channel *Channel) NameCasefolded() string {
130
+	channel.stateMutex.RLock()
131
+	defer channel.stateMutex.RUnlock()
132
+	return channel.nameCasefolded
133
+}
134
+
135
+func (channel *Channel) setNameCasefolded(nameCasefolded string) {
136
+	channel.stateMutex.Lock()
137
+	defer channel.stateMutex.Unlock()
138
+	channel.nameCasefolded = nameCasefolded
139
+}
140
+
117 141
 func (channel *Channel) Members() (result []*Client) {
118 142
 	channel.stateMutex.RLock()
119 143
 	defer channel.stateMutex.RUnlock()

+ 3
- 6
irc/monitor.go 查看文件

@@ -52,12 +52,9 @@ func (manager *MonitorManager) AlertAbout(client *Client, online bool) {
52 52
 		command = RPL_MONONLINE
53 53
 	}
54 54
 
55
-	// asynchronously send all the notifications
56
-	go func() {
57
-		for _, mClient := range watchers {
58
-			mClient.Send(nil, client.server.name, command, mClient.getNick(), nick)
59
-		}
60
-	}()
55
+	for _, mClient := range watchers {
56
+		mClient.Send(nil, client.server.name, command, mClient.getNick(), nick)
57
+	}
61 58
 }
62 59
 
63 60
 // Add registers `client` to receive notifications about `nick`.

+ 62
- 87
irc/server.go 查看文件

@@ -9,6 +9,7 @@ import (
9 9
 	"bufio"
10 10
 	"crypto/tls"
11 11
 	"encoding/base64"
12
+	"errors"
12 13
 	"fmt"
13 14
 	"log"
14 15
 	"math/rand"
@@ -39,6 +40,8 @@ var (
39 40
 
40 41
 	// common error responses
41 42
 	couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line()
43
+
44
+	RenamePrivsNeeded = errors.New("Only chanops can rename channels")
42 45
 )
43 46
 
44 47
 const (
@@ -80,8 +83,7 @@ type Server struct {
80 83
 	accountRegistration          *AccountRegistration
81 84
 	accounts                     map[string]*ClientAccount
82 85
 	channelRegistrationEnabled   bool
83
-	channels                     ChannelNameMap
84
-	channelJoinPartMutex         sync.Mutex // used when joining/parting channels to prevent stomping over each others' access and all
86
+	channels                     *ChannelManager
85 87
 	checkIdent                   bool
86 88
 	clients                      *ClientLookupSet
87 89
 	commands                     chan Command
@@ -147,7 +149,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
147 149
 	// initialize data structures
148 150
 	server := &Server{
149 151
 		accounts:            make(map[string]*ClientAccount),
150
-		channels:            *NewChannelNameMap(),
152
+		channels:            NewChannelManager(),
151 153
 		clients:             NewClientLookupSet(),
152 154
 		commands:            make(chan Command),
153 155
 		connectionLimiter:   connection_limits.NewLimiter(),
@@ -553,53 +555,62 @@ func pongHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
553 555
 }
554 556
 
555 557
 // RENAME <oldchan> <newchan> [<reason>]
556
-//TODO(dan): Clean up this function so it doesn't look like an eldrich horror... prolly by putting it into a server.renameChannel function.
557
-func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
558
-	// get lots of locks... make sure nobody touches anything while we're doing this
558
+func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) (result bool) {
559
+	result = false
560
+
561
+	// TODO(slingamn, #152) clean up locking here
559 562
 	server.registeredChannelsMutex.Lock()
560 563
 	defer server.registeredChannelsMutex.Unlock()
561
-	server.channels.ChansLock.Lock()
562
-	defer server.channels.ChansLock.Unlock()
564
+
565
+	errorResponse := func(err error, name string) {
566
+		// TODO: send correct error codes, e.g., ERR_CANNOTRENAME, ERR_CHANNAMEINUSE
567
+		var code string
568
+		switch err {
569
+		case NoSuchChannel:
570
+			code = ERR_NOSUCHCHANNEL
571
+		case RenamePrivsNeeded:
572
+			code = ERR_CHANOPRIVSNEEDED
573
+		case InvalidChannelName:
574
+			code = ERR_UNKNOWNERROR
575
+		case ChannelNameInUse:
576
+			code = ERR_UNKNOWNERROR
577
+		default:
578
+			code = ERR_UNKNOWNERROR
579
+		}
580
+		client.Send(nil, server.name, code, client.getNick(), "RENAME", name, err.Error())
581
+	}
563 582
 
564 583
 	oldName := strings.TrimSpace(msg.Params[0])
565 584
 	newName := strings.TrimSpace(msg.Params[1])
566
-	reason := "No reason"
567
-	if 2 < len(msg.Params) {
568
-		reason = msg.Params[2]
585
+	if oldName == "" || newName == "" {
586
+		errorResponse(InvalidChannelName, "<empty>")
587
+		return
569 588
 	}
570
-
571
-	// check for all the reasons why the rename couldn't happen
572 589
 	casefoldedOldName, err := CasefoldChannel(oldName)
573 590
 	if err != nil {
574
-		//TODO(dan): Change this to ERR_CANNOTRENAME
575
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "RENAME", oldName, "Old channel name is invalid")
576
-		return false
591
+		errorResponse(InvalidChannelName, oldName)
592
+		return
577 593
 	}
578
-
579
-	channel := server.channels.Chans[casefoldedOldName]
580
-	if channel == nil {
581
-		client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, oldName, "No such channel")
582
-		return false
594
+	casefoldedNewName, err := CasefoldChannel(newName)
595
+	if err != nil {
596
+		errorResponse(InvalidChannelName, newName)
597
+		return
583 598
 	}
584 599
 
585
-	//TODO(dan): allow IRCops to do this?
586
-	if !channel.ClientIsAtLeast(client, Operator) {
587
-		client.Send(nil, server.name, ERR_CHANOPRIVSNEEDED, client.nick, oldName, "Only chanops can rename channels")
588
-		return false
600
+	reason := "No reason"
601
+	if 2 < len(msg.Params) {
602
+		reason = msg.Params[2]
589 603
 	}
590 604
 
591
-	casefoldedNewName, err := CasefoldChannel(newName)
592
-	if err != nil {
593
-		//TODO(dan): Change this to ERR_CANNOTRENAME
594
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "RENAME", newName, "New channel name is invalid")
595
-		return false
605
+	channel := server.channels.Get(oldName)
606
+	if channel == nil {
607
+		errorResponse(NoSuchChannel, oldName)
608
+		return
596 609
 	}
597
-
598
-	newChannel := server.channels.Chans[casefoldedNewName]
599
-	if newChannel != nil {
600
-		//TODO(dan): Change this to ERR_CHANNAMEINUSE
601
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "RENAME", newName, "New channel name is in use")
602
-		return false
610
+	//TODO(dan): allow IRCops to do this?
611
+	if !channel.ClientIsAtLeast(client, Operator) {
612
+		errorResponse(RenamePrivsNeeded, oldName)
613
+		return
603 614
 	}
604 615
 
605 616
 	var canEdit bool
@@ -622,11 +633,11 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
622 633
 	}
623 634
 
624 635
 	// perform the channel rename
625
-	server.channels.Chans[casefoldedOldName] = nil
626
-	server.channels.Chans[casefoldedNewName] = channel
627
-
628
-	channel.name = strings.TrimSpace(msg.Params[1])
629
-	channel.nameCasefolded = casefoldedNewName
636
+	err = server.channels.Rename(oldName, newName)
637
+	if err != nil {
638
+		errorResponse(err, newName)
639
+		return
640
+	}
630 641
 
631 642
 	// rename stored channel info if any exists
632 643
 	server.store.Update(func(tx *buntdb.Tx) error {
@@ -679,34 +690,15 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
679 690
 		keys = strings.Split(msg.Params[1], ",")
680 691
 	}
681 692
 
682
-	// get lock
683
-	server.channelJoinPartMutex.Lock()
684
-	defer server.channelJoinPartMutex.Unlock()
685
-
686 693
 	for i, name := range channels {
687
-		casefoldedName, err := CasefoldChannel(name)
688
-		if err != nil {
689
-			if len(name) > 0 {
690
-				client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
691
-			}
692
-			continue
693
-		}
694
-
695
-		channel := server.channels.Get(casefoldedName)
696
-		if channel == nil {
697
-			if len(casefoldedName) > server.getLimits().ChannelLen {
698
-				client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
699
-				continue
700
-			}
701
-			channel = NewChannel(server, name, true)
702
-		}
703
-
704 694
 		var key string
705 695
 		if len(keys) > i {
706 696
 			key = keys[i]
707 697
 		}
708
-
709
-		channel.Join(client, key)
698
+		err := server.channels.Join(client, name, key)
699
+		if err == NoSuchChannel {
700
+			client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.getNick(), name, "No such channel")
701
+		}
710 702
 	}
711 703
 	return false
712 704
 }
@@ -719,22 +711,11 @@ func partHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
719 711
 		reason = msg.Params[1]
720 712
 	}
721 713
 
722
-	// get lock
723
-	server.channelJoinPartMutex.Lock()
724
-	defer server.channelJoinPartMutex.Unlock()
725
-
726 714
 	for _, chname := range channels {
727
-		casefoldedChannelName, err := CasefoldChannel(chname)
728
-		channel := server.channels.Get(casefoldedChannelName)
729
-
730
-		if err != nil || channel == nil {
731
-			if len(chname) > 0 {
732
-				client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, "No such channel")
733
-			}
734
-			continue
715
+		err := server.channels.Part(client, chname, reason)
716
+		if err == NoSuchChannel {
717
+			client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, "No such channel")
735 718
 		}
736
-
737
-		channel.Part(client, reason)
738 719
 	}
739 720
 	return false
740 721
 }
@@ -1096,11 +1077,9 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1096 1077
 	//}
1097 1078
 
1098 1079
 	if mask == "" {
1099
-		server.channels.ChansLock.RLock()
1100
-		for _, channel := range server.channels.Chans {
1080
+		for _, channel := range server.channels.Channels() {
1101 1081
 			whoChannel(client, channel, friends)
1102 1082
 		}
1103
-		server.channels.ChansLock.RUnlock()
1104 1083
 	} else if mask[0] == '#' {
1105 1084
 		// TODO implement wildcard matching
1106 1085
 		//TODO(dan): ^ only for opers
@@ -1859,8 +1838,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1859 1838
 	}
1860 1839
 
1861 1840
 	if len(channels) == 0 {
1862
-		server.channels.ChansLock.RLock()
1863
-		for _, channel := range server.channels.Chans {
1841
+		for _, channel := range server.channels.Channels() {
1864 1842
 			if !client.flags[Operator] && channel.flags[Secret] {
1865 1843
 				continue
1866 1844
 			}
@@ -1868,7 +1846,6 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1868 1846
 				client.RplList(channel)
1869 1847
 			}
1870 1848
 		}
1871
-		server.channels.ChansLock.RUnlock()
1872 1849
 	} else {
1873 1850
 		// limit regular users to only listing one channel
1874 1851
 		if !client.flags[Operator] {
@@ -1922,11 +1899,9 @@ func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1922 1899
 	//}
1923 1900
 
1924 1901
 	if len(channels) == 0 {
1925
-		server.channels.ChansLock.RLock()
1926
-		for _, channel := range server.channels.Chans {
1902
+		for _, channel := range server.channels.Channels() {
1927 1903
 			channel.Names(client)
1928 1904
 		}
1929
-		server.channels.ChansLock.RUnlock()
1930 1905
 		return false
1931 1906
 	}
1932 1907
 

+ 0
- 55
irc/types.go 查看文件

@@ -6,64 +6,9 @@
6 6
 package irc
7 7
 
8 8
 import (
9
-	"fmt"
10 9
 	"strings"
11
-	"sync"
12 10
 )
13 11
 
14
-// ChannelNameMap is a map that converts channel names to actual channel objects.
15
-type ChannelNameMap struct {
16
-	ChansLock sync.RWMutex
17
-	Chans     map[string]*Channel
18
-}
19
-
20
-// NewChannelNameMap returns a new ChannelNameMap.
21
-func NewChannelNameMap() *ChannelNameMap {
22
-	var channels ChannelNameMap
23
-	channels.Chans = make(map[string]*Channel)
24
-	return &channels
25
-}
26
-
27
-// Get returns the given channel if it exists.
28
-func (channels *ChannelNameMap) Get(name string) *Channel {
29
-	name, err := CasefoldChannel(name)
30
-	if err == nil {
31
-		channels.ChansLock.RLock()
32
-		defer channels.ChansLock.RUnlock()
33
-		return channels.Chans[name]
34
-	}
35
-	return nil
36
-}
37
-
38
-// Add adds the given channel to our map.
39
-func (channels *ChannelNameMap) Add(channel *Channel) error {
40
-	channels.ChansLock.Lock()
41
-	defer channels.ChansLock.Unlock()
42
-	if channels.Chans[channel.nameCasefolded] != nil {
43
-		return fmt.Errorf("%s: already set", channel.name)
44
-	}
45
-	channels.Chans[channel.nameCasefolded] = channel
46
-	return nil
47
-}
48
-
49
-// Remove removes the given channel from our map.
50
-func (channels *ChannelNameMap) Remove(channel *Channel) error {
51
-	channels.ChansLock.Lock()
52
-	defer channels.ChansLock.Unlock()
53
-	if channel != channels.Chans[channel.nameCasefolded] {
54
-		return fmt.Errorf("%s: mismatch", channel.name)
55
-	}
56
-	delete(channels.Chans, channel.nameCasefolded)
57
-	return nil
58
-}
59
-
60
-// Len returns how many channels we have.
61
-func (channels *ChannelNameMap) Len() int {
62
-	channels.ChansLock.RLock()
63
-	defer channels.ChansLock.RUnlock()
64
-	return len(channels.Chans)
65
-}
66
-
67 12
 // ModeSet holds a set of modes.
68 13
 type ModeSet map[Mode]bool
69 14
 

Loading…
取消
儲存