2009-02-11 06:59:35 +01:00
|
|
|
/*
|
2011-12-09 06:18:34 +01:00
|
|
|
* Copyright 2011 The Netty Project
|
2009-02-11 06:59:35 +01:00
|
|
|
*
|
2011-12-09 06:18:34 +01:00
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
2009-02-11 06:59:35 +01:00
|
|
|
*
|
2011-12-09 06:18:34 +01:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2009-02-11 06:59:35 +01:00
|
|
|
*
|
2009-08-28 09:15:49 +02:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
2011-12-09 06:18:34 +01:00
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
2009-08-28 09:15:49 +02:00
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
2009-02-11 06:59:35 +01:00
|
|
|
*/
|
2011-12-09 04:38:59 +01:00
|
|
|
package io.netty.handler.timeout;
|
2009-02-11 06:59:35 +01:00
|
|
|
|
2011-12-09 04:38:59 +01:00
|
|
|
import static io.netty.channel.Channels.*;
|
2009-02-12 16:29:35 +01:00
|
|
|
|
2009-02-11 06:59:35 +01:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
2011-12-09 04:38:59 +01:00
|
|
|
import io.netty.bootstrap.ServerBootstrap;
|
|
|
|
import io.netty.channel.Channel;
|
|
|
|
import io.netty.channel.ChannelHandler;
|
|
|
|
import io.netty.channel.ChannelHandler.Sharable;
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
|
|
import io.netty.channel.ChannelPipeline;
|
|
|
|
import io.netty.channel.ChannelPipelineFactory;
|
|
|
|
import io.netty.channel.ChannelStateEvent;
|
|
|
|
import io.netty.channel.Channels;
|
|
|
|
import io.netty.channel.LifeCycleAwareChannelHandler;
|
|
|
|
import io.netty.channel.MessageEvent;
|
|
|
|
import io.netty.channel.SimpleChannelUpstreamHandler;
|
|
|
|
import io.netty.channel.WriteCompletionEvent;
|
|
|
|
import io.netty.util.ExternalResourceReleasable;
|
|
|
|
import io.netty.util.HashedWheelTimer;
|
|
|
|
import io.netty.util.Timeout;
|
|
|
|
import io.netty.util.Timer;
|
|
|
|
import io.netty.util.TimerTask;
|
2009-02-11 06:59:35 +01:00
|
|
|
|
|
|
|
/**
|
2009-06-19 13:17:38 +02:00
|
|
|
* Triggers an {@link IdleStateEvent} when a {@link Channel} has not performed
|
|
|
|
* read, write, or both operation for a while.
|
|
|
|
*
|
|
|
|
* <h3>Supported idle states</h3>
|
2009-06-19 18:45:30 +02:00
|
|
|
* <table border="1">
|
2009-06-19 13:17:38 +02:00
|
|
|
* <tr>
|
|
|
|
* <th>Property</th><th>Meaning</th>
|
|
|
|
* </tr>
|
|
|
|
* <tr>
|
|
|
|
* <td>{@code readerIdleTime}</td>
|
|
|
|
* <td>an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE}
|
|
|
|
* will be triggered when no read was performed for the specified period of
|
|
|
|
* time. Specify {@code 0} to disable.</td>
|
|
|
|
* </tr>
|
|
|
|
* <tr>
|
|
|
|
* <td>{@code writerIdleTime}</td>
|
|
|
|
* <td>an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE}
|
|
|
|
* will be triggered when no write was performed for the specified period of
|
|
|
|
* time. Specify {@code 0} to disable.</td>
|
|
|
|
* </tr>
|
|
|
|
* <tr>
|
|
|
|
* <td>{@code allIdleTime}</td>
|
|
|
|
* <td>an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE}
|
|
|
|
* will be triggered when neither read nor write was performed for the
|
|
|
|
* specified period of time. Specify {@code 0} to disable.</td>
|
|
|
|
* </tr>
|
|
|
|
* </table>
|
|
|
|
*
|
2009-09-04 05:24:03 +02:00
|
|
|
* <pre>
|
2010-03-30 10:02:32 +02:00
|
|
|
* // An example that sends a ping message when there is no outbound traffic
|
|
|
|
* // for 30 seconds. The connection is closed when there is no inbound traffic
|
|
|
|
* // for 60 seconds.
|
2010-02-02 01:38:07 +01:00
|
|
|
*
|
2010-02-02 03:00:04 +01:00
|
|
|
* public class MyPipelineFactory implements {@link ChannelPipelineFactory} {
|
2010-02-02 01:38:07 +01:00
|
|
|
*
|
2010-02-02 03:00:04 +01:00
|
|
|
* private final {@link Timer} timer;
|
2011-05-04 10:30:09 +02:00
|
|
|
* private final {@link ChannelHandler} idleStateHandler;
|
2010-02-02 03:00:04 +01:00
|
|
|
*
|
|
|
|
* public MyPipelineFactory({@link Timer} timer) {
|
2010-02-02 01:38:07 +01:00
|
|
|
* this.timer = timer;
|
2011-05-04 10:30:09 +02:00
|
|
|
* this.idleStateHandler = <b>new {@link IdleStateHandler}(timer, 60, 30, 0), // timer must be shared.</b>
|
2010-02-02 01:38:07 +01:00
|
|
|
* }
|
|
|
|
*
|
2010-02-02 03:00:04 +01:00
|
|
|
* public {@link ChannelPipeline} getPipeline() {
|
|
|
|
* return {@link Channels}.pipeline(
|
2011-05-04 10:30:09 +02:00
|
|
|
* idleStateHandler,
|
2010-02-02 01:38:07 +01:00
|
|
|
* new MyHandler());
|
|
|
|
* }
|
|
|
|
* }
|
2009-09-04 05:24:03 +02:00
|
|
|
*
|
2010-03-30 10:02:32 +02:00
|
|
|
* // Handler should handle the {@link IdleStateEvent} triggered by {@link IdleStateHandler}.
|
2010-02-02 03:00:04 +01:00
|
|
|
* public class MyHandler extends {@link IdleStateAwareChannelHandler} {
|
|
|
|
*
|
|
|
|
* {@code @Override}
|
|
|
|
* public void channelIdle({@link ChannelHandlerContext} ctx, {@link IdleStateEvent} e) {
|
2010-03-30 10:02:32 +02:00
|
|
|
* if (e.getState() == {@link IdleState}.READER_IDLE) {
|
|
|
|
* e.getChannel().close();
|
|
|
|
* } else if (e.getState() == {@link IdleState}.WRITER_IDLE) {
|
|
|
|
* e.getChannel().write(new PingMessage());
|
|
|
|
* }
|
2009-09-04 05:24:03 +02:00
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
2010-02-02 03:00:04 +01:00
|
|
|
* {@link ServerBootstrap} bootstrap = ...;
|
|
|
|
* {@link Timer} timer = new {@link HashedWheelTimer}();
|
2010-02-02 01:38:07 +01:00
|
|
|
* ...
|
|
|
|
* bootstrap.setPipelineFactory(new MyPipelineFactory(timer));
|
|
|
|
* ...
|
2009-09-04 05:24:03 +02:00
|
|
|
* </pre>
|
|
|
|
*
|
2011-02-01 04:28:40 +01:00
|
|
|
* The {@link Timer} which was specified when the {@link IdleStateHandler} is
|
2010-02-08 07:11:25 +01:00
|
|
|
* created should be stopped manually by calling {@link #releaseExternalResources()}
|
|
|
|
* or {@link Timer#stop()} when your application shuts down.
|
2009-06-19 13:33:38 +02:00
|
|
|
* @see ReadTimeoutHandler
|
|
|
|
* @see WriteTimeoutHandler
|
2009-07-20 06:50:03 +02:00
|
|
|
*
|
|
|
|
* @apiviz.landmark
|
2011-12-09 04:38:59 +01:00
|
|
|
* @apiviz.uses io.netty.util.HashedWheelTimer
|
|
|
|
* @apiviz.has io.netty.handler.timeout.IdleStateEvent oneway - - triggers
|
2009-02-11 06:59:35 +01:00
|
|
|
*/
|
2011-05-04 10:30:09 +02:00
|
|
|
@Sharable
|
2009-02-12 16:21:25 +01:00
|
|
|
public class IdleStateHandler extends SimpleChannelUpstreamHandler
|
|
|
|
implements LifeCycleAwareChannelHandler,
|
|
|
|
ExternalResourceReleasable {
|
2009-02-11 06:59:35 +01:00
|
|
|
|
|
|
|
final Timer timer;
|
|
|
|
|
|
|
|
final long readerIdleTimeMillis;
|
|
|
|
final long writerIdleTimeMillis;
|
2009-02-12 16:31:39 +01:00
|
|
|
final long allIdleTimeMillis;
|
2009-02-12 16:21:25 +01:00
|
|
|
|
2009-06-19 13:17:38 +02:00
|
|
|
/**
|
|
|
|
* Creates a new instance.
|
|
|
|
*
|
|
|
|
* @param timer
|
2009-06-19 13:40:28 +02:00
|
|
|
* the {@link Timer} that is used to trigger the scheduled event.
|
|
|
|
* The recommended {@link Timer} implementation is {@link HashedWheelTimer}.
|
2009-06-19 13:17:38 +02:00
|
|
|
* @param readerIdleTimeSeconds
|
|
|
|
* an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE}
|
|
|
|
* will be triggered when no read was performed for the specified
|
|
|
|
* period of time. Specify {@code 0} to disable.
|
|
|
|
* @param writerIdleTimeSeconds
|
|
|
|
* an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE}
|
|
|
|
* will be triggered when no write was performed for the specified
|
|
|
|
* period of time. Specify {@code 0} to disable.
|
|
|
|
* @param allIdleTimeSeconds
|
|
|
|
* an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE}
|
|
|
|
* will be triggered when neither read nor write was performed for
|
|
|
|
* the specified period of time. Specify {@code 0} to disable.
|
|
|
|
*/
|
2009-02-12 16:21:25 +01:00
|
|
|
public IdleStateHandler(
|
|
|
|
Timer timer,
|
2009-02-13 05:24:29 +01:00
|
|
|
int readerIdleTimeSeconds,
|
|
|
|
int writerIdleTimeSeconds,
|
|
|
|
int allIdleTimeSeconds) {
|
2009-02-12 16:21:25 +01:00
|
|
|
|
|
|
|
this(timer,
|
2009-02-12 16:33:28 +01:00
|
|
|
readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds,
|
|
|
|
TimeUnit.SECONDS);
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|
|
|
|
|
2009-06-19 13:17:38 +02:00
|
|
|
/**
|
|
|
|
* Creates a new instance.
|
|
|
|
*
|
|
|
|
* @param timer
|
2009-06-19 13:40:28 +02:00
|
|
|
* the {@link Timer} that is used to trigger the scheduled event.
|
|
|
|
* The recommended {@link Timer} implementation is {@link HashedWheelTimer}.
|
2009-06-19 13:17:38 +02:00
|
|
|
* @param readerIdleTime
|
|
|
|
* an {@link IdleStateEvent} whose state is {@link IdleState#READER_IDLE}
|
|
|
|
* will be triggered when no read was performed for the specified
|
|
|
|
* period of time. Specify {@code 0} to disable.
|
|
|
|
* @param writerIdleTime
|
|
|
|
* an {@link IdleStateEvent} whose state is {@link IdleState#WRITER_IDLE}
|
|
|
|
* will be triggered when no write was performed for the specified
|
|
|
|
* period of time. Specify {@code 0} to disable.
|
|
|
|
* @param allIdleTime
|
|
|
|
* an {@link IdleStateEvent} whose state is {@link IdleState#ALL_IDLE}
|
|
|
|
* will be triggered when neither read nor write was performed for
|
|
|
|
* the specified period of time. Specify {@code 0} to disable.
|
|
|
|
* @param unit
|
|
|
|
* the {@link TimeUnit} of {@code readerIdleTime},
|
|
|
|
* {@code writeIdleTime}, and {@code allIdleTime}
|
|
|
|
*/
|
2009-02-12 16:21:25 +01:00
|
|
|
public IdleStateHandler(
|
|
|
|
Timer timer,
|
2009-02-12 16:31:39 +01:00
|
|
|
long readerIdleTime, long writerIdleTime, long allIdleTime,
|
2009-02-12 16:21:25 +01:00
|
|
|
TimeUnit unit) {
|
|
|
|
|
2009-02-11 06:59:35 +01:00
|
|
|
if (timer == null) {
|
|
|
|
throw new NullPointerException("timer");
|
|
|
|
}
|
|
|
|
if (unit == null) {
|
|
|
|
throw new NullPointerException("unit");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.timer = timer;
|
2009-09-04 08:57:45 +02:00
|
|
|
if (readerIdleTime <= 0) {
|
|
|
|
readerIdleTimeMillis = 0;
|
|
|
|
} else {
|
|
|
|
readerIdleTimeMillis = Math.max(unit.toMillis(readerIdleTime), 1);
|
|
|
|
}
|
|
|
|
if (writerIdleTime <= 0) {
|
|
|
|
writerIdleTimeMillis = 0;
|
|
|
|
} else {
|
|
|
|
writerIdleTimeMillis = Math.max(unit.toMillis(writerIdleTime), 1);
|
|
|
|
}
|
|
|
|
if (allIdleTime <= 0) {
|
|
|
|
allIdleTimeMillis = 0;
|
|
|
|
} else {
|
|
|
|
allIdleTimeMillis = Math.max(unit.toMillis(allIdleTime), 1);
|
|
|
|
}
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|
|
|
|
|
2009-06-19 13:17:38 +02:00
|
|
|
/**
|
|
|
|
* Stops the {@link Timer} which was specified in the constructor of this
|
|
|
|
* handler. You should not call this method if the {@link Timer} is in use
|
|
|
|
* by other objects.
|
|
|
|
*/
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-11 06:59:35 +01:00
|
|
|
public void releaseExternalResources() {
|
|
|
|
timer.stop();
|
|
|
|
}
|
|
|
|
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-11 06:59:35 +01:00
|
|
|
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
2009-12-17 11:14:22 +01:00
|
|
|
if (ctx.getPipeline().isAttached()) {
|
2009-12-17 11:19:09 +01:00
|
|
|
// channelOpen event has been fired already, which means
|
|
|
|
// this.channelOpen() will not be invoked.
|
|
|
|
// We have to initialize here instead.
|
2009-12-17 11:14:22 +01:00
|
|
|
initialize(ctx);
|
2009-12-17 11:21:34 +01:00
|
|
|
} else {
|
|
|
|
// channelOpen event has not been fired yet.
|
|
|
|
// this.channelOpen() will be invoked and initialization will occur there.
|
2009-12-17 11:14:22 +01:00
|
|
|
}
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|
|
|
|
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-11 06:59:35 +01:00
|
|
|
public void afterAdd(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
// NOOP
|
|
|
|
}
|
|
|
|
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-11 06:59:35 +01:00
|
|
|
public void beforeRemove(ChannelHandlerContext ctx) throws Exception {
|
2011-05-04 10:30:09 +02:00
|
|
|
destroy(ctx);
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|
|
|
|
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-11 06:59:35 +01:00
|
|
|
public void afterRemove(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
// NOOP
|
|
|
|
}
|
|
|
|
|
2009-12-17 10:16:52 +01:00
|
|
|
@Override
|
|
|
|
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
|
|
|
|
throws Exception {
|
2009-12-17 11:19:09 +01:00
|
|
|
// This method will be invoked only if this handler was added
|
|
|
|
// before channelOpen event is fired. If a user adds this handler
|
|
|
|
// after the channelOpen event, initialize() will be called by beforeAdd().
|
2009-12-17 11:14:22 +01:00
|
|
|
initialize(ctx);
|
2009-12-17 11:19:09 +01:00
|
|
|
ctx.sendUpstream(e);
|
2009-12-17 10:16:52 +01:00
|
|
|
}
|
|
|
|
|
2009-02-11 06:59:35 +01:00
|
|
|
@Override
|
|
|
|
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
|
|
|
|
throws Exception {
|
2011-05-04 10:30:09 +02:00
|
|
|
destroy(ctx);
|
2009-02-11 06:59:35 +01:00
|
|
|
ctx.sendUpstream(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
|
|
|
throws Exception {
|
2011-05-04 10:30:09 +02:00
|
|
|
State state = (State) ctx.getAttachment();
|
|
|
|
state.lastReadTime = System.currentTimeMillis();
|
2009-02-11 06:59:35 +01:00
|
|
|
ctx.sendUpstream(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e)
|
|
|
|
throws Exception {
|
|
|
|
if (e.getWrittenAmount() > 0) {
|
2011-05-04 10:30:09 +02:00
|
|
|
State state = (State) ctx.getAttachment();
|
|
|
|
state.lastWriteTime = System.currentTimeMillis();
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|
|
|
|
ctx.sendUpstream(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initialize(ChannelHandlerContext ctx) {
|
2011-05-04 10:30:09 +02:00
|
|
|
State state = new State();
|
|
|
|
ctx.setAttachment(state);
|
|
|
|
|
|
|
|
state.lastReadTime = state.lastWriteTime = System.currentTimeMillis();
|
2009-02-11 10:38:24 +01:00
|
|
|
if (readerIdleTimeMillis > 0) {
|
2011-05-04 10:30:09 +02:00
|
|
|
state.readerIdleTimeout = timer.newTimeout(
|
2009-07-11 21:04:27 +02:00
|
|
|
new ReaderIdleTimeoutTask(ctx),
|
|
|
|
readerIdleTimeMillis, TimeUnit.MILLISECONDS);
|
2009-02-11 10:38:24 +01:00
|
|
|
}
|
|
|
|
if (writerIdleTimeMillis > 0) {
|
2011-05-04 10:30:09 +02:00
|
|
|
state.writerIdleTimeout = timer.newTimeout(
|
2009-07-11 21:04:27 +02:00
|
|
|
new WriterIdleTimeoutTask(ctx),
|
|
|
|
writerIdleTimeMillis, TimeUnit.MILLISECONDS);
|
2009-02-11 10:38:24 +01:00
|
|
|
}
|
2009-02-12 16:31:39 +01:00
|
|
|
if (allIdleTimeMillis > 0) {
|
2011-05-04 10:30:09 +02:00
|
|
|
state.allIdleTimeout = timer.newTimeout(
|
2009-07-11 21:04:27 +02:00
|
|
|
new AllIdleTimeoutTask(ctx),
|
|
|
|
allIdleTimeMillis, TimeUnit.MILLISECONDS);
|
2009-02-12 16:21:25 +01:00
|
|
|
}
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|
|
|
|
|
2011-05-04 10:30:09 +02:00
|
|
|
private void destroy(ChannelHandlerContext ctx) {
|
|
|
|
State state = (State) ctx.getAttachment();
|
2012-01-07 19:42:00 +01:00
|
|
|
// Check if the state was set before, it may not if the destroy method was called before the
|
|
|
|
// channelOpen(...) method.
|
|
|
|
//
|
|
|
|
// See #143
|
|
|
|
if (state != null) {
|
|
|
|
if (state.readerIdleTimeout != null) {
|
|
|
|
state.readerIdleTimeout.cancel();
|
|
|
|
state.readerIdleTimeout = null;
|
|
|
|
}
|
|
|
|
if (state.writerIdleTimeout != null) {
|
|
|
|
state.writerIdleTimeout.cancel();
|
|
|
|
state.writerIdleTimeout = null;
|
|
|
|
}
|
|
|
|
if (state.allIdleTimeout != null) {
|
|
|
|
state.allIdleTimeout.cancel();
|
|
|
|
state.allIdleTimeout = null;
|
|
|
|
}
|
2009-02-12 16:21:25 +01:00
|
|
|
}
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|
|
|
|
|
2009-02-12 16:29:35 +01:00
|
|
|
protected void channelIdle(
|
|
|
|
ChannelHandlerContext ctx, IdleState state, long lastActivityTimeMillis) throws Exception {
|
2009-02-12 16:21:25 +01:00
|
|
|
ctx.sendUpstream(new DefaultIdleStateEvent(ctx.getChannel(), state, lastActivityTimeMillis));
|
2009-02-12 12:03:10 +01:00
|
|
|
}
|
|
|
|
|
2009-02-11 06:59:35 +01:00
|
|
|
private final class ReaderIdleTimeoutTask implements TimerTask {
|
|
|
|
|
|
|
|
private final ChannelHandlerContext ctx;
|
|
|
|
|
|
|
|
ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
|
|
|
|
this.ctx = ctx;
|
|
|
|
}
|
|
|
|
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-11 06:59:35 +01:00
|
|
|
public void run(Timeout timeout) throws Exception {
|
2009-02-12 16:21:25 +01:00
|
|
|
if (timeout.isCancelled() || !ctx.getChannel().isOpen()) {
|
2009-02-11 06:59:35 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-04 10:30:09 +02:00
|
|
|
State state = (State) ctx.getAttachment();
|
2009-02-11 06:59:35 +01:00
|
|
|
long currentTime = System.currentTimeMillis();
|
2011-05-04 10:30:09 +02:00
|
|
|
long lastReadTime = state.lastReadTime;
|
2009-02-11 06:59:35 +01:00
|
|
|
long nextDelay = readerIdleTimeMillis - (currentTime - lastReadTime);
|
|
|
|
if (nextDelay <= 0) {
|
|
|
|
// Reader is idle - set a new timeout and notify the callback.
|
2011-05-04 10:30:09 +02:00
|
|
|
state.readerIdleTimeout =
|
2009-02-11 06:59:35 +01:00
|
|
|
timer.newTimeout(this, readerIdleTimeMillis, TimeUnit.MILLISECONDS);
|
2009-02-12 16:29:35 +01:00
|
|
|
try {
|
|
|
|
channelIdle(ctx, IdleState.READER_IDLE, lastReadTime);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
fireExceptionCaught(ctx, t);
|
|
|
|
}
|
2009-02-11 06:59:35 +01:00
|
|
|
} else {
|
|
|
|
// Read occurred before the timeout - set a new timeout with shorter delay.
|
2011-05-04 10:30:09 +02:00
|
|
|
state.readerIdleTimeout =
|
2009-02-11 06:59:35 +01:00
|
|
|
timer.newTimeout(this, nextDelay, TimeUnit.MILLISECONDS);
|
|
|
|
}
|
|
|
|
}
|
2009-02-12 11:50:43 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-02-11 06:59:35 +01:00
|
|
|
private final class WriterIdleTimeoutTask implements TimerTask {
|
|
|
|
|
|
|
|
private final ChannelHandlerContext ctx;
|
|
|
|
|
|
|
|
WriterIdleTimeoutTask(ChannelHandlerContext ctx) {
|
|
|
|
this.ctx = ctx;
|
|
|
|
}
|
|
|
|
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-11 06:59:35 +01:00
|
|
|
public void run(Timeout timeout) throws Exception {
|
2009-02-12 16:21:25 +01:00
|
|
|
if (timeout.isCancelled() || !ctx.getChannel().isOpen()) {
|
2009-02-11 06:59:35 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-04 10:30:09 +02:00
|
|
|
State state = (State) ctx.getAttachment();
|
2009-02-11 06:59:35 +01:00
|
|
|
long currentTime = System.currentTimeMillis();
|
2011-05-04 10:30:09 +02:00
|
|
|
long lastWriteTime = state.lastWriteTime;
|
2009-02-11 06:59:35 +01:00
|
|
|
long nextDelay = writerIdleTimeMillis - (currentTime - lastWriteTime);
|
|
|
|
if (nextDelay <= 0) {
|
|
|
|
// Writer is idle - set a new timeout and notify the callback.
|
2011-05-04 10:30:09 +02:00
|
|
|
state.writerIdleTimeout =
|
2009-02-11 06:59:35 +01:00
|
|
|
timer.newTimeout(this, writerIdleTimeMillis, TimeUnit.MILLISECONDS);
|
2009-02-12 16:29:35 +01:00
|
|
|
try {
|
2009-02-13 12:33:22 +01:00
|
|
|
channelIdle(ctx, IdleState.WRITER_IDLE, lastWriteTime);
|
2009-02-12 16:29:35 +01:00
|
|
|
} catch (Throwable t) {
|
|
|
|
fireExceptionCaught(ctx, t);
|
|
|
|
}
|
2009-02-11 06:59:35 +01:00
|
|
|
} else {
|
|
|
|
// Write occurred before the timeout - set a new timeout with shorter delay.
|
2011-05-04 10:30:09 +02:00
|
|
|
state.writerIdleTimeout =
|
2009-02-11 06:59:35 +01:00
|
|
|
timer.newTimeout(this, nextDelay, TimeUnit.MILLISECONDS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-02-12 16:21:25 +01:00
|
|
|
|
2009-02-12 16:31:39 +01:00
|
|
|
private final class AllIdleTimeoutTask implements TimerTask {
|
2009-02-12 16:21:25 +01:00
|
|
|
|
|
|
|
private final ChannelHandlerContext ctx;
|
|
|
|
|
2009-02-12 16:31:39 +01:00
|
|
|
AllIdleTimeoutTask(ChannelHandlerContext ctx) {
|
2009-02-12 16:21:25 +01:00
|
|
|
this.ctx = ctx;
|
|
|
|
}
|
|
|
|
|
2010-11-12 01:45:39 +01:00
|
|
|
@Override
|
2009-02-12 16:21:25 +01:00
|
|
|
public void run(Timeout timeout) throws Exception {
|
|
|
|
if (timeout.isCancelled() || !ctx.getChannel().isOpen()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-04 10:30:09 +02:00
|
|
|
State state = (State) ctx.getAttachment();
|
2009-02-12 16:21:25 +01:00
|
|
|
long currentTime = System.currentTimeMillis();
|
2011-05-04 10:30:09 +02:00
|
|
|
long lastIoTime = Math.max(state.lastReadTime, state.lastWriteTime);
|
2009-02-12 16:31:39 +01:00
|
|
|
long nextDelay = allIdleTimeMillis - (currentTime - lastIoTime);
|
2009-02-12 16:21:25 +01:00
|
|
|
if (nextDelay <= 0) {
|
|
|
|
// Both reader and writer are idle - set a new timeout and
|
|
|
|
// notify the callback.
|
2011-05-04 10:30:09 +02:00
|
|
|
state.allIdleTimeout =
|
2009-02-12 16:31:39 +01:00
|
|
|
timer.newTimeout(this, allIdleTimeMillis, TimeUnit.MILLISECONDS);
|
2009-02-12 16:29:35 +01:00
|
|
|
try {
|
2009-02-13 12:33:22 +01:00
|
|
|
channelIdle(ctx, IdleState.ALL_IDLE, lastIoTime);
|
2009-02-12 16:29:35 +01:00
|
|
|
} catch (Throwable t) {
|
|
|
|
fireExceptionCaught(ctx, t);
|
|
|
|
}
|
2009-02-12 16:21:25 +01:00
|
|
|
} else {
|
|
|
|
// Either read or write occurred before the timeout - set a new
|
|
|
|
// timeout with shorter delay.
|
2011-05-04 10:30:09 +02:00
|
|
|
state.allIdleTimeout =
|
2009-02-12 16:21:25 +01:00
|
|
|
timer.newTimeout(this, nextDelay, TimeUnit.MILLISECONDS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-05-04 10:30:09 +02:00
|
|
|
|
|
|
|
private static final class State {
|
|
|
|
State() {
|
|
|
|
}
|
|
|
|
|
|
|
|
volatile Timeout readerIdleTimeout;
|
|
|
|
volatile long lastReadTime;
|
|
|
|
|
|
|
|
volatile Timeout writerIdleTimeout;
|
|
|
|
volatile long lastWriteTime;
|
|
|
|
|
|
|
|
volatile Timeout allIdleTimeout;
|
|
|
|
}
|
2009-02-11 06:59:35 +01:00
|
|
|
}
|