@@ -87,6 +87,27 @@ const (
87
87
initStatsWeight = 1
88
88
)
89
89
90
+ // connReq represents a request for peer connection.
91
+ type connReq struct {
92
+ p * peer
93
+ ip net.IP
94
+ port uint16
95
+ result chan * poolEntry
96
+ }
97
+
98
+ // disconnReq represents a request for peer disconnection.
99
+ type disconnReq struct {
100
+ entry * poolEntry
101
+ stopped bool
102
+ done chan struct {}
103
+ }
104
+
105
+ // registerReq represents a request for peer registration.
106
+ type registerReq struct {
107
+ entry * poolEntry
108
+ done chan struct {}
109
+ }
110
+
90
111
// serverPool implements a pool for storing and selecting newly discovered and already
91
112
// known light server nodes. It received discovered nodes, stores statistics about
92
113
// known nodes and takes care of always having enough good quality servers connected.
@@ -105,10 +126,13 @@ type serverPool struct {
105
126
discLookups chan bool
106
127
107
128
entries map [discover.NodeID ]* poolEntry
108
- lock sync.Mutex
109
129
timeout , enableRetry chan * poolEntry
110
130
adjustStats chan poolStatAdjust
111
131
132
+ connCh chan * connReq
133
+ disconnCh chan * disconnReq
134
+ registerCh chan * registerReq
135
+
112
136
knownQueue , newQueue poolEntryQueue
113
137
knownSelect , newSelect * weightedRandomSelect
114
138
knownSelected , newSelected int
@@ -125,6 +149,9 @@ func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup) *s
125
149
timeout : make (chan * poolEntry , 1 ),
126
150
adjustStats : make (chan poolStatAdjust , 100 ),
127
151
enableRetry : make (chan * poolEntry , 1 ),
152
+ connCh : make (chan * connReq ),
153
+ disconnCh : make (chan * disconnReq ),
154
+ registerCh : make (chan * registerReq ),
128
155
knownSelect : newWeightedRandomSelect (),
129
156
newSelect : newWeightedRandomSelect (),
130
157
fastDiscover : true ,
@@ -147,9 +174,8 @@ func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) {
147
174
pool .discLookups = make (chan bool , 100 )
148
175
go pool .server .DiscV5 .SearchTopic (pool .topic , pool .discSetPeriod , pool .discNodes , pool .discLookups )
149
176
}
150
-
151
- go pool .eventLoop ()
152
177
pool .checkDial ()
178
+ go pool .eventLoop ()
153
179
}
154
180
155
181
// connect should be called upon any incoming connection. If the connection has been
@@ -158,83 +184,44 @@ func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) {
158
184
// Note that whenever a connection has been accepted and a pool entry has been returned,
159
185
// disconnect should also always be called.
160
186
func (pool * serverPool ) connect (p * peer , ip net.IP , port uint16 ) * poolEntry {
161
- pool .lock .Lock ()
162
- defer pool .lock .Unlock ()
163
- entry := pool .entries [p .ID ()]
164
- if entry == nil {
165
- entry = pool .findOrNewNode (p .ID (), ip , port )
166
- }
167
- p .Log ().Debug ("Connecting to new peer" , "state" , entry .state )
168
- if entry .state == psConnected || entry .state == psRegistered {
187
+ log .Debug ("Connect new entry" , "enode" , p .id )
188
+ req := & connReq {p : p , ip : ip , port : port , result : make (chan * poolEntry , 1 )}
189
+ select {
190
+ case pool .connCh <- req :
191
+ case <- pool .quit :
169
192
return nil
170
193
}
171
- pool .connWg .Add (1 )
172
- entry .peer = p
173
- entry .state = psConnected
174
- addr := & poolEntryAddress {
175
- ip : ip ,
176
- port : port ,
177
- lastSeen : mclock .Now (),
178
- }
179
- entry .lastConnected = addr
180
- entry .addr = make (map [string ]* poolEntryAddress )
181
- entry .addr [addr .strKey ()] = addr
182
- entry .addrSelect = * newWeightedRandomSelect ()
183
- entry .addrSelect .update (addr )
184
- return entry
194
+ return <- req .result
185
195
}
186
196
187
197
// registered should be called after a successful handshake
188
198
func (pool * serverPool ) registered (entry * poolEntry ) {
189
199
log .Debug ("Registered new entry" , "enode" , entry .id )
190
- pool .lock .Lock ()
191
- defer pool .lock .Unlock ()
192
-
193
- entry .state = psRegistered
194
- entry .regTime = mclock .Now ()
195
- if ! entry .known {
196
- pool .newQueue .remove (entry )
197
- entry .known = true
200
+ req := & registerReq {entry : entry , done : make (chan struct {})}
201
+ select {
202
+ case pool .registerCh <- req :
203
+ case <- pool .quit :
204
+ return
198
205
}
199
- pool .knownQueue .setLatest (entry )
200
- entry .shortRetry = shortRetryCnt
206
+ <- req .done
201
207
}
202
208
203
209
// disconnect should be called when ending a connection. Service quality statistics
204
210
// can be updated optionally (not updated if no registration happened, in this case
205
211
// only connection statistics are updated, just like in case of timeout)
206
212
func (pool * serverPool ) disconnect (entry * poolEntry ) {
207
- log .Debug ("Disconnected old entry" , "enode" , entry .id )
208
- pool .lock .Lock ()
209
- defer pool .lock .Unlock ()
210
-
211
- if entry .state == psRegistered {
212
- connTime := mclock .Now () - entry .regTime
213
- connAdjust := float64 (connTime ) / float64 (targetConnTime )
214
- if connAdjust > 1 {
215
- connAdjust = 1
216
- }
217
- stopped := false
218
- select {
219
- case <- pool .quit :
220
- stopped = true
221
- default :
222
- }
223
- if stopped {
224
- entry .connectStats .add (1 , connAdjust )
225
- } else {
226
- entry .connectStats .add (connAdjust , 1 )
227
- }
213
+ stopped := false
214
+ select {
215
+ case <- pool .quit :
216
+ stopped = true
217
+ default :
228
218
}
219
+ log .Debug ("Disconnected old entry" , "enode" , entry .id )
220
+ req := & disconnReq {entry : entry , stopped : stopped , done : make (chan struct {})}
229
221
230
- entry .state = psNotConnected
231
- if entry .knownSelected {
232
- pool .knownSelected --
233
- } else {
234
- pool .newSelected --
235
- }
236
- pool .setRetryDial (entry )
237
- pool .connWg .Done ()
222
+ // Block until disconnection request is served.
223
+ pool .disconnCh <- req
224
+ <- req .done
238
225
}
239
226
240
227
const (
@@ -277,25 +264,51 @@ func (pool *serverPool) eventLoop() {
277
264
if pool .discSetPeriod != nil {
278
265
pool .discSetPeriod <- time .Millisecond * 100
279
266
}
267
+
268
+ // disconnect updates service quality statistics depending on the connection time
269
+ // and disconnection initiator.
270
+ disconnect := func (req * disconnReq , stopped bool ) {
271
+ // Handle peer disconnection requests.
272
+ entry := req .entry
273
+ if entry .state == psRegistered {
274
+ connAdjust := float64 (mclock .Now ()- entry .regTime ) / float64 (targetConnTime )
275
+ if connAdjust > 1 {
276
+ connAdjust = 1
277
+ }
278
+ if stopped {
279
+ // disconnect requested by ourselves.
280
+ entry .connectStats .add (1 , connAdjust )
281
+ } else {
282
+ // disconnect requested by server side.
283
+ entry .connectStats .add (connAdjust , 1 )
284
+ }
285
+ }
286
+ entry .state = psNotConnected
287
+
288
+ if entry .knownSelected {
289
+ pool .knownSelected --
290
+ } else {
291
+ pool .newSelected --
292
+ }
293
+ pool .setRetryDial (entry )
294
+ pool .connWg .Done ()
295
+ close (req .done )
296
+ }
297
+
280
298
for {
281
299
select {
282
300
case entry := <- pool .timeout :
283
- pool .lock .Lock ()
284
301
if ! entry .removed {
285
302
pool .checkDialTimeout (entry )
286
303
}
287
- pool .lock .Unlock ()
288
304
289
305
case entry := <- pool .enableRetry :
290
- pool .lock .Lock ()
291
306
if ! entry .removed {
292
307
entry .delayedRetry = false
293
308
pool .updateCheckDial (entry )
294
309
}
295
- pool .lock .Unlock ()
296
310
297
311
case adj := <- pool .adjustStats :
298
- pool .lock .Lock ()
299
312
switch adj .adjustType {
300
313
case pseBlockDelay :
301
314
adj .entry .delayStats .add (float64 (adj .time ), 1 )
@@ -305,13 +318,10 @@ func (pool *serverPool) eventLoop() {
305
318
case pseResponseTimeout :
306
319
adj .entry .timeoutStats .add (1 , 1 )
307
320
}
308
- pool .lock .Unlock ()
309
321
310
322
case node := <- pool .discNodes :
311
- pool .lock .Lock ()
312
323
entry := pool .findOrNewNode (discover .NodeID (node .ID ), node .IP , node .TCP )
313
324
pool .updateCheckDial (entry )
314
- pool .lock .Unlock ()
315
325
316
326
case conv := <- pool .discLookups :
317
327
if conv {
@@ -327,15 +337,66 @@ func (pool *serverPool) eventLoop() {
327
337
}
328
338
}
329
339
340
+ case req := <- pool .connCh :
341
+ // Handle peer connection requests.
342
+ entry := pool .entries [req .p .ID ()]
343
+ if entry == nil {
344
+ entry = pool .findOrNewNode (req .p .ID (), req .ip , req .port )
345
+ }
346
+ if entry .state == psConnected || entry .state == psRegistered {
347
+ req .result <- nil
348
+ continue
349
+ }
350
+ pool .connWg .Add (1 )
351
+ entry .peer = req .p
352
+ entry .state = psConnected
353
+ addr := & poolEntryAddress {
354
+ ip : req .ip ,
355
+ port : req .port ,
356
+ lastSeen : mclock .Now (),
357
+ }
358
+ entry .lastConnected = addr
359
+ entry .addr = make (map [string ]* poolEntryAddress )
360
+ entry .addr [addr .strKey ()] = addr
361
+ entry .addrSelect = * newWeightedRandomSelect ()
362
+ entry .addrSelect .update (addr )
363
+ req .result <- entry
364
+
365
+ case req := <- pool .registerCh :
366
+ // Handle peer registration requests.
367
+ entry := req .entry
368
+ entry .state = psRegistered
369
+ entry .regTime = mclock .Now ()
370
+ if ! entry .known {
371
+ pool .newQueue .remove (entry )
372
+ entry .known = true
373
+ }
374
+ pool .knownQueue .setLatest (entry )
375
+ entry .shortRetry = shortRetryCnt
376
+ close (req .done )
377
+
378
+ case req := <- pool .disconnCh :
379
+ // Handle peer disconnection requests.
380
+ disconnect (req , req .stopped )
381
+
330
382
case <- pool .quit :
331
383
if pool .discSetPeriod != nil {
332
384
close (pool .discSetPeriod )
333
385
}
334
- pool .connWg .Wait ()
386
+
387
+ // Spawn a goroutine to close the disconnCh after all connections are disconnected.
388
+ go func () {
389
+ pool .connWg .Wait ()
390
+ close (pool .disconnCh )
391
+ }()
392
+
393
+ // Handle all remaining disconnection requests before exit.
394
+ for req := range pool .disconnCh {
395
+ disconnect (req , true )
396
+ }
335
397
pool .saveNodes ()
336
398
pool .wg .Done ()
337
399
return
338
-
339
400
}
340
401
}
341
402
}
0 commit comments