@@ -23,10 +23,11 @@ class DataProxyConnect: DataProxy {
23
23
private var idToken : String ?
24
24
25
25
private var webSocket : WebSocket ?
26
- private var handshakeTimer : Timer ?
27
- private var pingTimer : Timer ?
26
+ private var handshakeTimer : DispatchSourceTimer ?
27
+ private var pingTimer : DispatchSourceTimer ?
28
+ private static let pingFrequency : TimeInterval = 5 * 60
28
29
29
- private var connectionRecoveryTimer : Timer ?
30
+ private var connectionRecoveryTimer : DispatchSourceTimer ?
30
31
private var connectionRecoveryCount = 0
31
32
//The max num of attempts before capping the delay time - not before giving up
32
33
private static let connectionRecoveryCountMax = 8
@@ -40,12 +41,24 @@ class DataProxyConnect: DataProxy {
40
41
self . idToken = idToken
41
42
}
42
43
43
- @objc func startServer( ) {
44
+ deinit {
45
+ //Ensure the server proxy isn't running when we go out of scope
46
+ assert ( !isActive, " DataProxyConnect was deinitialized while active " )
47
+
48
+ //Ensure timers are cleaned up
49
+ assert ( pingTimer == nil , " DataProxyConnect was deinitialized with an active ping timer " )
50
+ assert ( handshakeTimer == nil , " DataProxyConnect was deinitialized with an active handshake timer " )
51
+ assert ( connectionRecoveryTimer == nil , " DataProxyConnect was deinitialized with an active connection recovery timer " )
52
+ }
53
+
54
+ func startServer( ) {
44
55
//Ignore if we're already connecting or connected
45
56
guard !isActive else { return }
46
57
47
58
//Cancel any active connection recover timers (in case the user initiated a reconnect)
48
- stopConnectionRecoveryTimer ( )
59
+ processingQueue. sync {
60
+ stopConnectionRecoveryTimer ( )
61
+ }
49
62
50
63
//Build the URL
51
64
var queryItems = [
@@ -76,29 +89,24 @@ class DataProxyConnect: DataProxy {
76
89
}
77
90
78
91
func stopServer( ) {
92
+ //Ignore if we're not running
93
+ guard isActive else { return }
94
+
79
95
processingQueue. sync {
80
- //Ignore if we're not connected
81
- guard isActive, let socket = webSocket else { return }
82
-
83
- //Cancel the handshake timer
84
- stopHandshakeTimer ( )
96
+ let socket = webSocket!
85
97
86
98
//Clear connected clients
87
99
connectionsLock. withWriteLock {
88
100
connectionsMap. removeAll ( )
89
101
}
90
102
NotificationNames . postUpdateConnectionCount ( 0 )
91
103
92
- //Disconect
104
+ //Disconnect
93
105
socket. disconnect ( )
94
- isActive = false
95
106
delegate? . dataProxy ( self , didStopWithState: . stopped, isRecoverable: false )
96
107
}
97
- }
98
-
99
- deinit {
100
- //Make sure we stop the server when we go out of scope
101
- stopServer ( )
108
+
109
+ isActive = false
102
110
}
103
111
104
112
func send( message data: Data , to client: ClientConnection ? , encrypt: Bool , onSent: ( ( ) -> ( ) ) ? ) {
@@ -193,23 +201,28 @@ class DataProxyConnect: DataProxy {
193
201
assertDispatchQueue ( processingQueue)
194
202
195
203
//Cancel the old timer
196
- pingTimer? . invalidate ( )
204
+ pingTimer? . cancel ( )
197
205
198
206
//Create the new timer
199
- let timer = Timer ( timeInterval: 5 * 60 , target: self , selector: #selector( onPingTimer) , userInfo: nil , repeats: true )
200
- RunLoop . main. add ( timer, forMode: . common)
207
+ let timer = DispatchSource . makeTimerSource ( queue: processingQueue)
208
+ timer. schedule ( deadline: . now( ) + DataProxyConnect. pingFrequency, repeating: DataProxyConnect . pingFrequency)
209
+ timer. setEventHandler ( handler: onPingTimer)
210
+ timer. resume ( )
201
211
pingTimer = timer
202
212
}
203
213
204
214
private func stopPingTimer( ) {
205
215
//Make sure we're on the processing queue
206
216
assertDispatchQueue ( processingQueue)
207
217
208
- pingTimer? . invalidate ( )
218
+ pingTimer? . cancel ( )
209
219
pingTimer = nil
210
220
}
211
221
212
- @objc private func onPingTimer( ) {
222
+ private func onPingTimer( ) {
223
+ //Make sure we're on the processing queue
224
+ assertDispatchQueue ( processingQueue)
225
+
213
226
//Ping
214
227
webSocket? . write ( ping: Data ( ) )
215
228
}
@@ -221,40 +234,54 @@ class DataProxyConnect: DataProxy {
221
234
assertDispatchQueue ( processingQueue)
222
235
223
236
//Cancel the old timer
224
- handshakeTimer? . invalidate ( )
237
+ handshakeTimer? . cancel ( )
225
238
226
239
//Create the new timer
227
- let timer = Timer ( timeInterval: ConnectConstants . handshakeTimeout, target: self , selector: #selector( onHandshakeTimer) , userInfo: nil , repeats: false )
228
- RunLoop . main. add ( timer, forMode: . common)
240
+ let timer = DispatchSource . makeTimerSource ( queue: processingQueue)
241
+ timer. schedule ( deadline: . now( ) + ConnectConstants. handshakeTimeout, repeating: . never)
242
+ timer. setEventHandler ( handler: onHandshakeTimer)
243
+ timer. resume ( )
229
244
handshakeTimer = timer
230
245
}
231
246
232
247
private func stopHandshakeTimer( ) {
233
248
//Make sure we're on the processing queue
234
249
assertDispatchQueue ( processingQueue)
235
250
236
- handshakeTimer? . invalidate ( )
251
+ handshakeTimer? . cancel ( )
237
252
handshakeTimer = nil
238
253
}
239
254
240
- @objc private func onHandshakeTimer( ) {
255
+ private func onHandshakeTimer( ) {
256
+ //Make sure we're on the processing queue
257
+ assertDispatchQueue ( processingQueue)
258
+
241
259
//Disconnect
242
260
webSocket? . disconnect ( )
261
+
262
+ //Clean up
263
+ stopHandshakeTimer ( )
243
264
}
244
265
245
266
//MARK: Connection recovery timer
246
267
247
268
private func startConnectionRecoveryTimer( ) {
269
+ //Make sure we're on the processing queue
270
+ assertDispatchQueue ( processingQueue)
271
+
248
272
//Cancel the old timer
249
- connectionRecoveryTimer? . invalidate ( )
273
+ connectionRecoveryTimer? . cancel ( )
250
274
251
275
//Wait an exponentially increasing wait period + a random delay
252
276
let randomOffset : TimeInterval = Double ( arc4random ( ) ) / Double( UInt32 . max)
253
277
let delay : TimeInterval = pow ( 2 , Double ( connectionRecoveryCount) ) + randomOffset
254
278
255
279
//Create the new timer
256
- let timer = Timer ( timeInterval: delay, target: self , selector: #selector( startServer) , userInfo: nil , repeats: false )
257
- RunLoop . main. add ( timer, forMode: . common)
280
+ //startServer() must be invoked from the main thread
281
+ let timer = DispatchSource . makeTimerSource ( queue: DispatchQueue . main)
282
+ timer. schedule ( deadline: . now( ) + delay, repeating: . never)
283
+ timer. setEventHandler ( handler: startServer)
284
+ timer. resume ( )
258
285
connectionRecoveryTimer = timer
259
286
260
287
//Add to the attempt counter
@@ -264,7 +291,10 @@ class DataProxyConnect: DataProxy {
264
291
}
265
292
266
293
private func stopConnectionRecoveryTimer( ) {
267
- connectionRecoveryTimer? . invalidate ( )
294
+ //Make sure we're on the processing queue
295
+ assertDispatchQueue ( processingQueue)
296
+
297
+ connectionRecoveryTimer? . cancel ( )
268
298
connectionRecoveryTimer = nil
269
299
}
270
300
0 commit comments