@@ -23,10 +23,11 @@ class DataProxyConnect: DataProxy {
2323 private var idToken : String ?
2424
2525 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
2829
29- private var connectionRecoveryTimer : Timer ?
30+ private var connectionRecoveryTimer : DispatchSourceTimer ?
3031 private var connectionRecoveryCount = 0
3132 //The max num of attempts before capping the delay time - not before giving up
3233 private static let connectionRecoveryCountMax = 8
@@ -40,12 +41,24 @@ class DataProxyConnect: DataProxy {
4041 self . idToken = idToken
4142 }
4243
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( ) {
4455 //Ignore if we're already connecting or connected
4556 guard !isActive else { return }
4657
4758 //Cancel any active connection recover timers (in case the user initiated a reconnect)
48- stopConnectionRecoveryTimer ( )
59+ processingQueue. sync {
60+ stopConnectionRecoveryTimer ( )
61+ }
4962
5063 //Build the URL
5164 var queryItems = [
@@ -76,29 +89,24 @@ class DataProxyConnect: DataProxy {
7689 }
7790
7891 func stopServer( ) {
92+ //Ignore if we're not running
93+ guard isActive else { return }
94+
7995 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!
8597
8698 //Clear connected clients
8799 connectionsLock. withWriteLock {
88100 connectionsMap. removeAll ( )
89101 }
90102 NotificationNames . postUpdateConnectionCount ( 0 )
91103
92- //Disconect
104+ //Disconnect
93105 socket. disconnect ( )
94- isActive = false
95106 delegate? . dataProxy ( self , didStopWithState: . stopped, isRecoverable: false )
96107 }
97- }
98-
99- deinit {
100- //Make sure we stop the server when we go out of scope
101- stopServer ( )
108+
109+ isActive = false
102110 }
103111
104112 func send( message data: Data , to client: ClientConnection ? , encrypt: Bool , onSent: ( ( ) -> ( ) ) ? ) {
@@ -193,23 +201,28 @@ class DataProxyConnect: DataProxy {
193201 assertDispatchQueue ( processingQueue)
194202
195203 //Cancel the old timer
196- pingTimer? . invalidate ( )
204+ pingTimer? . cancel ( )
197205
198206 //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 ( )
201211 pingTimer = timer
202212 }
203213
204214 private func stopPingTimer( ) {
205215 //Make sure we're on the processing queue
206216 assertDispatchQueue ( processingQueue)
207217
208- pingTimer? . invalidate ( )
218+ pingTimer? . cancel ( )
209219 pingTimer = nil
210220 }
211221
212- @objc private func onPingTimer( ) {
222+ private func onPingTimer( ) {
223+ //Make sure we're on the processing queue
224+ assertDispatchQueue ( processingQueue)
225+
213226 //Ping
214227 webSocket? . write ( ping: Data ( ) )
215228 }
@@ -221,40 +234,54 @@ class DataProxyConnect: DataProxy {
221234 assertDispatchQueue ( processingQueue)
222235
223236 //Cancel the old timer
224- handshakeTimer? . invalidate ( )
237+ handshakeTimer? . cancel ( )
225238
226239 //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 ( )
229244 handshakeTimer = timer
230245 }
231246
232247 private func stopHandshakeTimer( ) {
233248 //Make sure we're on the processing queue
234249 assertDispatchQueue ( processingQueue)
235250
236- handshakeTimer? . invalidate ( )
251+ handshakeTimer? . cancel ( )
237252 handshakeTimer = nil
238253 }
239254
240- @objc private func onHandshakeTimer( ) {
255+ private func onHandshakeTimer( ) {
256+ //Make sure we're on the processing queue
257+ assertDispatchQueue ( processingQueue)
258+
241259 //Disconnect
242260 webSocket? . disconnect ( )
261+
262+ //Clean up
263+ stopHandshakeTimer ( )
243264 }
244265
245266 //MARK: Connection recovery timer
246267
247268 private func startConnectionRecoveryTimer( ) {
269+ //Make sure we're on the processing queue
270+ assertDispatchQueue ( processingQueue)
271+
248272 //Cancel the old timer
249- connectionRecoveryTimer? . invalidate ( )
273+ connectionRecoveryTimer? . cancel ( )
250274
251275 //Wait an exponentially increasing wait period + a random delay
252276 let randomOffset : TimeInterval = Double ( arc4random ( ) ) / Double( UInt32 . max)
253277 let delay : TimeInterval = pow ( 2 , Double ( connectionRecoveryCount) ) + randomOffset
254278
255279 //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 ( )
258285 connectionRecoveryTimer = timer
259286
260287 //Add to the attempt counter
@@ -264,7 +291,10 @@ class DataProxyConnect: DataProxy {
264291 }
265292
266293 private func stopConnectionRecoveryTimer( ) {
267- connectionRecoveryTimer? . invalidate ( )
294+ //Make sure we're on the processing queue
295+ assertDispatchQueue ( processingQueue)
296+
297+ connectionRecoveryTimer? . cancel ( )
268298 connectionRecoveryTimer = nil
269299 }
270300
0 commit comments