-
Notifications
You must be signed in to change notification settings - Fork 1
/
registry.go
289 lines (247 loc) · 6.98 KB
/
registry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// Copyright 2018 Dan Jacques. All rights reserved.
// Use of this source code is governed under the MIT License
// that can be found in the LICENSE file.
package device
import (
"sort"
"sync"
)
// Registry is a generic device registry. It tracks devices by ID, records
// which group devices belong to, and removes device entries when they expire.
type Registry struct {
mu sync.RWMutex
// Map of active devices.
devices map[string]*registryEntry
// Maintain a map of group IDs to the devices that are in them.
groupMap map[int]map[*registryEntry]struct{}
// Maintain a map of the devices that claim an Ordinal.
ordinalMap map[Ordinal]map[*registryEntry]struct{}
}
// Add adds or update's d's registration in the Registry.
func (reg *Registry) Add(d D) {
id := d.ID()
// Fast path (read lock): Device is registered, nothing's changed.
if reg.checkDeviceRegistration(id) {
return
}
reg.mu.Lock()
defer reg.mu.Unlock()
// Unregister any entries that are currently done, under lock. This prevents
// a race where the device is Done, and will be unregistered, but it is then
// rediscovered, causing the registration to fail as duplicate and be missed.
reg.unregisterDoneEntriesLocked()
isNew := false
e := reg.devices[id]
if e == nil {
// This is a new entry.
e = ®istryEntry{
reg: reg,
device: d,
deviceID: id,
}
if reg.devices == nil {
reg.devices = make(map[string]*registryEntry)
}
reg.devices[id] = e
isNew = true
}
// Update our device group accounting.
reg.updateOrdinalLocked(e, isNew)
if isNew {
// Unregister the device from the Registry when it is Done.
go e.manageEntryLifecycle()
}
}
// checkDeviceRegistration checks that the device for id is completely
// registered and up-to-date under read lock. This is faster than taking a
// write lock, and will generally be true for all devices.
func (reg *Registry) checkDeviceRegistration(id string) bool {
reg.mu.RLock()
defer reg.mu.RUnlock()
// Is the device registered?
e := reg.devices[id]
if e == nil {
return false
}
// Is the device properly registered for its group and ordinal?
ord := e.device.Ordinal()
if _, ok := reg.groupMap[ord.Group][e]; !ok {
return false
}
if _, ok := reg.ordinalMap[ord][e]; !ok {
return false
}
// Finally, is the device Done? If so, we will have to reregister.
if IsDone(e.device) {
return false
}
return true
}
// Get returns the registered device for the specified ID.
//
// If no device is registered for this ID, Get will return nil.
func (reg *Registry) Get(id string) D {
reg.mu.RLock()
defer reg.mu.RUnlock()
e := reg.devices[id]
if e == nil {
return nil
}
// Omit this device if it's Done.
if IsDone(e.device) {
return nil
}
return e.device
}
// GetUniqueOrdinal returns the registered device for the specified ordinal.
//
// If there is no device that is uniquely registered for o, GetOrdinal will
// return nil.
func (reg *Registry) GetUniqueOrdinal(o Ordinal) D {
reg.mu.RLock()
defer reg.mu.RUnlock()
emap := reg.ordinalMap[o]
if len(emap) == 1 {
// Exactly one device is registered for this Ordinal.
for e := range emap {
// Omit this device if it's Done.
if IsDone(e.device) {
return nil
}
return e.device
}
}
return nil
}
// AllGroups returns all registered groups and their respective devices.
func (reg *Registry) AllGroups() map[int][]D {
reg.mu.RLock()
defer reg.mu.RUnlock()
if len(reg.groupMap) == 0 {
return nil
}
groups := make(map[int][]D, len(reg.groupMap))
for group := range reg.groupMap {
devices := reg.devicesForGroupLocked(group)
if len(devices) > 0 {
groups[group] = devices
}
}
return groups
}
// DevicesForGroup returns the devices for the specified group.
//
// If no devices are registered in that group, it will return an empty slice.
func (reg *Registry) DevicesForGroup(group int) []D {
reg.mu.RLock()
defer reg.mu.RUnlock()
return reg.devicesForGroupLocked(group)
}
func (reg *Registry) devicesForGroupLocked(group int) []D {
devices := reg.groupMap[group]
if len(devices) == 0 {
return nil
}
result := make([]D, 0, len(devices))
for e := range devices {
// Omit this device if it's Done.
if IsDone(e.device) {
continue
}
result = append(result, e.device)
}
sort.Slice(result, func(i, j int) bool {
return result[i].ID() < result[j].ID()
})
return result
}
func (reg *Registry) updateOrdinalLocked(e *registryEntry, isNew bool) {
ordinal := e.device.Ordinal()
// Registration in group map.
if isNew || e.registeredGroup != ordinal.Group {
// The device has never been registered, or has switched groups. Update.
if !isNew {
reg.removeFromGroupMapLocked(e)
}
if reg.groupMap == nil {
reg.groupMap = make(map[int]map[*registryEntry]struct{})
}
entryMap := reg.groupMap[ordinal.Group]
if entryMap == nil {
entryMap = make(map[*registryEntry]struct{})
reg.groupMap[ordinal.Group] = entryMap
}
entryMap[e] = struct{}{}
e.registeredGroup = ordinal.Group
}
// Registration in ordinal map.
if isNew || e.registeredOrdinal != ordinal {
// The device has never been registered, or has switched ordinals. Update.
if !isNew {
reg.removeFromOrdinalMapLocked(e)
}
if reg.ordinalMap == nil {
reg.ordinalMap = make(map[Ordinal]map[*registryEntry]struct{})
}
entryMap := reg.ordinalMap[ordinal]
if entryMap == nil {
entryMap = make(map[*registryEntry]struct{})
reg.ordinalMap[ordinal] = entryMap
}
entryMap[e] = struct{}{}
e.registeredOrdinal = ordinal
}
}
func (reg *Registry) removeFromGroupMapLocked(e *registryEntry) {
delete(reg.groupMap[e.registeredGroup], e)
if len(reg.groupMap[e.registeredGroup]) == 0 {
delete(reg.groupMap, e.registeredGroup)
}
e.registeredGroup = 0
}
func (reg *Registry) removeFromOrdinalMapLocked(e *registryEntry) {
delete(reg.ordinalMap[e.registeredOrdinal], e)
if len(reg.ordinalMap[e.registeredOrdinal]) == 0 {
delete(reg.ordinalMap, e.registeredOrdinal)
}
e.registeredOrdinal = Ordinal{}
}
func (reg *Registry) unregisterDoneEntriesLocked() {
for _, e := range reg.devices {
if IsDone(e.device) {
reg.unregisterEntryLocked(e)
}
}
}
func (reg *Registry) unregisterEntry(e *registryEntry) {
reg.mu.Lock()
defer reg.mu.Unlock()
reg.unregisterEntryLocked(e)
}
func (reg *Registry) unregisterEntryLocked(e *registryEntry) {
if re := reg.devices[e.deviceID]; re != e {
// This device is already unregistered. This can happen if the entry
// self-unregisters while it's shutting itself down, but it's already been
// explicitly deleted.
return
}
// Remove this entry from its group/ordinal maps.
reg.removeFromGroupMapLocked(e)
reg.removeFromOrdinalMapLocked(e)
// Remove this entry from the devices map.
delete(reg.devices, e.deviceID)
}
type registryEntry struct {
// reg is the parent registry.
reg *Registry
// device is the discovered device entry.
device D
// deviceID is a copy of device's ID.
deviceID string
registeredGroup int
registeredOrdinal Ordinal
}
func (e *registryEntry) manageEntryLifecycle() {
<-e.device.DoneC()
e.reg.unregisterEntry(e)
}