Преглед изворни кода

refactor channel registration

tags/v1.1.0-rc1
Shivaram Lingamneni пре 5 година
родитељ
комит
63029e2ff5
21 измењених фајлова са 420 додато и 226 уклоњено
  1. 9
    17
      irc/accounts.go
  2. 0
    1
      irc/caps/set.go
  3. 147
    13
      irc/channel.go
  4. 119
    43
      irc/channelmanager.go
  5. 43
    59
      irc/channelreg.go
  6. 2
    6
      irc/chanserv.go
  7. 10
    13
      irc/client.go
  8. 4
    6
      irc/client_lookup_set.go
  9. 2
    0
      irc/errors.go
  10. 15
    8
      irc/getters.go
  11. 2
    6
      irc/handlers.go
  12. 2
    2
      irc/modes.go
  13. 0
    1
      irc/modes/modes.go
  14. 3
    4
      irc/semaphores.go
  15. 19
    15
      irc/server.go
  16. 0
    11
      irc/stats.go
  17. 0
    11
      irc/utils/bitset.go
  18. 0
    2
      irc/utils/bitset_test.go
  19. 35
    0
      irc/utils/sync.go
  20. 4
    6
      irc/whowas.go
  21. 4
    2
      irc/whowas_test.go

+ 9
- 17
irc/accounts.go Прегледај датотеку

59
 	accountToMethod   map[string]NickReservationMethod
59
 	accountToMethod   map[string]NickReservationMethod
60
 }
60
 }
61
 
61
 
62
-func NewAccountManager(server *Server) *AccountManager {
63
-	am := AccountManager{
64
-		accountToClients:  make(map[string][]*Client),
65
-		nickToAccount:     make(map[string]string),
66
-		skeletonToAccount: make(map[string]string),
67
-		accountToMethod:   make(map[string]NickReservationMethod),
68
-		server:            server,
69
-	}
62
+func (am *AccountManager) Initialize(server *Server) {
63
+	am.accountToClients = make(map[string][]*Client)
64
+	am.nickToAccount = make(map[string]string)
65
+	am.skeletonToAccount = make(map[string]string)
66
+	am.accountToMethod = make(map[string]NickReservationMethod)
67
+	am.server = server
70
 
68
 
71
 	am.buildNickToAccountIndex()
69
 	am.buildNickToAccountIndex()
72
 	am.initVHostRequestQueue()
70
 	am.initVHostRequestQueue()
73
-	return &am
74
 }
71
 }
75
 
72
 
76
 func (am *AccountManager) buildNickToAccountIndex() {
73
 func (am *AccountManager) buildNickToAccountIndex() {
855
 	verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
852
 	verificationCodeKey := fmt.Sprintf(keyAccountVerificationCode, casefoldedAccount)
856
 	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
853
 	verifiedKey := fmt.Sprintf(keyAccountVerified, casefoldedAccount)
857
 	nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
854
 	nicksKey := fmt.Sprintf(keyAccountAdditionalNicks, casefoldedAccount)
855
+	enforcementKey := fmt.Sprintf(keyAccountEnforcement, casefoldedAccount)
858
 	vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
856
 	vhostKey := fmt.Sprintf(keyAccountVHost, casefoldedAccount)
859
 	vhostQueueKey := fmt.Sprintf(keyVHostQueueAcctToId, casefoldedAccount)
857
 	vhostQueueKey := fmt.Sprintf(keyVHostQueueAcctToId, casefoldedAccount)
860
 	channelsKey := fmt.Sprintf(keyAccountChannels, casefoldedAccount)
858
 	channelsKey := fmt.Sprintf(keyAccountChannels, casefoldedAccount)
865
 	// on our way out, unregister all the account's channels and delete them from the db
863
 	// on our way out, unregister all the account's channels and delete them from the db
866
 	defer func() {
864
 	defer func() {
867
 		for _, channelName := range registeredChannels {
865
 		for _, channelName := range registeredChannels {
868
-			info := am.server.channelRegistry.LoadChannel(channelName)
869
-			if info != nil && info.Founder == casefoldedAccount {
870
-				am.server.channelRegistry.Delete(channelName, *info)
871
-			}
872
-			channel := am.server.channels.Get(channelName)
873
-			if channel != nil {
874
-				channel.SetUnregistered(casefoldedAccount)
875
-			}
866
+			am.server.channels.SetUnregistered(channelName, casefoldedAccount)
876
 		}
867
 		}
877
 	}()
868
 	}()
878
 
869
 
892
 		tx.Delete(registeredTimeKey)
883
 		tx.Delete(registeredTimeKey)
893
 		tx.Delete(callbackKey)
884
 		tx.Delete(callbackKey)
894
 		tx.Delete(verificationCodeKey)
885
 		tx.Delete(verificationCodeKey)
886
+		tx.Delete(enforcementKey)
895
 		rawNicks, _ = tx.Get(nicksKey)
887
 		rawNicks, _ = tx.Get(nicksKey)
896
 		tx.Delete(nicksKey)
888
 		tx.Delete(nicksKey)
897
 		credText, err = tx.Get(credentialsKey)
889
 		credText, err = tx.Get(credentialsKey)

+ 0
- 1
irc/caps/set.go Прегледај датотеку

16
 // NewSet returns a new Set, with the given capabilities enabled.
16
 // NewSet returns a new Set, with the given capabilities enabled.
17
 func NewSet(capabs ...Capability) *Set {
17
 func NewSet(capabs ...Capability) *Set {
18
 	var newSet Set
18
 	var newSet Set
19
-	utils.BitsetInitialize(newSet[:])
20
 	newSet.Enable(capabs...)
19
 	newSet.Enable(capabs...)
21
 	return &newSet
20
 	return &newSet
22
 }
21
 }

+ 147
- 13
irc/channel.go Прегледај датотеку

22
 
22
 
23
 // Channel represents a channel that clients can join.
23
 // Channel represents a channel that clients can join.
24
 type Channel struct {
24
 type Channel struct {
25
-	flags             *modes.ModeSet
25
+	flags             modes.ModeSet
26
 	lists             map[modes.Mode]*UserMaskSet
26
 	lists             map[modes.Mode]*UserMaskSet
27
 	key               string
27
 	key               string
28
 	members           MemberSet
28
 	members           MemberSet
33
 	createdTime       time.Time
33
 	createdTime       time.Time
34
 	registeredFounder string
34
 	registeredFounder string
35
 	registeredTime    time.Time
35
 	registeredTime    time.Time
36
-	stateMutex        sync.RWMutex // tier 1
37
-	joinPartMutex     sync.Mutex   // tier 3
38
 	topic             string
36
 	topic             string
39
 	topicSetBy        string
37
 	topicSetBy        string
40
 	topicSetTime      time.Time
38
 	topicSetTime      time.Time
41
 	userLimit         int
39
 	userLimit         int
42
 	accountToUMode    map[string]modes.Mode
40
 	accountToUMode    map[string]modes.Mode
43
 	history           history.Buffer
41
 	history           history.Buffer
42
+	stateMutex        sync.RWMutex // tier 1
43
+	writerSemaphore   Semaphore    // tier 1.5
44
+	joinPartMutex     sync.Mutex   // tier 3
45
+	ensureLoaded      utils.Once   // manages loading stored registration info from the database
46
+	dirtyBits         uint
44
 }
47
 }
45
 
48
 
46
 // NewChannel creates a new channel from a `Server` and a `name`
49
 // NewChannel creates a new channel from a `Server` and a `name`
47
 // string, which must be unique on the server.
50
 // string, which must be unique on the server.
48
-func NewChannel(s *Server, name string, regInfo *RegisteredChannel) *Channel {
51
+func NewChannel(s *Server, name string, registered bool) *Channel {
49
 	casefoldedName, err := CasefoldChannel(name)
52
 	casefoldedName, err := CasefoldChannel(name)
50
 	if err != nil {
53
 	if err != nil {
51
 		s.logger.Error("internal", "Bad channel name", name, err.Error())
54
 		s.logger.Error("internal", "Bad channel name", name, err.Error())
54
 
57
 
55
 	channel := &Channel{
58
 	channel := &Channel{
56
 		createdTime: time.Now(), // may be overwritten by applyRegInfo
59
 		createdTime: time.Now(), // may be overwritten by applyRegInfo
57
-		flags:       modes.NewModeSet(),
58
 		lists: map[modes.Mode]*UserMaskSet{
60
 		lists: map[modes.Mode]*UserMaskSet{
59
 			modes.BanMask:    NewUserMaskSet(),
61
 			modes.BanMask:    NewUserMaskSet(),
60
 			modes.ExceptMask: NewUserMaskSet(),
62
 			modes.ExceptMask: NewUserMaskSet(),
69
 
71
 
70
 	config := s.Config()
72
 	config := s.Config()
71
 
73
 
72
-	if regInfo != nil {
73
-		channel.applyRegInfo(regInfo)
74
-	} else {
74
+	channel.writerSemaphore.Initialize(1)
75
+	channel.history.Initialize(config.History.ChannelLength)
76
+
77
+	if !registered {
75
 		for _, mode := range config.Channels.defaultModes {
78
 		for _, mode := range config.Channels.defaultModes {
76
 			channel.flags.SetMode(mode, true)
79
 			channel.flags.SetMode(mode, true)
77
 		}
80
 		}
78
-	}
79
-
80
-	channel.history.Initialize(config.History.ChannelLength)
81
+		// no loading to do, so "mark" the load operation as "done":
82
+		channel.ensureLoaded.Do(func() {})
83
+	} // else: modes will be loaded before first join
81
 
84
 
82
 	return channel
85
 	return channel
83
 }
86
 }
84
 
87
 
88
+// EnsureLoaded blocks until the channel's registration info has been loaded
89
+// from the database.
90
+func (channel *Channel) EnsureLoaded() {
91
+	channel.ensureLoaded.Do(func() {
92
+		nmc := channel.NameCasefolded()
93
+		info, err := channel.server.channelRegistry.LoadChannel(nmc)
94
+		if err == nil {
95
+			channel.applyRegInfo(info)
96
+		} else {
97
+			channel.server.logger.Error("internal", "couldn't load channel", nmc, err.Error())
98
+		}
99
+	})
100
+}
101
+
102
+func (channel *Channel) IsLoaded() bool {
103
+	return channel.ensureLoaded.Done()
104
+}
105
+
85
 // read in channel state that was persisted in the DB
106
 // read in channel state that was persisted in the DB
86
-func (channel *Channel) applyRegInfo(chanReg *RegisteredChannel) {
107
+func (channel *Channel) applyRegInfo(chanReg RegisteredChannel) {
108
+	channel.stateMutex.Lock()
109
+	defer channel.stateMutex.Unlock()
110
+
87
 	channel.registeredFounder = chanReg.Founder
111
 	channel.registeredFounder = chanReg.Founder
88
 	channel.registeredTime = chanReg.RegisteredAt
112
 	channel.registeredTime = chanReg.RegisteredAt
89
 	channel.topic = chanReg.Topic
113
 	channel.topic = chanReg.Topic
116
 	defer channel.stateMutex.RUnlock()
140
 	defer channel.stateMutex.RUnlock()
117
 
141
 
118
 	info.Name = channel.name
142
 	info.Name = channel.name
143
+	info.NameCasefolded = channel.nameCasefolded
119
 	info.Founder = channel.registeredFounder
144
 	info.Founder = channel.registeredFounder
120
 	info.RegisteredAt = channel.registeredTime
145
 	info.RegisteredAt = channel.registeredTime
121
 
146
 
149
 	return
174
 	return
150
 }
175
 }
151
 
176
 
177
+// begin: asynchronous database writeback implementation, modeled on irc/socket.go
178
+
179
+// MarkDirty marks part (or all) of a channel's data as needing to be written back
180
+// to the database, then starts a writer goroutine if necessary.
181
+// This is the equivalent of Socket.Write().
182
+func (channel *Channel) MarkDirty(dirtyBits uint) {
183
+	channel.stateMutex.Lock()
184
+	isRegistered := channel.registeredFounder != ""
185
+	channel.dirtyBits = channel.dirtyBits | dirtyBits
186
+	channel.stateMutex.Unlock()
187
+	if !isRegistered {
188
+		return
189
+	}
190
+
191
+	channel.wakeWriter()
192
+}
193
+
194
+// IsClean returns whether a channel can be safely removed from the server.
195
+// To avoid the obvious TOCTOU race condition, it must be called while holding
196
+// ChannelManager's lock (that way, no one can join and make the channel dirty again
197
+// between this method exiting and the actual deletion).
198
+func (channel *Channel) IsClean() bool {
199
+	if !channel.writerSemaphore.TryAcquire() {
200
+		// a database write (which may fail) is in progress, the channel cannot be cleaned up
201
+		return false
202
+	}
203
+	defer channel.writerSemaphore.Release()
204
+
205
+	channel.stateMutex.RLock()
206
+	defer channel.stateMutex.RUnlock()
207
+	// the channel must be empty, and either be unregistered or fully written to the DB
208
+	return len(channel.members) == 0 && (channel.registeredFounder == "" || channel.dirtyBits == 0)
209
+}
210
+
211
+func (channel *Channel) wakeWriter() {
212
+	if channel.writerSemaphore.TryAcquire() {
213
+		go channel.writeLoop()
214
+	}
215
+}
216
+
217
+// equivalent of Socket.send()
218
+func (channel *Channel) writeLoop() {
219
+	for {
220
+		// TODO(#357) check the error value of this and implement timed backoff
221
+		channel.performWrite(0)
222
+		channel.writerSemaphore.Release()
223
+
224
+		channel.stateMutex.RLock()
225
+		isDirty := channel.dirtyBits != 0
226
+		isEmpty := len(channel.members) == 0
227
+		channel.stateMutex.RUnlock()
228
+
229
+		if !isDirty {
230
+			if isEmpty {
231
+				channel.server.channels.Cleanup(channel)
232
+			}
233
+			return // nothing to do
234
+		} // else: isDirty, so we need to write again
235
+
236
+		if !channel.writerSemaphore.TryAcquire() {
237
+			return
238
+		}
239
+	}
240
+}
241
+
242
+// Store writes part (or all) of the channel's data back to the database,
243
+// blocking until the write is complete. This is the equivalent of
244
+// Socket.BlockingWrite.
245
+func (channel *Channel) Store(dirtyBits uint) (err error) {
246
+	defer func() {
247
+		channel.stateMutex.Lock()
248
+		isDirty := channel.dirtyBits != 0
249
+		isEmpty := len(channel.members) == 0
250
+		channel.stateMutex.Unlock()
251
+
252
+		if isDirty {
253
+			channel.wakeWriter()
254
+		} else if isEmpty {
255
+			channel.server.channels.Cleanup(channel)
256
+		}
257
+	}()
258
+
259
+	channel.writerSemaphore.Acquire()
260
+	defer channel.writerSemaphore.Release()
261
+	return channel.performWrite(dirtyBits)
262
+}
263
+
264
+// do an individual write; equivalent of Socket.send()
265
+func (channel *Channel) performWrite(additionalDirtyBits uint) (err error) {
266
+	channel.stateMutex.Lock()
267
+	dirtyBits := channel.dirtyBits | additionalDirtyBits
268
+	channel.dirtyBits = 0
269
+	isRegistered := channel.registeredFounder != ""
270
+	channel.stateMutex.Unlock()
271
+
272
+	if !isRegistered || dirtyBits == 0 {
273
+		return
274
+	}
275
+
276
+	info := channel.ExportRegistration(dirtyBits)
277
+	err = channel.server.channelRegistry.StoreChannel(info, dirtyBits)
278
+	if err != nil {
279
+		channel.stateMutex.Lock()
280
+		channel.dirtyBits = channel.dirtyBits | dirtyBits
281
+		channel.stateMutex.Unlock()
282
+	}
283
+	return
284
+}
285
+
152
 // SetRegistered registers the channel, returning an error if it was already registered.
286
 // SetRegistered registers the channel, returning an error if it was already registered.
153
 func (channel *Channel) SetRegistered(founder string) error {
287
 func (channel *Channel) SetRegistered(founder string) error {
154
 	channel.stateMutex.Lock()
288
 	channel.stateMutex.Lock()
698
 		}
832
 		}
699
 	}
833
 	}
700
 
834
 
701
-	go channel.server.channelRegistry.StoreChannel(channel, IncludeTopic)
835
+	channel.MarkDirty(IncludeTopic)
702
 }
836
 }
703
 
837
 
704
 // CanSpeak returns true if the client can speak on this channel.
838
 // CanSpeak returns true if the client can speak on this channel.

+ 119
- 43
irc/channelmanager.go Прегледај датотеку

19
 // providing synchronization for creation of new channels on first join,
19
 // providing synchronization for creation of new channels on first join,
20
 // cleanup of empty channels on last part, and renames.
20
 // cleanup of empty channels on last part, and renames.
21
 type ChannelManager struct {
21
 type ChannelManager struct {
22
-	sync.RWMutex // tier 2
23
-	chans        map[string]*channelManagerEntry
22
+	sync.RWMutex       // tier 2
23
+	chans              map[string]*channelManagerEntry
24
+	registeredChannels map[string]bool
25
+	server             *Server
24
 }
26
 }
25
 
27
 
26
 // NewChannelManager returns a new ChannelManager.
28
 // NewChannelManager returns a new ChannelManager.
27
-func NewChannelManager() *ChannelManager {
28
-	return &ChannelManager{
29
-		chans: make(map[string]*channelManagerEntry),
29
+func (cm *ChannelManager) Initialize(server *Server) {
30
+	cm.chans = make(map[string]*channelManagerEntry)
31
+	cm.server = server
32
+
33
+	if server.Config().Channels.Registration.Enabled {
34
+		cm.loadRegisteredChannels()
30
 	}
35
 	}
31
 }
36
 }
32
 
37
 
38
+func (cm *ChannelManager) loadRegisteredChannels() {
39
+	registeredChannels := cm.server.channelRegistry.AllChannels()
40
+	cm.Lock()
41
+	defer cm.Unlock()
42
+	cm.registeredChannels = registeredChannels
43
+}
44
+
33
 // Get returns an existing channel with name equivalent to `name`, or nil
45
 // Get returns an existing channel with name equivalent to `name`, or nil
34
-func (cm *ChannelManager) Get(name string) *Channel {
46
+func (cm *ChannelManager) Get(name string) (channel *Channel) {
35
 	name, err := CasefoldChannel(name)
47
 	name, err := CasefoldChannel(name)
36
 	if err == nil {
48
 	if err == nil {
37
 		cm.RLock()
49
 		cm.RLock()
38
 		defer cm.RUnlock()
50
 		defer cm.RUnlock()
39
 		entry := cm.chans[name]
51
 		entry := cm.chans[name]
40
-		if entry != nil {
52
+		// if the channel is still loading, pretend we don't have it
53
+		if entry != nil && entry.channel.IsLoaded() {
41
 			return entry.channel
54
 			return entry.channel
42
 		}
55
 		}
43
 	}
56
 	}
55
 	cm.Lock()
68
 	cm.Lock()
56
 	entry := cm.chans[casefoldedName]
69
 	entry := cm.chans[casefoldedName]
57
 	if entry == nil {
70
 	if entry == nil {
58
-		// XXX give up the lock to check for a registration, then check again
59
-		// to see if we need to create the channel. we could solve this by doing LoadChannel
60
-		// outside the lock initially on every join, so this is best thought of as an
61
-		// optimization to avoid that.
62
-		cm.Unlock()
63
-		info := client.server.channelRegistry.LoadChannel(casefoldedName)
64
-		cm.Lock()
65
-		entry = cm.chans[casefoldedName]
66
-		if entry == nil {
67
-			entry = &channelManagerEntry{
68
-				channel:      NewChannel(server, name, info),
69
-				pendingJoins: 0,
70
-			}
71
-			cm.chans[casefoldedName] = entry
71
+		registered := cm.registeredChannels[casefoldedName]
72
+		entry = &channelManagerEntry{
73
+			channel:      NewChannel(server, name, registered),
74
+			pendingJoins: 0,
72
 		}
75
 		}
76
+		cm.chans[casefoldedName] = entry
73
 	}
77
 	}
74
 	entry.pendingJoins += 1
78
 	entry.pendingJoins += 1
79
+	channel := entry.channel
75
 	cm.Unlock()
80
 	cm.Unlock()
76
 
81
 
77
-	entry.channel.Join(client, key, isSajoin, rb)
82
+	channel.EnsureLoaded()
83
+	channel.Join(client, key, isSajoin, rb)
78
 
84
 
79
-	cm.maybeCleanup(entry.channel, true)
85
+	cm.maybeCleanup(channel, true)
80
 
86
 
81
 	return nil
87
 	return nil
82
 }
88
 }
85
 	cm.Lock()
91
 	cm.Lock()
86
 	defer cm.Unlock()
92
 	defer cm.Unlock()
87
 
93
 
88
-	entry := cm.chans[channel.NameCasefolded()]
94
+	nameCasefolded := channel.NameCasefolded()
95
+	entry := cm.chans[nameCasefolded]
89
 	if entry == nil || entry.channel != channel {
96
 	if entry == nil || entry.channel != channel {
90
 		return
97
 		return
91
 	}
98
 	}
93
 	if afterJoin {
100
 	if afterJoin {
94
 		entry.pendingJoins -= 1
101
 		entry.pendingJoins -= 1
95
 	}
102
 	}
96
-	// TODO(slingamn) right now, registered channels cannot be cleaned up.
97
-	// this is because once ChannelManager becomes the source of truth about a channel,
98
-	// we can't move the source of truth back to the database unless we do an ACID
99
-	// store while holding the ChannelManager's Lock(). This is pending more decisions
100
-	// about where the database transaction lock fits into the overall lock model.
101
-	if !entry.channel.IsRegistered() && entry.channel.IsEmpty() && entry.pendingJoins == 0 {
102
-		// reread the name, handling the case where the channel was renamed
103
-		casefoldedName := entry.channel.NameCasefolded()
104
-		delete(cm.chans, casefoldedName)
105
-		// invalidate the entry (otherwise, a subsequent cleanup attempt could delete
106
-		// a valid, distinct entry under casefoldedName):
107
-		entry.channel = nil
103
+	if entry.pendingJoins == 0 && entry.channel.IsClean() {
104
+		delete(cm.chans, nameCasefolded)
108
 	}
105
 	}
109
 }
106
 }
110
 
107
 
111
 // Part parts `client` from the channel named `name`, deleting it if it's empty.
108
 // Part parts `client` from the channel named `name`, deleting it if it's empty.
112
 func (cm *ChannelManager) Part(client *Client, name string, message string, rb *ResponseBuffer) error {
109
 func (cm *ChannelManager) Part(client *Client, name string, message string, rb *ResponseBuffer) error {
110
+	var channel *Channel
111
+
113
 	casefoldedName, err := CasefoldChannel(name)
112
 	casefoldedName, err := CasefoldChannel(name)
114
 	if err != nil {
113
 	if err != nil {
115
 		return errNoSuchChannel
114
 		return errNoSuchChannel
117
 
116
 
118
 	cm.RLock()
117
 	cm.RLock()
119
 	entry := cm.chans[casefoldedName]
118
 	entry := cm.chans[casefoldedName]
119
+	if entry != nil {
120
+		channel = entry.channel
121
+	}
120
 	cm.RUnlock()
122
 	cm.RUnlock()
121
 
123
 
122
-	if entry == nil {
124
+	if channel == nil {
123
 		return errNoSuchChannel
125
 		return errNoSuchChannel
124
 	}
126
 	}
125
-	entry.channel.Part(client, message, rb)
127
+	channel.Part(client, message, rb)
126
 	return nil
128
 	return nil
127
 }
129
 }
128
 
130
 
130
 	cm.maybeCleanup(channel, false)
132
 	cm.maybeCleanup(channel, false)
131
 }
133
 }
132
 
134
 
135
+func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) {
136
+	var channel *Channel
137
+	cfname, err := CasefoldChannel(channelName)
138
+	if err != nil {
139
+		return err
140
+	}
141
+
142
+	var entry *channelManagerEntry
143
+
144
+	defer func() {
145
+		if err == nil && channel != nil {
146
+			// registration was successful: make the database reflect it
147
+			err = channel.Store(IncludeAllChannelAttrs)
148
+		}
149
+	}()
150
+
151
+	cm.Lock()
152
+	defer cm.Unlock()
153
+	entry = cm.chans[cfname]
154
+	if entry == nil {
155
+		return errNoSuchChannel
156
+	}
157
+	channel = entry.channel
158
+	err = channel.SetRegistered(account)
159
+	if err != nil {
160
+		return err
161
+	}
162
+	cm.registeredChannels[cfname] = true
163
+	return nil
164
+}
165
+
166
+func (cm *ChannelManager) SetUnregistered(channelName string, account string) (err error) {
167
+	cfname, err := CasefoldChannel(channelName)
168
+	if err != nil {
169
+		return err
170
+	}
171
+
172
+	var info RegisteredChannel
173
+
174
+	defer func() {
175
+		if err == nil {
176
+			err = cm.server.channelRegistry.Delete(info)
177
+		}
178
+	}()
179
+
180
+	cm.Lock()
181
+	defer cm.Unlock()
182
+	entry := cm.chans[cfname]
183
+	if entry == nil {
184
+		return errNoSuchChannel
185
+	}
186
+	info = entry.channel.ExportRegistration(0)
187
+	if info.Founder != account {
188
+		return errChannelNotOwnedByAccount
189
+	}
190
+	entry.channel.SetUnregistered(account)
191
+	delete(cm.registeredChannels, cfname)
192
+	return nil
193
+}
194
+
133
 // Rename renames a channel (but does not notify the members)
195
 // Rename renames a channel (but does not notify the members)
134
-func (cm *ChannelManager) Rename(name string, newname string) error {
196
+func (cm *ChannelManager) Rename(name string, newname string) (err error) {
135
 	cfname, err := CasefoldChannel(name)
197
 	cfname, err := CasefoldChannel(name)
136
 	if err != nil {
198
 	if err != nil {
137
 		return errNoSuchChannel
199
 		return errNoSuchChannel
142
 		return errInvalidChannelName
204
 		return errInvalidChannelName
143
 	}
205
 	}
144
 
206
 
207
+	var channel *Channel
208
+	var info RegisteredChannel
209
+	defer func() {
210
+		if channel != nil && info.Founder != "" {
211
+			channel.Store(IncludeAllChannelAttrs)
212
+			// we just flushed the channel under its new name, therefore this delete
213
+			// cannot be overwritten by a write to the old name:
214
+			cm.server.channelRegistry.Delete(info)
215
+		}
216
+	}()
217
+
145
 	cm.Lock()
218
 	cm.Lock()
146
 	defer cm.Unlock()
219
 	defer cm.Unlock()
147
 
220
 
152
 	if entry == nil {
225
 	if entry == nil {
153
 		return errNoSuchChannel
226
 		return errNoSuchChannel
154
 	}
227
 	}
228
+	channel = entry.channel
229
+	info = channel.ExportRegistration(IncludeInitial)
155
 	delete(cm.chans, cfname)
230
 	delete(cm.chans, cfname)
156
 	cm.chans[cfnewname] = entry
231
 	cm.chans[cfnewname] = entry
157
-	entry.channel.setName(newname)
158
-	entry.channel.setNameCasefolded(cfnewname)
232
+	entry.channel.Rename(newname, cfnewname)
159
 	return nil
233
 	return nil
160
-
161
 }
234
 }
162
 
235
 
163
 // Len returns the number of channels
236
 // Len returns the number of channels
171
 func (cm *ChannelManager) Channels() (result []*Channel) {
244
 func (cm *ChannelManager) Channels() (result []*Channel) {
172
 	cm.RLock()
245
 	cm.RLock()
173
 	defer cm.RUnlock()
246
 	defer cm.RUnlock()
247
+	result = make([]*Channel, 0, len(cm.chans))
174
 	for _, entry := range cm.chans {
248
 	for _, entry := range cm.chans {
175
-		result = append(result, entry.channel)
249
+		if entry.channel.IsLoaded() {
250
+			result = append(result, entry.channel)
251
+		}
176
 	}
252
 	}
177
 	return
253
 	return
178
 }
254
 }

+ 43
- 59
irc/channelreg.go Прегледај датотеку

7
 	"fmt"
7
 	"fmt"
8
 	"strconv"
8
 	"strconv"
9
 	"strings"
9
 	"strings"
10
-	"sync"
11
 	"time"
10
 	"time"
12
 
11
 
13
 	"encoding/json"
12
 	"encoding/json"
71
 type RegisteredChannel struct {
70
 type RegisteredChannel struct {
72
 	// Name of the channel.
71
 	// Name of the channel.
73
 	Name string
72
 	Name string
73
+	// Casefolded name of the channel.
74
+	NameCasefolded string
74
 	// RegisteredAt represents the time that the channel was registered.
75
 	// RegisteredAt represents the time that the channel was registered.
75
 	RegisteredAt time.Time
76
 	RegisteredAt time.Time
76
 	// Founder indicates the founder of the channel.
77
 	// Founder indicates the founder of the channel.
97
 
98
 
98
 // ChannelRegistry manages registered channels.
99
 // ChannelRegistry manages registered channels.
99
 type ChannelRegistry struct {
100
 type ChannelRegistry struct {
100
-	// This serializes operations of the form (read channel state, synchronously persist it);
101
-	// this is enough to guarantee eventual consistency of the database with the
102
-	// ChannelManager and Channel objects, which are the source of truth.
103
-	//
104
-	// We could use the buntdb RW transaction lock for this purpose but we share
105
-	// that with all the other modules, so let's not.
106
-	sync.Mutex // tier 2
107
-	server     *Server
101
+	server *Server
108
 }
102
 }
109
 
103
 
110
 // NewChannelRegistry returns a new ChannelRegistry.
104
 // NewChannelRegistry returns a new ChannelRegistry.
111
-func NewChannelRegistry(server *Server) *ChannelRegistry {
112
-	return &ChannelRegistry{
113
-		server: server,
114
-	}
105
+func (reg *ChannelRegistry) Initialize(server *Server) {
106
+	reg.server = server
107
+}
108
+
109
+func (reg *ChannelRegistry) AllChannels() (result map[string]bool) {
110
+	result = make(map[string]bool)
111
+
112
+	prefix := fmt.Sprintf(keyChannelExists, "")
113
+	reg.server.store.View(func(tx *buntdb.Tx) error {
114
+		return tx.AscendGreaterOrEqual("", prefix, func(key, value string) bool {
115
+			if !strings.HasPrefix(key, prefix) {
116
+				return false
117
+			}
118
+			channel := strings.TrimPrefix(key, prefix)
119
+			result[channel] = true
120
+			return true
121
+		})
122
+	})
123
+
124
+	return
115
 }
125
 }
116
 
126
 
117
 // StoreChannel obtains a consistent view of a channel, then persists it to the store.
127
 // StoreChannel obtains a consistent view of a channel, then persists it to the store.
118
-func (reg *ChannelRegistry) StoreChannel(channel *Channel, includeFlags uint) {
128
+func (reg *ChannelRegistry) StoreChannel(info RegisteredChannel, includeFlags uint) (err error) {
119
 	if !reg.server.ChannelRegistrationEnabled() {
129
 	if !reg.server.ChannelRegistrationEnabled() {
120
 		return
130
 		return
121
 	}
131
 	}
122
 
132
 
123
-	reg.Lock()
124
-	defer reg.Unlock()
125
-
126
-	key := channel.NameCasefolded()
127
-	info := channel.ExportRegistration(includeFlags)
128
 	if info.Founder == "" {
133
 	if info.Founder == "" {
129
 		// sanity check, don't try to store an unregistered channel
134
 		// sanity check, don't try to store an unregistered channel
130
 		return
135
 		return
131
 	}
136
 	}
132
 
137
 
133
 	reg.server.store.Update(func(tx *buntdb.Tx) error {
138
 	reg.server.store.Update(func(tx *buntdb.Tx) error {
134
-		reg.saveChannel(tx, key, info, includeFlags)
139
+		reg.saveChannel(tx, info, includeFlags)
135
 		return nil
140
 		return nil
136
 	})
141
 	})
142
+
143
+	return nil
137
 }
144
 }
138
 
145
 
139
 // LoadChannel loads a channel from the store.
146
 // LoadChannel loads a channel from the store.
140
-func (reg *ChannelRegistry) LoadChannel(nameCasefolded string) (info *RegisteredChannel) {
147
+func (reg *ChannelRegistry) LoadChannel(nameCasefolded string) (info RegisteredChannel, err error) {
141
 	if !reg.server.ChannelRegistrationEnabled() {
148
 	if !reg.server.ChannelRegistrationEnabled() {
142
-		return nil
149
+		err = errFeatureDisabled
150
+		return
143
 	}
151
 	}
144
 
152
 
145
 	channelKey := nameCasefolded
153
 	channelKey := nameCasefolded
146
 	// nice to have: do all JSON (de)serialization outside of the buntdb transaction
154
 	// nice to have: do all JSON (de)serialization outside of the buntdb transaction
147
-	reg.server.store.View(func(tx *buntdb.Tx) error {
148
-		_, err := tx.Get(fmt.Sprintf(keyChannelExists, channelKey))
149
-		if err == buntdb.ErrNotFound {
155
+	err = reg.server.store.View(func(tx *buntdb.Tx) error {
156
+		_, dberr := tx.Get(fmt.Sprintf(keyChannelExists, channelKey))
157
+		if dberr == buntdb.ErrNotFound {
150
 			// chan does not already exist, return
158
 			// chan does not already exist, return
151
-			return nil
159
+			return errNoSuchChannel
152
 		}
160
 		}
153
 
161
 
154
 		// channel exists, load it
162
 		// channel exists, load it
181
 		accountToUMode := make(map[string]modes.Mode)
189
 		accountToUMode := make(map[string]modes.Mode)
182
 		_ = json.Unmarshal([]byte(accountToUModeString), &accountToUMode)
190
 		_ = json.Unmarshal([]byte(accountToUModeString), &accountToUMode)
183
 
191
 
184
-		info = &RegisteredChannel{
192
+		info = RegisteredChannel{
185
 			Name:           name,
193
 			Name:           name,
186
 			RegisteredAt:   time.Unix(regTimeInt, 0),
194
 			RegisteredAt:   time.Unix(regTimeInt, 0),
187
 			Founder:        founder,
195
 			Founder:        founder,
198
 		return nil
206
 		return nil
199
 	})
207
 	})
200
 
208
 
201
-	return info
202
-}
203
-
204
-func (reg *ChannelRegistry) Delete(casefoldedName string, info RegisteredChannel) {
205
-	if !reg.server.ChannelRegistrationEnabled() {
206
-		return
207
-	}
208
-
209
-	reg.Lock()
210
-	defer reg.Unlock()
211
-
212
-	reg.server.store.Update(func(tx *buntdb.Tx) error {
213
-		reg.deleteChannel(tx, casefoldedName, info)
214
-		return nil
215
-	})
209
+	return
216
 }
210
 }
217
 
211
 
218
-// Rename handles the persistence part of a channel rename: the channel is
219
-// persisted under its new name, and the old name is cleaned up if necessary.
220
-func (reg *ChannelRegistry) Rename(channel *Channel, casefoldedOldName string) {
212
+// Delete deletes a channel corresponding to `info`. If no such channel
213
+// is present in the database, no error is returned.
214
+func (reg *ChannelRegistry) Delete(info RegisteredChannel) (err error) {
221
 	if !reg.server.ChannelRegistrationEnabled() {
215
 	if !reg.server.ChannelRegistrationEnabled() {
222
 		return
216
 		return
223
 	}
217
 	}
224
 
218
 
225
-	reg.Lock()
226
-	defer reg.Unlock()
227
-
228
-	includeFlags := IncludeAllChannelAttrs
229
-	oldKey := casefoldedOldName
230
-	key := channel.NameCasefolded()
231
-	info := channel.ExportRegistration(includeFlags)
232
-	if info.Founder == "" {
233
-		return
234
-	}
235
-
236
 	reg.server.store.Update(func(tx *buntdb.Tx) error {
219
 	reg.server.store.Update(func(tx *buntdb.Tx) error {
237
-		reg.deleteChannel(tx, oldKey, info)
238
-		reg.saveChannel(tx, key, info, includeFlags)
220
+		reg.deleteChannel(tx, info.NameCasefolded, info)
239
 		return nil
221
 		return nil
240
 	})
222
 	})
223
+	return nil
241
 }
224
 }
242
 
225
 
243
 // delete a channel, unless it was overwritten by another registration of the same channel
226
 // delete a channel, unless it was overwritten by another registration of the same channel
274
 }
257
 }
275
 
258
 
276
 // saveChannel saves a channel to the store.
259
 // saveChannel saves a channel to the store.
277
-func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelKey string, channelInfo RegisteredChannel, includeFlags uint) {
260
+func (reg *ChannelRegistry) saveChannel(tx *buntdb.Tx, channelInfo RegisteredChannel, includeFlags uint) {
261
+	channelKey := channelInfo.NameCasefolded
278
 	// maintain the mapping of account -> registered channels
262
 	// maintain the mapping of account -> registered channels
279
 	chanExistsKey := fmt.Sprintf(keyChannelExists, channelKey)
263
 	chanExistsKey := fmt.Sprintf(keyChannelExists, channelKey)
280
 	_, existsErr := tx.Get(chanExistsKey)
264
 	_, existsErr := tx.Get(chanExistsKey)

+ 2
- 6
irc/chanserv.go Прегледај датотеку

232
 	}
232
 	}
233
 
233
 
234
 	// this provides the synchronization that allows exactly one registration of the channel:
234
 	// this provides the synchronization that allows exactly one registration of the channel:
235
-	err = channelInfo.SetRegistered(account)
235
+	err = server.channels.SetRegistered(channelKey, account)
236
 	if err != nil {
236
 	if err != nil {
237
 		csNotice(rb, err.Error())
237
 		csNotice(rb, err.Error())
238
 		return
238
 		return
239
 	}
239
 	}
240
 
240
 
241
-	// registration was successful: make the database reflect it
242
-	go server.channelRegistry.StoreChannel(channelInfo, IncludeAllChannelAttrs)
243
-
244
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
241
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
245
 
242
 
246
 	server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
243
 	server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
297
 		return
294
 		return
298
 	}
295
 	}
299
 
296
 
300
-	channel.SetUnregistered(founder)
301
-	server.channelRegistry.Delete(channelKey, info)
297
+	server.channels.SetUnregistered(channelKey, founder)
302
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
298
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
303
 }
299
 }
304
 
300
 

+ 10
- 13
irc/client.go Прегледај датотеку

50
 	accountName        string // display name of the account: uncasefolded, '*' if not logged in
50
 	accountName        string // display name of the account: uncasefolded, '*' if not logged in
51
 	atime              time.Time
51
 	atime              time.Time
52
 	awayMessage        string
52
 	awayMessage        string
53
-	capabilities       *caps.Set
53
+	capabilities       caps.Set
54
 	capState           caps.State
54
 	capState           caps.State
55
 	capVersion         caps.Version
55
 	capVersion         caps.Version
56
 	certfp             string
56
 	certfp             string
58
 	ctime              time.Time
58
 	ctime              time.Time
59
 	exitedSnomaskSent  bool
59
 	exitedSnomaskSent  bool
60
 	fakelag            Fakelag
60
 	fakelag            Fakelag
61
-	flags              *modes.ModeSet
61
+	flags              modes.ModeSet
62
 	hasQuit            bool
62
 	hasQuit            bool
63
 	hops               int
63
 	hops               int
64
 	hostname           string
64
 	hostname           string
125
 	// give them 1k of grace over the limit:
125
 	// give them 1k of grace over the limit:
126
 	socket := NewSocket(conn.Conn, fullLineLenLimit+1024, config.Server.MaxSendQBytes)
126
 	socket := NewSocket(conn.Conn, fullLineLenLimit+1024, config.Server.MaxSendQBytes)
127
 	client := &Client{
127
 	client := &Client{
128
-		atime:        now,
129
-		capabilities: caps.NewSet(),
130
-		capState:     caps.NoneState,
131
-		capVersion:   caps.Cap301,
132
-		channels:     make(ChannelSet),
133
-		ctime:        now,
134
-		flags:        modes.NewModeSet(),
135
-		isTor:        conn.IsTor,
136
-		languages:    server.Languages().Default(),
128
+		atime:      now,
129
+		capState:   caps.NoneState,
130
+		capVersion: caps.Cap301,
131
+		channels:   make(ChannelSet),
132
+		ctime:      now,
133
+		isTor:      conn.IsTor,
134
+		languages:  server.Languages().Default(),
137
 		loginThrottle: connection_limits.GenericThrottle{
135
 		loginThrottle: connection_limits.GenericThrottle{
138
 			Duration: config.Accounts.LoginThrottling.Duration,
136
 			Duration: config.Accounts.LoginThrottling.Duration,
139
 			Limit:    config.Accounts.LoginThrottling.MaxAttempts,
137
 			Limit:    config.Accounts.LoginThrottling.MaxAttempts,
546
 // copy applicable state from oldClient to client as part of a resume
544
 // copy applicable state from oldClient to client as part of a resume
547
 func (client *Client) copyResumeData(oldClient *Client) {
545
 func (client *Client) copyResumeData(oldClient *Client) {
548
 	oldClient.stateMutex.RLock()
546
 	oldClient.stateMutex.RLock()
549
-	flags := oldClient.flags
550
 	history := oldClient.history
547
 	history := oldClient.history
551
 	nick := oldClient.nick
548
 	nick := oldClient.nick
552
 	nickCasefolded := oldClient.nickCasefolded
549
 	nickCasefolded := oldClient.nickCasefolded
560
 	// resume over plaintext)
557
 	// resume over plaintext)
561
 	hasTLS := client.flags.HasMode(modes.TLS)
558
 	hasTLS := client.flags.HasMode(modes.TLS)
562
 	temp := modes.NewModeSet()
559
 	temp := modes.NewModeSet()
563
-	temp.Copy(flags)
560
+	temp.Copy(&oldClient.flags)
564
 	temp.SetMode(modes.TLS, hasTLS)
561
 	temp.SetMode(modes.TLS, hasTLS)
565
 	client.flags.Copy(temp)
562
 	client.flags.Copy(temp)
566
 
563
 

+ 4
- 6
irc/client_lookup_set.go Прегледај датотеку

37
 	bySkeleton   map[string]*Client
37
 	bySkeleton   map[string]*Client
38
 }
38
 }
39
 
39
 
40
-// NewClientManager returns a new ClientManager.
41
-func NewClientManager() *ClientManager {
42
-	return &ClientManager{
43
-		byNick:     make(map[string]*Client),
44
-		bySkeleton: make(map[string]*Client),
45
-	}
40
+// Initialize initializes a ClientManager.
41
+func (clients *ClientManager) Initialize() {
42
+	clients.byNick = make(map[string]*Client)
43
+	clients.bySkeleton = make(map[string]*Client)
46
 }
44
 }
47
 
45
 
48
 // Count returns how many clients are in the manager.
46
 // Count returns how many clients are in the manager.

+ 2
- 0
irc/errors.go Прегледај датотеку

27
 	errAccountMustHoldNick            = errors.New(`You must hold that nickname in order to register it`)
27
 	errAccountMustHoldNick            = errors.New(`You must hold that nickname in order to register it`)
28
 	errCallbackFailed                 = errors.New("Account verification could not be sent")
28
 	errCallbackFailed                 = errors.New("Account verification could not be sent")
29
 	errCertfpAlreadyExists            = errors.New(`An account already exists for your certificate fingerprint`)
29
 	errCertfpAlreadyExists            = errors.New(`An account already exists for your certificate fingerprint`)
30
+	errChannelNotOwnedByAccount       = errors.New("Channel not owned by the specified account")
31
+	errChannelDoesNotExist            = errors.New("Channel does not exist")
30
 	errChannelAlreadyRegistered       = errors.New("Channel is already registered")
32
 	errChannelAlreadyRegistered       = errors.New("Channel is already registered")
31
 	errChannelNameInUse               = errors.New(`Channel name in use`)
33
 	errChannelNameInUse               = errors.New(`Channel name in use`)
32
 	errInvalidChannelName             = errors.New(`Invalid channel name`)
34
 	errInvalidChannelName             = errors.New(`Invalid channel name`)

+ 15
- 8
irc/getters.go Прегледај датотеку

4
 package irc
4
 package irc
5
 
5
 
6
 import (
6
 import (
7
+	"time"
8
+
7
 	"github.com/oragono/oragono/irc/isupport"
9
 	"github.com/oragono/oragono/irc/isupport"
8
 	"github.com/oragono/oragono/irc/languages"
10
 	"github.com/oragono/oragono/irc/languages"
9
 	"github.com/oragono/oragono/irc/modes"
11
 	"github.com/oragono/oragono/irc/modes"
267
 	return channel.name
269
 	return channel.name
268
 }
270
 }
269
 
271
 
270
-func (channel *Channel) setName(name string) {
271
-	channel.stateMutex.Lock()
272
-	defer channel.stateMutex.Unlock()
273
-	channel.name = name
274
-}
275
-
276
 func (channel *Channel) NameCasefolded() string {
272
 func (channel *Channel) NameCasefolded() string {
277
 	channel.stateMutex.RLock()
273
 	channel.stateMutex.RLock()
278
 	defer channel.stateMutex.RUnlock()
274
 	defer channel.stateMutex.RUnlock()
279
 	return channel.nameCasefolded
275
 	return channel.nameCasefolded
280
 }
276
 }
281
 
277
 
282
-func (channel *Channel) setNameCasefolded(nameCasefolded string) {
278
+func (channel *Channel) Rename(name, nameCasefolded string) {
283
 	channel.stateMutex.Lock()
279
 	channel.stateMutex.Lock()
284
-	defer channel.stateMutex.Unlock()
280
+	channel.name = name
285
 	channel.nameCasefolded = nameCasefolded
281
 	channel.nameCasefolded = nameCasefolded
282
+	if channel.registeredFounder != "" {
283
+		channel.registeredTime = time.Now()
284
+	}
285
+	channel.stateMutex.Unlock()
286
 }
286
 }
287
 
287
 
288
 func (channel *Channel) Members() (result []*Client) {
288
 func (channel *Channel) Members() (result []*Client) {
314
 	defer channel.stateMutex.RUnlock()
314
 	defer channel.stateMutex.RUnlock()
315
 	return channel.registeredFounder
315
 	return channel.registeredFounder
316
 }
316
 }
317
+
318
+func (channel *Channel) DirtyBits() (dirtyBits uint) {
319
+	channel.stateMutex.Lock()
320
+	dirtyBits = channel.dirtyBits
321
+	channel.stateMutex.Unlock()
322
+	return
323
+}

+ 2
- 6
irc/handlers.go Прегледај датотеку

1607
 		}
1607
 		}
1608
 	}
1608
 	}
1609
 
1609
 
1610
-	if channel.IsRegistered() && includeFlags != 0 {
1611
-		go server.channelRegistry.StoreChannel(channel, includeFlags)
1610
+	if includeFlags != 0 {
1611
+		channel.MarkDirty(includeFlags)
1612
 	}
1612
 	}
1613
 
1613
 
1614
 	// send out changes
1614
 	// send out changes
2215
 		rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), oldName, client.t("No such channel"))
2215
 		rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), oldName, client.t("No such channel"))
2216
 		return false
2216
 		return false
2217
 	}
2217
 	}
2218
-	casefoldedOldName := channel.NameCasefolded()
2219
 	if !(channel.ClientIsAtLeast(client, modes.Operator) || client.HasRoleCapabs("chanreg")) {
2218
 	if !(channel.ClientIsAtLeast(client, modes.Operator) || client.HasRoleCapabs("chanreg")) {
2220
 		rb.Add(nil, server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), oldName, client.t("You're not a channel operator"))
2219
 		rb.Add(nil, server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), oldName, client.t("You're not a channel operator"))
2221
 		return false
2220
 		return false
2240
 		return false
2239
 		return false
2241
 	}
2240
 	}
2242
 
2241
 
2243
-	// rename succeeded, persist it
2244
-	go server.channelRegistry.Rename(channel, casefoldedOldName)
2245
-
2246
 	// send RENAME messages
2242
 	// send RENAME messages
2247
 	clientPrefix := client.NickMaskString()
2243
 	clientPrefix := client.NickMaskString()
2248
 	for _, mcl := range channel.Members() {
2244
 	for _, mcl := range channel.Members() {

+ 2
- 2
irc/modes.go Прегледај датотеку

290
 	case modes.Add:
290
 	case modes.Add:
291
 		if targetModeNow != targetModeAfter {
291
 		if targetModeNow != targetModeAfter {
292
 			channel.accountToUMode[change.Arg] = change.Mode
292
 			channel.accountToUMode[change.Arg] = change.Mode
293
-			go client.server.channelRegistry.StoreChannel(channel, IncludeLists)
293
+			channel.MarkDirty(IncludeLists)
294
 			return []modes.ModeChange{change}, nil
294
 			return []modes.ModeChange{change}, nil
295
 		}
295
 		}
296
 		return nil, nil
296
 		return nil, nil
297
 	case modes.Remove:
297
 	case modes.Remove:
298
 		if targetModeNow == change.Mode {
298
 		if targetModeNow == change.Mode {
299
 			delete(channel.accountToUMode, change.Arg)
299
 			delete(channel.accountToUMode, change.Arg)
300
-			go client.server.channelRegistry.StoreChannel(channel, IncludeLists)
300
+			channel.MarkDirty(IncludeLists)
301
 			return []modes.ModeChange{change}, nil
301
 			return []modes.ModeChange{change}, nil
302
 		}
302
 		}
303
 		return nil, nil
303
 		return nil, nil

+ 0
- 1
irc/modes/modes.go Прегледај датотеку

335
 // returns a pointer to a new ModeSet
335
 // returns a pointer to a new ModeSet
336
 func NewModeSet() *ModeSet {
336
 func NewModeSet() *ModeSet {
337
 	var set ModeSet
337
 	var set ModeSet
338
-	utils.BitsetInitialize(set[:])
339
 	return &set
338
 	return &set
340
 }
339
 }
341
 
340
 

+ 3
- 4
irc/semaphores.go Прегледај датотеку

32
 	ClientDestroy Semaphore
32
 	ClientDestroy Semaphore
33
 }
33
 }
34
 
34
 
35
-// NewServerSemaphores creates a new ServerSemaphores.
36
-func NewServerSemaphores() (result *ServerSemaphores) {
35
+// Initialize initializes a set of server semaphores.
36
+func (serversem *ServerSemaphores) Initialize() {
37
 	capacity := runtime.NumCPU()
37
 	capacity := runtime.NumCPU()
38
 	if capacity > MaxServerSemaphoreCapacity {
38
 	if capacity > MaxServerSemaphoreCapacity {
39
 		capacity = MaxServerSemaphoreCapacity
39
 		capacity = MaxServerSemaphoreCapacity
40
 	}
40
 	}
41
-	result = new(ServerSemaphores)
42
-	result.ClientDestroy.Initialize(capacity)
41
+	serversem.ClientDestroy.Initialize(capacity)
43
 	return
42
 	return
44
 }
43
 }
45
 
44
 

+ 19
- 15
irc/server.go Прегледај датотеку

61
 
61
 
62
 // Server is the main Oragono server.
62
 // Server is the main Oragono server.
63
 type Server struct {
63
 type Server struct {
64
-	accounts               *AccountManager
65
-	channels               *ChannelManager
66
-	channelRegistry        *ChannelRegistry
67
-	clients                *ClientManager
64
+	accounts               AccountManager
65
+	channels               ChannelManager
66
+	channelRegistry        ChannelRegistry
67
+	clients                ClientManager
68
 	config                 *Config
68
 	config                 *Config
69
 	configFilename         string
69
 	configFilename         string
70
 	configurableStateMutex sync.RWMutex // tier 1; generic protection for server state modified by rehash()
70
 	configurableStateMutex sync.RWMutex // tier 1; generic protection for server state modified by rehash()
89
 	snomasks               *SnoManager
89
 	snomasks               *SnoManager
90
 	store                  *buntdb.DB
90
 	store                  *buntdb.DB
91
 	torLimiter             connection_limits.TorLimiter
91
 	torLimiter             connection_limits.TorLimiter
92
-	whoWas                 *WhoWasList
93
-	stats                  *Stats
94
-	semaphores             *ServerSemaphores
92
+	whoWas                 WhoWasList
93
+	stats                  Stats
94
+	semaphores             ServerSemaphores
95
 }
95
 }
96
 
96
 
97
 var (
97
 var (
113
 func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
113
 func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
114
 	// initialize data structures
114
 	// initialize data structures
115
 	server := &Server{
115
 	server := &Server{
116
-		channels:            NewChannelManager(),
117
-		clients:             NewClientManager(),
118
 		connectionLimiter:   connection_limits.NewLimiter(),
116
 		connectionLimiter:   connection_limits.NewLimiter(),
119
 		connectionThrottler: connection_limits.NewThrottler(),
117
 		connectionThrottler: connection_limits.NewThrottler(),
120
 		listeners:           make(map[string]*ListenerWrapper),
118
 		listeners:           make(map[string]*ListenerWrapper),
123
 		rehashSignal:        make(chan os.Signal, 1),
121
 		rehashSignal:        make(chan os.Signal, 1),
124
 		signals:             make(chan os.Signal, len(ServerExitSignals)),
122
 		signals:             make(chan os.Signal, len(ServerExitSignals)),
125
 		snomasks:            NewSnoManager(),
123
 		snomasks:            NewSnoManager(),
126
-		whoWas:              NewWhoWasList(config.Limits.WhowasEntries),
127
-		stats:               NewStats(),
128
-		semaphores:          NewServerSemaphores(),
129
 	}
124
 	}
130
 
125
 
126
+	server.clients.Initialize()
127
+	server.semaphores.Initialize()
131
 	server.resumeManager.Initialize(server)
128
 	server.resumeManager.Initialize(server)
129
+	server.whoWas.Initialize(config.Limits.WhowasEntries)
132
 
130
 
133
 	if err := server.applyConfig(config, true); err != nil {
131
 	if err := server.applyConfig(config, true); err != nil {
134
 		return nil, err
132
 		return nil, err
697
 		server.accounts.initVHostRequestQueue()
695
 		server.accounts.initVHostRequestQueue()
698
 	}
696
 	}
699
 
697
 
698
+	chanRegPreviouslyDisabled := oldConfig != nil && !oldConfig.Channels.Registration.Enabled
699
+	chanRegNowEnabled := config.Channels.Registration.Enabled
700
+	if chanRegPreviouslyDisabled && chanRegNowEnabled {
701
+		server.channels.loadRegisteredChannels()
702
+	}
703
+
700
 	// MaxLine
704
 	// MaxLine
701
 	if config.Limits.LineLen.Rest != 512 {
705
 	if config.Limits.LineLen.Rest != 512 {
702
 		SupportedCapabilities.Enable(caps.MaxLine)
706
 		SupportedCapabilities.Enable(caps.MaxLine)
922
 	server.loadDLines()
926
 	server.loadDLines()
923
 	server.loadKLines()
927
 	server.loadKLines()
924
 
928
 
925
-	server.channelRegistry = NewChannelRegistry(server)
926
-
927
-	server.accounts = NewAccountManager(server)
929
+	server.channelRegistry.Initialize(server)
930
+	server.channels.Initialize(server)
931
+	server.accounts.Initialize(server)
928
 
932
 
929
 	return nil
933
 	return nil
930
 }
934
 }

+ 0
- 11
irc/stats.go Прегледај датотеку

13
 	Operators int
13
 	Operators int
14
 }
14
 }
15
 
15
 
16
-// NewStats creates a new instance of Stats
17
-func NewStats() *Stats {
18
-	serverStats := &Stats{
19
-		Total:     0,
20
-		Invisible: 0,
21
-		Operators: 0,
22
-	}
23
-
24
-	return serverStats
25
-}
26
-
27
 // ChangeTotal increments the total user count on server
16
 // ChangeTotal increments the total user count on server
28
 func (s *Stats) ChangeTotal(i int) {
17
 func (s *Stats) ChangeTotal(i int) {
29
 	s.Lock()
18
 	s.Lock()

+ 0
- 11
irc/utils/bitset.go Прегледај датотеку

9
 // For examples of use, see caps.Set and modes.ModeSet; the array has to be converted to a
9
 // For examples of use, see caps.Set and modes.ModeSet; the array has to be converted to a
10
 // slice to use these functions.
10
 // slice to use these functions.
11
 
11
 
12
-// BitsetInitialize initializes a bitset.
13
-func BitsetInitialize(set []uint64) {
14
-	// XXX re-zero the bitset using atomic stores. it's unclear whether this is required,
15
-	// however, golang issue #5045 suggests that you shouldn't mix atomic operations
16
-	// with non-atomic operations (such as the runtime's automatic zero-initialization) on
17
-	// the same word
18
-	for i := 0; i < len(set); i++ {
19
-		atomic.StoreUint64(&set[i], 0)
20
-	}
21
-}
22
-
23
 // BitsetGet returns whether a given bit of the bitset is set.
12
 // BitsetGet returns whether a given bit of the bitset is set.
24
 func BitsetGet(set []uint64, position uint) bool {
13
 func BitsetGet(set []uint64, position uint) bool {
25
 	idx := position / 64
14
 	idx := position / 64

+ 0
- 2
irc/utils/bitset_test.go Прегледај датотеку

10
 func TestSets(t *testing.T) {
10
 func TestSets(t *testing.T) {
11
 	var t1 testBitset
11
 	var t1 testBitset
12
 	t1s := t1[:]
12
 	t1s := t1[:]
13
-	BitsetInitialize(t1s)
14
 
13
 
15
 	if BitsetGet(t1s, 0) || BitsetGet(t1s, 63) || BitsetGet(t1s, 64) || BitsetGet(t1s, 127) {
14
 	if BitsetGet(t1s, 0) || BitsetGet(t1s, 63) || BitsetGet(t1s, 64) || BitsetGet(t1s, 127) {
16
 		t.Error("no bits should be set in a newly initialized bitset")
15
 		t.Error("no bits should be set in a newly initialized bitset")
47
 
46
 
48
 	var t2 testBitset
47
 	var t2 testBitset
49
 	t2s := t2[:]
48
 	t2s := t2[:]
50
-	BitsetInitialize(t2s)
51
 
49
 
52
 	for i = 0; i < 128; i++ {
50
 	for i = 0; i < 128; i++ {
53
 		if i%2 == 1 {
51
 		if i%2 == 1 {

+ 35
- 0
irc/utils/sync.go Прегледај датотеку

1
+// Copyright 2009 The Go Authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package utils
6
+
7
+import (
8
+	"sync"
9
+	"sync/atomic"
10
+)
11
+
12
+// Once is a fork of sync.Once to expose a Done() method.
13
+type Once struct {
14
+	done uint32
15
+	m    sync.Mutex
16
+}
17
+
18
+func (o *Once) Do(f func()) {
19
+	if atomic.LoadUint32(&o.done) == 0 {
20
+		o.doSlow(f)
21
+	}
22
+}
23
+
24
+func (o *Once) doSlow(f func()) {
25
+	o.m.Lock()
26
+	defer o.m.Unlock()
27
+	if o.done == 0 {
28
+		defer atomic.StoreUint32(&o.done, 1)
29
+		f()
30
+	}
31
+}
32
+
33
+func (o *Once) Done() bool {
34
+	return atomic.LoadUint32(&o.done) == 1
35
+}

+ 4
- 6
irc/whowas.go Прегледај датотеку

23
 }
23
 }
24
 
24
 
25
 // NewWhoWasList returns a new WhoWasList
25
 // NewWhoWasList returns a new WhoWasList
26
-func NewWhoWasList(size int) *WhoWasList {
27
-	return &WhoWasList{
28
-		buffer: make([]WhoWas, size),
29
-		start:  -1,
30
-		end:    -1,
31
-	}
26
+func (list *WhoWasList) Initialize(size int) {
27
+	list.buffer = make([]WhoWas, size)
28
+	list.start = -1
29
+	list.end = -1
32
 }
30
 }
33
 
31
 
34
 // Append adds an entry to the WhoWasList.
32
 // Append adds an entry to the WhoWasList.

+ 4
- 2
irc/whowas_test.go Прегледај датотеку

23
 
23
 
24
 func TestWhoWas(t *testing.T) {
24
 func TestWhoWas(t *testing.T) {
25
 	var results []WhoWas
25
 	var results []WhoWas
26
-	wwl := NewWhoWasList(3)
26
+	var wwl WhoWasList
27
+	wwl.Initialize(3)
27
 	// test Find on empty list
28
 	// test Find on empty list
28
 	results = wwl.Find("nobody", 10)
29
 	results = wwl.Find("nobody", 10)
29
 	if len(results) != 0 {
30
 	if len(results) != 0 {
88
 
89
 
89
 func TestEmptyWhoWas(t *testing.T) {
90
 func TestEmptyWhoWas(t *testing.T) {
90
 	// stupid edge case; setting an empty whowas buffer should not panic
91
 	// stupid edge case; setting an empty whowas buffer should not panic
91
-	wwl := NewWhoWasList(0)
92
+	var wwl WhoWasList
93
+	wwl.Initialize(0)
92
 	results := wwl.Find("slingamn", 10)
94
 	results := wwl.Find("slingamn", 10)
93
 	if len(results) != 0 {
95
 	if len(results) != 0 {
94
 		t.Fatalf("incorrect whowas results: %v", results)
96
 		t.Fatalf("incorrect whowas results: %v", results)

Loading…
Откажи
Сачувај