@@ -52,6 +52,11 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler {
52
52
private var idleReadTimeoutStateMachine : IdleReadStateMachine ?
53
53
private var idleReadTimeoutTimer : Scheduled < Void > ?
54
54
55
+ /// Cancelling a task in NIO does *not* guarantee that the task will not execute under certain race conditions.
56
+ /// We therefore give each timer an ID and increase the ID every time we reset or cancel it.
57
+ /// We check in the task if the timer ID has changed in the meantime and do not execute any action if has changed.
58
+ private var currentIdleReadTimeoutTimerID : Int = 0
59
+
55
60
private let backgroundLogger : Logger
56
61
private var logger : Logger
57
62
@@ -253,6 +258,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler {
253
258
254
259
let oldRequest = self . request!
255
260
self . request = nil
261
+ self . runTimeoutAction ( . clearIdleReadTimeoutTimer, context: context)
256
262
257
263
switch finalAction {
258
264
case . close:
@@ -271,6 +277,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler {
271
277
// see comment in the `succeedRequest` case.
272
278
let oldRequest = self . request!
273
279
self . request = nil
280
+ self . runTimeoutAction ( . clearIdleReadTimeoutTimer, context: context)
274
281
275
282
switch finalAction {
276
283
case . close:
@@ -292,7 +299,9 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler {
292
299
case . startIdleReadTimeoutTimer( let timeAmount) :
293
300
assert ( self . idleReadTimeoutTimer == nil , " Expected there is no timeout timer so far. " )
294
301
302
+ let timerID = self . currentIdleReadTimeoutTimerID
295
303
self . idleReadTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
304
+ guard self . currentIdleReadTimeoutTimerID == timerID else { return }
296
305
let action = self . state. idleReadTimeoutTriggered ( )
297
306
self . run ( action, context: context)
298
307
}
@@ -302,17 +311,19 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler {
302
311
oldTimer. cancel ( )
303
312
}
304
313
314
+ self . currentIdleReadTimeoutTimerID &+= 1
315
+ let timerID = self . currentIdleReadTimeoutTimerID
305
316
self . idleReadTimeoutTimer = self . eventLoop. scheduleTask ( in: timeAmount) {
317
+ guard self . currentIdleReadTimeoutTimerID == timerID else { return }
306
318
let action = self . state. idleReadTimeoutTriggered ( )
307
319
self . run ( action, context: context)
308
320
}
309
-
310
321
case . clearIdleReadTimeoutTimer:
311
322
if let oldTimer = self . idleReadTimeoutTimer {
312
323
self . idleReadTimeoutTimer = nil
324
+ self . currentIdleReadTimeoutTimerID &+= 1
313
325
oldTimer. cancel ( )
314
326
}
315
-
316
327
case . none:
317
328
break
318
329
}
@@ -465,7 +476,7 @@ struct IdleReadStateMachine {
465
476
return . resetIdleReadTimeoutTimer( self . timeAmount)
466
477
case . end:
467
478
self . state = . responseEndReceived
468
- return . clearIdleReadTimeoutTimer
479
+ return . none
469
480
}
470
481
471
482
case . responseEndReceived:
0 commit comments