1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.timeout;
17
18 import io.netty.bootstrap.ServerBootstrap;
19 import io.netty.channel.Channel;
20 import io.netty.channel.Channel.Unsafe;
21 import io.netty.channel.ChannelDuplexHandler;
22 import io.netty.channel.ChannelFutureListener;
23 import io.netty.channel.ChannelHandlerContext;
24 import io.netty.channel.ChannelInitializer;
25 import io.netty.channel.ChannelOutboundBuffer;
26 import io.netty.channel.ChannelPromise;
27 import io.netty.util.concurrent.Future;
28 import io.netty.util.concurrent.Ticker;
29 import io.netty.util.internal.ObjectUtil;
30
31 import java.util.concurrent.TimeUnit;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 public class IdleStateHandler extends ChannelDuplexHandler {
100 private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
101
102 private final boolean observeOutput;
103 private final long readerIdleTimeNanos;
104 private final long writerIdleTimeNanos;
105 private final long allIdleTimeNanos;
106
107 private Ticker ticker = Ticker.systemTicker();
108
109 private Future<?> readerIdleTimeout;
110 private long lastReadTime;
111 private boolean firstReaderIdleEvent = true;
112
113 private Future<?> writerIdleTimeout;
114 private long lastWriteTime;
115 private boolean firstWriterIdleEvent = true;
116
117 private Future<?> allIdleTimeout;
118 private boolean firstAllIdleEvent = true;
119
120 private byte state;
121 private static final byte ST_INITIALIZED = 1;
122 private static final byte ST_DESTROYED = 2;
123
124 private boolean reading;
125
126 private long lastChangeCheckTimeStamp;
127 private int lastMessageHashCode;
128 private long lastPendingWriteBytes;
129 private long lastFlushProgress;
130
131 private final ChannelFutureListener writeListener = future -> {
132 lastWriteTime = ticker.nanoTime();
133 firstWriterIdleEvent = firstAllIdleEvent = true;
134 };
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 public IdleStateHandler(
153 int readerIdleTimeSeconds,
154 int writerIdleTimeSeconds,
155 int allIdleTimeSeconds) {
156
157 this(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds,
158 TimeUnit.SECONDS);
159 }
160
161
162
163
164 public IdleStateHandler(
165 long readerIdleTime, long writerIdleTime, long allIdleTime,
166 TimeUnit unit) {
167 this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
168 }
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 public IdleStateHandler(boolean observeOutput,
193 long readerIdleTime, long writerIdleTime, long allIdleTime,
194 TimeUnit unit) {
195 ObjectUtil.checkNotNull(unit, "unit");
196
197 this.observeOutput = observeOutput;
198
199 if (readerIdleTime <= 0) {
200 readerIdleTimeNanos = 0;
201 } else {
202 readerIdleTimeNanos = Math.max(unit.toNanos(readerIdleTime), MIN_TIMEOUT_NANOS);
203 }
204 if (writerIdleTime <= 0) {
205 writerIdleTimeNanos = 0;
206 } else {
207 writerIdleTimeNanos = Math.max(unit.toNanos(writerIdleTime), MIN_TIMEOUT_NANOS);
208 }
209 if (allIdleTime <= 0) {
210 allIdleTimeNanos = 0;
211 } else {
212 allIdleTimeNanos = Math.max(unit.toNanos(allIdleTime), MIN_TIMEOUT_NANOS);
213 }
214 }
215
216
217
218
219
220 public long getReaderIdleTimeInMillis() {
221 return TimeUnit.NANOSECONDS.toMillis(readerIdleTimeNanos);
222 }
223
224
225
226
227
228 public long getWriterIdleTimeInMillis() {
229 return TimeUnit.NANOSECONDS.toMillis(writerIdleTimeNanos);
230 }
231
232
233
234
235
236 public long getAllIdleTimeInMillis() {
237 return TimeUnit.NANOSECONDS.toMillis(allIdleTimeNanos);
238 }
239
240 @Override
241 public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
242 this.ticker = ctx.executor().ticker();
243 if (ctx.channel().isActive() && ctx.channel().isRegistered()) {
244
245
246 initialize(ctx);
247 } else {
248
249
250 }
251 }
252
253 @Override
254 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
255 destroy();
256 }
257
258 @Override
259 public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
260
261 if (ctx.channel().isActive()) {
262 initialize(ctx);
263 }
264 super.channelRegistered(ctx);
265 }
266
267 @Override
268 public void channelActive(ChannelHandlerContext ctx) throws Exception {
269
270
271
272 initialize(ctx);
273 super.channelActive(ctx);
274 }
275
276 @Override
277 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
278 destroy();
279 super.channelInactive(ctx);
280 }
281
282 @Override
283 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
284 if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
285 reading = true;
286 firstReaderIdleEvent = firstAllIdleEvent = true;
287 }
288 ctx.fireChannelRead(msg);
289 }
290
291 @Override
292 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
293 if ((readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) && reading) {
294 lastReadTime = ticker.nanoTime();
295 reading = false;
296 }
297 ctx.fireChannelReadComplete();
298 }
299
300 @Override
301 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
302
303 if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
304 ctx.write(msg, promise.unvoid()).addListener(writeListener);
305 } else {
306 ctx.write(msg, promise);
307 }
308 }
309
310
311
312
313 public void resetReadTimeout() {
314 if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
315 lastReadTime = ticker.nanoTime();
316 reading = false;
317 firstReaderIdleEvent = firstAllIdleEvent = true;
318 }
319 }
320
321
322
323
324 public void resetWriteTimeout() {
325 if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
326 lastWriteTime = ticker.nanoTime();
327 firstWriterIdleEvent = firstAllIdleEvent = true;
328 }
329 }
330
331 private void initialize(ChannelHandlerContext ctx) {
332
333
334 switch (state) {
335 case 1:
336 case 2:
337 return;
338 default:
339 break;
340 }
341
342 state = ST_INITIALIZED;
343 initOutputChanged(ctx);
344
345 lastReadTime = lastWriteTime = ticker.nanoTime();
346 if (readerIdleTimeNanos > 0) {
347 readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
348 readerIdleTimeNanos, TimeUnit.NANOSECONDS);
349 }
350 if (writerIdleTimeNanos > 0) {
351 writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),
352 writerIdleTimeNanos, TimeUnit.NANOSECONDS);
353 }
354 if (allIdleTimeNanos > 0) {
355 allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
356 allIdleTimeNanos, TimeUnit.NANOSECONDS);
357 }
358 }
359
360
361
362
363 Future<?> schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) {
364 return ctx.executor().schedule(task, delay, unit);
365 }
366
367 private void destroy() {
368 state = ST_DESTROYED;
369
370 if (readerIdleTimeout != null) {
371 readerIdleTimeout.cancel(false);
372 readerIdleTimeout = null;
373 }
374 if (writerIdleTimeout != null) {
375 writerIdleTimeout.cancel(false);
376 writerIdleTimeout = null;
377 }
378 if (allIdleTimeout != null) {
379 allIdleTimeout.cancel(false);
380 allIdleTimeout = null;
381 }
382 }
383
384
385
386
387
388 protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
389 ctx.fireUserEventTriggered(evt);
390 }
391
392
393
394
395 protected IdleStateEvent newIdleStateEvent(IdleState state, boolean first) {
396 switch (state) {
397 case ALL_IDLE:
398 return first ? IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT : IdleStateEvent.ALL_IDLE_STATE_EVENT;
399 case READER_IDLE:
400 return first ? IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT : IdleStateEvent.READER_IDLE_STATE_EVENT;
401 case WRITER_IDLE:
402 return first ? IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT : IdleStateEvent.WRITER_IDLE_STATE_EVENT;
403 default:
404 throw new IllegalArgumentException("Unhandled: state=" + state + ", first=" + first);
405 }
406 }
407
408
409
410
411 private void initOutputChanged(ChannelHandlerContext ctx) {
412 if (observeOutput) {
413 Channel channel = ctx.channel();
414 Unsafe unsafe = channel.unsafe();
415 ChannelOutboundBuffer buf = unsafe.outboundBuffer();
416
417 if (buf != null) {
418 lastMessageHashCode = System.identityHashCode(buf.current());
419 lastPendingWriteBytes = buf.totalPendingWriteBytes();
420 lastFlushProgress = buf.currentProgress();
421 }
422 }
423 }
424
425
426
427
428
429
430
431
432 private boolean hasOutputChanged(ChannelHandlerContext ctx, boolean first) {
433 if (observeOutput) {
434
435
436
437
438
439
440 if (lastChangeCheckTimeStamp != lastWriteTime) {
441 lastChangeCheckTimeStamp = lastWriteTime;
442
443
444 if (!first) {
445 return true;
446 }
447 }
448
449 Channel channel = ctx.channel();
450 Unsafe unsafe = channel.unsafe();
451 ChannelOutboundBuffer buf = unsafe.outboundBuffer();
452
453 if (buf != null) {
454 int messageHashCode = System.identityHashCode(buf.current());
455 long pendingWriteBytes = buf.totalPendingWriteBytes();
456
457 if (messageHashCode != lastMessageHashCode || pendingWriteBytes != lastPendingWriteBytes) {
458 lastMessageHashCode = messageHashCode;
459 lastPendingWriteBytes = pendingWriteBytes;
460
461 if (!first) {
462 return true;
463 }
464 }
465
466 long flushProgress = buf.currentProgress();
467 if (flushProgress != lastFlushProgress) {
468 lastFlushProgress = flushProgress;
469 return !first;
470 }
471 }
472 }
473
474 return false;
475 }
476
477 private abstract static class AbstractIdleTask implements Runnable {
478
479 private final ChannelHandlerContext ctx;
480
481 AbstractIdleTask(ChannelHandlerContext ctx) {
482 this.ctx = ctx;
483 }
484
485 @Override
486 public void run() {
487 if (!ctx.channel().isOpen()) {
488 return;
489 }
490
491 run(ctx);
492 }
493
494 protected abstract void run(ChannelHandlerContext ctx);
495 }
496
497 private final class ReaderIdleTimeoutTask extends AbstractIdleTask {
498
499 ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
500 super(ctx);
501 }
502
503 @Override
504 protected void run(ChannelHandlerContext ctx) {
505 long nextDelay = readerIdleTimeNanos;
506 if (!reading) {
507 nextDelay -= ticker.nanoTime() - lastReadTime;
508 }
509
510 if (nextDelay <= 0) {
511
512 readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);
513
514 boolean first = firstReaderIdleEvent;
515 firstReaderIdleEvent = false;
516
517 try {
518 IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
519 channelIdle(ctx, event);
520 } catch (Throwable t) {
521 ctx.fireExceptionCaught(t);
522 }
523 } else {
524
525 readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
526 }
527 }
528 }
529
530 private final class WriterIdleTimeoutTask extends AbstractIdleTask {
531
532 WriterIdleTimeoutTask(ChannelHandlerContext ctx) {
533 super(ctx);
534 }
535
536 @Override
537 protected void run(ChannelHandlerContext ctx) {
538
539 long lastWriteTime = IdleStateHandler.this.lastWriteTime;
540 long nextDelay = writerIdleTimeNanos - (ticker.nanoTime() - lastWriteTime);
541 if (nextDelay <= 0) {
542
543 writerIdleTimeout = schedule(ctx, this, writerIdleTimeNanos, TimeUnit.NANOSECONDS);
544
545 boolean first = firstWriterIdleEvent;
546 firstWriterIdleEvent = false;
547
548 try {
549 if (hasOutputChanged(ctx, first)) {
550 return;
551 }
552
553 IdleStateEvent event = newIdleStateEvent(IdleState.WRITER_IDLE, first);
554 channelIdle(ctx, event);
555 } catch (Throwable t) {
556 ctx.fireExceptionCaught(t);
557 }
558 } else {
559
560 writerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
561 }
562 }
563 }
564
565 private final class AllIdleTimeoutTask extends AbstractIdleTask {
566
567 AllIdleTimeoutTask(ChannelHandlerContext ctx) {
568 super(ctx);
569 }
570
571 @Override
572 protected void run(ChannelHandlerContext ctx) {
573
574 long nextDelay = allIdleTimeNanos;
575 if (!reading) {
576 nextDelay -= ticker.nanoTime() - Math.max(lastReadTime, lastWriteTime);
577 }
578 if (nextDelay <= 0) {
579
580
581 allIdleTimeout = schedule(ctx, this, allIdleTimeNanos, TimeUnit.NANOSECONDS);
582
583 boolean first = firstAllIdleEvent;
584 firstAllIdleEvent = false;
585
586 try {
587 if (hasOutputChanged(ctx, first)) {
588 return;
589 }
590
591 IdleStateEvent event = newIdleStateEvent(IdleState.ALL_IDLE, first);
592 channelIdle(ctx, event);
593 } catch (Throwable t) {
594 ctx.fireExceptionCaught(t);
595 }
596 } else {
597
598
599 allIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
600 }
601 }
602 }
603 }