@@ -107,8 +107,12 @@ type Connection struct {
107
107
108
108
vppConnected uint32 // non-zero if the adapter is connected to VPP
109
109
110
- connChan chan ConnectionEvent // connection status events are sent to this channel
111
- healthCheckDone chan struct {} // used to terminate health check loop
110
+ connChan chan ConnectionEvent // connection status events are sent to this channel
111
+ healthCheckDone chan struct {} // used to terminate connect/health check loop
112
+ backgroundLoopActive uint32 // used to guard background loop from double close errors
113
+
114
+ async bool // connection to be operated in async mode
115
+ healthCheckExited chan struct {} // used to notify Disconnect() callers about healthcheck loop exit
112
116
113
117
codec MessageCodec // message codec
114
118
msgIDs map [string ]uint16 // map of message IDs indexed by message name + CRC
@@ -135,7 +139,14 @@ type Connection struct {
135
139
apiTrace * trace // API tracer (disabled by default)
136
140
}
137
141
138
- func newConnection (binapi adapter.VppAPI , attempts int , interval time.Duration ) * Connection {
142
+ type backgroundLoopStatus int
143
+
144
+ const (
145
+ terminate backgroundLoopStatus = iota
146
+ resume
147
+ )
148
+
149
+ func newConnection (binapi adapter.VppAPI , attempts int , interval time.Duration , async bool ) * Connection {
139
150
if attempts == 0 {
140
151
attempts = DefaultMaxReconnectAttempts
141
152
}
@@ -149,6 +160,8 @@ func newConnection(binapi adapter.VppAPI, attempts int, interval time.Duration)
149
160
recInterval : interval ,
150
161
connChan : make (chan ConnectionEvent , NotificationChanBufSize ),
151
162
healthCheckDone : make (chan struct {}),
163
+ healthCheckExited : make (chan struct {}),
164
+ async : async ,
152
165
codec : codec .DefaultCodec ,
153
166
msgIDs : make (map [string ]uint16 ),
154
167
msgMapByPath : make (map [string ]map [uint16 ]api.Message ),
@@ -190,7 +203,7 @@ func newConnection(binapi adapter.VppAPI, attempts int, interval time.Duration)
190
203
// Only one connection attempt will be performed.
191
204
func Connect (binapi adapter.VppAPI ) (* Connection , error ) {
192
205
// create new connection handle
193
- c := newConnection (binapi , DefaultMaxReconnectAttempts , DefaultReconnectInterval )
206
+ c := newConnection (binapi , DefaultMaxReconnectAttempts , DefaultReconnectInterval , false )
194
207
195
208
// blocking attempt to connect to VPP
196
209
if err := c .connectVPP (); err != nil {
@@ -205,13 +218,16 @@ func Connect(binapi adapter.VppAPI) (*Connection, error) {
205
218
// returns immediately. The caller is supposed to watch the returned ConnectionState channel for
206
219
// Connected/Disconnected events. In case of disconnect, the library will asynchronously try to reconnect.
207
220
func AsyncConnect (binapi adapter.VppAPI , attempts int , interval time.Duration ) (* Connection , chan ConnectionEvent , error ) {
221
+
208
222
// create new connection handle
209
- c := newConnection (binapi , attempts , interval )
223
+ conn := newConnection (binapi , attempts , interval , true )
224
+
225
+ atomic .StoreUint32 (& conn .backgroundLoopActive , 1 )
210
226
211
227
// asynchronously attempt to connect to VPP
212
- go c . connectLoop ()
228
+ go conn . backgroudConnectionLoop ()
213
229
214
- return c , c .connChan , nil
230
+ return conn , conn .connChan , nil
215
231
}
216
232
217
233
// connectVPP performs blocking attempt to connect to VPP.
@@ -242,19 +258,24 @@ func (c *Connection) Disconnect() {
242
258
if c == nil {
243
259
return
244
260
}
261
+
262
+ if c .async {
263
+ if atomic .CompareAndSwapUint32 (& c .backgroundLoopActive , 1 , 0 ) {
264
+ close (c .healthCheckDone )
265
+ }
266
+
267
+ // Wait for the connect/healthcheck loop termination
268
+ <- c .healthCheckExited
269
+ }
270
+
245
271
if c .vppClient != nil {
246
- c .disconnectVPP (true )
272
+ c .disconnectVPP ()
247
273
}
248
274
}
249
275
250
- // disconnectVPP disconnects from VPP in case it is connected. terminate tells
251
- // that disconnectVPP() was called from Close(), so healthCheckLoop() can be
252
- // terminated.
253
- func (c * Connection ) disconnectVPP (terminate bool ) {
276
+ // disconnectVPP disconnects from VPP in case it is connected
277
+ func (c * Connection ) disconnectVPP () {
254
278
if atomic .CompareAndSwapUint32 (& c .vppConnected , 1 , 0 ) {
255
- if terminate {
256
- close (c .healthCheckDone )
257
- }
258
279
log .Debug ("Disconnecting from VPP.." )
259
280
260
281
if err := c .vppClient .Disconnect (); err != nil {
@@ -303,9 +324,24 @@ func (c *Connection) releaseAPIChannel(ch *Channel) {
303
324
go c .channelPool .Put (ch )
304
325
}
305
326
327
+ // runs connectionLoop and healthCheckLoop until they fail
328
+ func (c * Connection ) backgroudConnectionLoop () {
329
+ defer close (c .healthCheckExited )
330
+
331
+ for {
332
+ if c .connectLoop () == terminate {
333
+ return
334
+ }
335
+
336
+ if c .healthCheckLoop () == terminate {
337
+ return
338
+ }
339
+ }
340
+ }
341
+
306
342
// connectLoop attempts to connect to VPP until it succeeds.
307
343
// Then it continues with healthCheckLoop.
308
- func (c * Connection ) connectLoop () {
344
+ func (c * Connection ) connectLoop () backgroundLoopStatus {
309
345
var reconnectAttempts int
310
346
311
347
// loop until connected
@@ -316,29 +352,33 @@ func (c *Connection) connectLoop() {
316
352
if err := c .connectVPP (); err == nil {
317
353
// signal connected event
318
354
c .sendConnEvent (ConnectionEvent {Timestamp : time .Now (), State : Connected })
319
- break
355
+ return resume
320
356
} else if reconnectAttempts < c .maxAttempts {
321
357
reconnectAttempts ++
322
358
log .Warnf ("connecting failed (attempt %d/%d): %v" , reconnectAttempts , c .maxAttempts , err )
323
- time .Sleep (c .recInterval )
324
359
} else {
325
360
c .sendConnEvent (ConnectionEvent {Timestamp : time .Now (), State : Failed , Error : err })
326
- return
361
+ return terminate
327
362
}
328
- }
329
363
330
- // we are now connected, continue with health check loop
331
- c .healthCheckLoop ()
364
+ select {
365
+ case <- c .healthCheckDone :
366
+ // Terminate the connect loop on connection disconnect
367
+ log .Debug ("Disconnected on request, exiting connect loop." )
368
+ return terminate
369
+ case <- time .After (c .recInterval ):
370
+ }
371
+ }
332
372
}
333
373
334
374
// healthCheckLoop checks whether connection to VPP is alive. In case of disconnect,
335
375
// it continues with connectLoop and tries to reconnect.
336
- func (c * Connection ) healthCheckLoop () {
376
+ func (c * Connection ) healthCheckLoop () backgroundLoopStatus {
337
377
// create a separate API channel for health check probes
338
378
ch , err := c .newAPIChannel (1 , 1 )
339
379
if err != nil {
340
380
log .Error ("Failed to create health check API channel, health check will be disabled:" , err )
341
- return
381
+ return terminate
342
382
}
343
383
defer ch .Close ()
344
384
@@ -357,7 +397,7 @@ HealthCheck:
357
397
case <- c .healthCheckDone :
358
398
// Terminate the health check loop on connection disconnect
359
399
log .Debug ("Disconnected on request, exiting health check loop." )
360
- return
400
+ return terminate
361
401
case <- probeInterval .C :
362
402
// try draining probe replies from previous request before sending next one
363
403
select {
@@ -415,10 +455,9 @@ HealthCheck:
415
455
}
416
456
417
457
// cleanup
418
- c .disconnectVPP (false )
458
+ c .disconnectVPP ()
419
459
420
- // we are now disconnected, start connect loop
421
- c .connectLoop ()
460
+ return resume
422
461
}
423
462
424
463
func getMsgNameWithCrc (x api.Message ) string {
0 commit comments