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