Skip to content

Commit 0b7d687

Browse files
authored
fix: possible race condition in ping timer (#1848)
Ref: #1845
1 parent a24cf14 commit 0b7d687

File tree

4 files changed

+39
-20
lines changed

4 files changed

+39
-20
lines changed

package-lock.json

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib/PingTimer.ts

+20-6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export default class PingTimer {
1010

1111
private checkPing: () => void
1212

13+
private destroyed = false
14+
1315
constructor(
1416
keepalive: number,
1517
checkPing: () => void,
@@ -21,22 +23,34 @@ export default class PingTimer {
2123
this.reschedule()
2224
}
2325

24-
clear() {
26+
private clear() {
2527
if (this.timerId) {
2628
this.timer.clear(this.timerId)
2729
this.timerId = null
2830
}
2931
}
3032

33+
destroy() {
34+
this.clear()
35+
this.destroyed = true
36+
}
37+
3138
reschedule() {
39+
if (this.destroyed) {
40+
return
41+
}
42+
3243
this.clear()
3344
this.timerId = this.timer.set(() => {
34-
this.checkPing()
35-
// prevent possible race condition where the timer is destroyed on _cleauUp
36-
// and recreated here
37-
if (this.timerId) {
38-
this.reschedule()
45+
// this should never happen, but just in case
46+
if (this.destroyed) {
47+
return
3948
}
49+
50+
this.checkPing()
51+
// this must be called after `checkPing` otherwise in case `destroy`
52+
// is called in `checkPing` the timer would be rescheduled anyway
53+
this.reschedule()
4054
}, this.keepalive)
4155
}
4256
}

src/lib/client.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -659,9 +659,9 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac
659659
this.log('close :: clearing connackTimer')
660660
clearTimeout(this.connackTimer)
661661

662-
this.log('close :: clearing ping timer')
662+
this.log('close :: destroy ping timer')
663663
if (this.pingTimer) {
664-
this.pingTimer.clear()
664+
this.pingTimer.destroy()
665665
this.pingTimer = null
666666
}
667667

@@ -1783,8 +1783,8 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac
17831783
}
17841784

17851785
if (this.pingTimer) {
1786-
this.log('_cleanUp :: clearing pingTimer')
1787-
this.pingTimer.clear()
1786+
this.log('_cleanUp :: destroy pingTimer')
1787+
this.pingTimer.destroy()
17881788
this.pingTimer = null
17891789
}
17901790

test/pingTimer.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('PingTimer', () => {
1313
clock.restore()
1414
})
1515

16-
it('should schedule and clear', () => {
16+
it('should schedule and destroy', () => {
1717
const keepalive = 10 // seconds
1818
const cb = spy()
1919
const pingTimer = new PingTimer(keepalive, cb, 'auto')
@@ -28,10 +28,15 @@ describe('PingTimer', () => {
2828
)
2929
clock.tick(keepalive * 1000 + 1)
3030
assert.equal(cb.callCount, 2, 'should reschedule automatically')
31-
pingTimer.clear()
31+
pingTimer.destroy()
3232
assert.ok(
3333
!pingTimer['timerId'],
34-
'timer should not exists after clear()',
34+
'timer should not exists after destroy()',
35+
)
36+
37+
assert.ok(
38+
pingTimer['destroyed'],
39+
'timer should have `destroyed` set to true after destroy()',
3540
)
3641
})
3742

@@ -41,7 +46,7 @@ describe('PingTimer', () => {
4146
const pingTimer = new PingTimer(
4247
keepalive,
4348
() => {
44-
pingTimer.clear()
49+
pingTimer.destroy()
4550
cb()
4651
},
4752
'auto',

0 commit comments

Comments
 (0)