@@ -57,6 +57,12 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
5757 private var idleWriteTimeoutStateMachine : IdleWriteStateMachine ?
5858 private var idleWriteTimeoutTimer : Scheduled < Void > ?
5959
60+ /// Cancelling a task in NIO does *not* guarantee that the task will not execute under certain race conditions.
61+ /// We therefore give each timer an ID and increase the ID every time we reset or cancel it.
62+ /// We check in the task if the timer ID has changed in the meantime and do not execute any action if has changed.
63+ private var currentIdleReadTimeoutTimerID : Int = 0
64+ private var currentIdleWriteTimeoutTimerID : Int = 0
65+
6066 init ( eventLoop: EventLoop ) {
6167 self . eventLoop = eventLoop
6268 }
@@ -295,8 +301,9 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
295301 case . startIdleReadTimeoutTimer( let timeAmount) :
296302 assert ( self . idleReadTimeoutTimer == nil , " Expected there is no timeout timer so far. " )
297303
304+ let timerID = self . currentIdleReadTimeoutTimerID
298305 self . idleReadTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
299- guard self . idleReadTimeoutTimer != nil else { return }
306+ guard self . currentIdleReadTimeoutTimerID == timerID else { return }
300307 let action = self . state. idleReadTimeoutTriggered ( )
301308 self . run ( action, context: context)
302309 }
@@ -306,14 +313,17 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
306313 oldTimer. cancel ( )
307314 }
308315
316+ self . currentIdleReadTimeoutTimerID &+= 1
317+ let timerID = self . currentIdleReadTimeoutTimerID
309318 self . idleReadTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
310- guard self . idleReadTimeoutTimer != nil else { return }
319+ guard self . currentIdleReadTimeoutTimerID == timerID else { return }
311320 let action = self . state. idleReadTimeoutTriggered ( )
312321 self . run ( action, context: context)
313322 }
314323 case . clearIdleReadTimeoutTimer:
315324 if let oldTimer = self . idleReadTimeoutTimer {
316325 self . idleReadTimeoutTimer = nil
326+ self . currentIdleReadTimeoutTimerID &+= 1
317327 oldTimer. cancel ( )
318328 }
319329
@@ -327,8 +337,9 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
327337 case . startIdleWriteTimeoutTimer( let timeAmount) :
328338 assert ( self . idleWriteTimeoutTimer == nil , " Expected there is no timeout timer so far. " )
329339
340+ let timerID = self . currentIdleWriteTimeoutTimerID
330341 self . idleWriteTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
331- guard self . idleWriteTimeoutTimer != nil else { return }
342+ guard self . currentIdleWriteTimeoutTimerID == timerID else { return }
332343 let action = self . state. idleWriteTimeoutTriggered ( )
333344 self . run ( action, context: context)
334345 }
@@ -337,14 +348,17 @@ final class HTTP2ClientRequestHandler: ChannelDuplexHandler {
337348 oldTimer. cancel ( )
338349 }
339350
351+ self . currentIdleWriteTimeoutTimerID &+= 1
352+ let timerID = self . currentIdleWriteTimeoutTimerID
340353 self . idleWriteTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
341- guard self . idleWriteTimeoutTimer != nil else { return }
354+ guard self . currentIdleWriteTimeoutTimerID == timerID else { return }
342355 let action = self . state. idleWriteTimeoutTriggered ( )
343356 self . run ( action, context: context)
344357 }
345358 case . clearIdleWriteTimeoutTimer:
346359 if let oldTimer = self . idleWriteTimeoutTimer {
347360 self . idleWriteTimeoutTimer = nil
361+ self . currentIdleWriteTimeoutTimerID &+= 1
348362 oldTimer. cancel ( )
349363 }
350364 case . none:
0 commit comments