Reduce calls to System.nanoTime() and object creation in IdleStateHandler. Related to [#3808]

Motivation:

Calling System.nanoTime() for each channelRead(...) is very expensive. See [#3808] for more detailed description.
Also we always do extra work for each write and read even if read or write idle states should not be handled.

Modifications:

- Move System.nanoTime() call to channelReadComplete(...).
- Reuse ChannelFutureListener for writes
- Only add ChannelFutureListener to writes if write and all idle states should be handled.
- Only call System.nanoTime() for reads if idle state events for read and all states should be handled.

Result:

Less overhead when using the IdleStateHandler.
This commit is contained in:
Norman Maurer 2015-05-21 22:21:22 +02:00
parent 6fce3b79c3
commit bac2e3a6d2

View File

@ -97,6 +97,15 @@ import java.util.concurrent.TimeUnit;
public class IdleStateHandler extends ChannelDuplexHandler {
private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
// Not create a new ChannelFutureListener per write operation to reduce GC pressure.
private final ChannelFutureListener writeListener = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
lastWriteTime = System.nanoTime();
firstWriterIdleEvent = firstAllIdleEvent = true;
}
};
private final long readerIdleTimeNanos;
private final long writerIdleTimeNanos;
private final long allIdleTimeNanos;
@ -113,6 +122,7 @@ public class IdleStateHandler extends ChannelDuplexHandler {
private boolean firstAllIdleEvent = true;
private volatile int state; // 0 - none, 1 - initialized, 2 - destroyed
private volatile boolean reading;
/**
* Creates a new instance firing {@link IdleStateEvent}s.
@ -249,23 +259,28 @@ public class IdleStateHandler extends ChannelDuplexHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
lastReadTime = System.nanoTime();
firstReaderIdleEvent = firstAllIdleEvent = true;
if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
reading = true;
firstReaderIdleEvent = firstAllIdleEvent = true;
}
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
lastReadTime = System.nanoTime();
reading = false;
}
ctx.fireChannelReadComplete();
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// Allow writing with void promise if handler is only configured for read timeout events.
if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
ChannelPromise unvoid = promise.unvoid();
unvoid.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
lastWriteTime = System.nanoTime();
firstWriterIdleEvent = firstAllIdleEvent = true;
}
});
unvoid.addListener(writeListener);
ctx.write(msg, unvoid);
} else {
ctx.write(msg, promise);
@ -342,9 +357,11 @@ public class IdleStateHandler extends ChannelDuplexHandler {
return;
}
long currentTime = System.nanoTime();
long lastReadTime = IdleStateHandler.this.lastReadTime;
long nextDelay = readerIdleTimeNanos - (currentTime - lastReadTime);
long nextDelay = readerIdleTimeNanos;
if (!reading) {
nextDelay -= System.nanoTime() - lastReadTime;
}
if (nextDelay <= 0) {
// Reader is idle - set a new timeout and notify the callback.
readerIdleTimeout =
@ -382,9 +399,8 @@ public class IdleStateHandler extends ChannelDuplexHandler {
return;
}
long currentTime = System.nanoTime();
long lastWriteTime = IdleStateHandler.this.lastWriteTime;
long nextDelay = writerIdleTimeNanos - (currentTime - lastWriteTime);
long nextDelay = writerIdleTimeNanos - (System.nanoTime() - lastWriteTime);
if (nextDelay <= 0) {
// Writer is idle - set a new timeout and notify the callback.
writerIdleTimeout = ctx.executor().schedule(
@ -422,9 +438,10 @@ public class IdleStateHandler extends ChannelDuplexHandler {
return;
}
long currentTime = System.nanoTime();
long lastIoTime = Math.max(lastReadTime, lastWriteTime);
long nextDelay = allIdleTimeNanos - (currentTime - lastIoTime);
long nextDelay = allIdleTimeNanos;
if (!reading) {
nextDelay -= System.nanoTime() - Math.max(lastReadTime, lastWriteTime);
}
if (nextDelay <= 0) {
// Both reader and writer are idle - set a new timeout and
// notify the callback.