Fix #239: IdleStateHandler starts two timers
1) Do not rely on ChannelPipeline.isAttached() to ensure initialize() is called once. 2) Fix a race condition where initialize() can schedule timeouts after destroy() is called.
This commit is contained in:
parent
32ff810b64
commit
f8253e031d
@ -224,15 +224,7 @@ public class IdleStateHandler extends SimpleChannelUpstreamHandler
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
||||||
if (ctx.getPipeline().isAttached()) {
|
initialize(ctx);
|
||||||
// channelOpen event has been fired already, which means
|
|
||||||
// this.channelOpen() will not be invoked.
|
|
||||||
// We have to initialize here instead.
|
|
||||||
initialize(ctx);
|
|
||||||
} else {
|
|
||||||
// channelOpen event has not been fired yet.
|
|
||||||
// this.channelOpen() will be invoked and initialization will occur there.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -250,16 +242,6 @@ public class IdleStateHandler extends SimpleChannelUpstreamHandler
|
|||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
|
|
||||||
throws Exception {
|
|
||||||
// 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().
|
|
||||||
initialize(ctx);
|
|
||||||
ctx.sendUpstream(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
|
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@ -286,8 +268,13 @@ public class IdleStateHandler extends SimpleChannelUpstreamHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initialize(ChannelHandlerContext ctx) {
|
private void initialize(ChannelHandlerContext ctx) {
|
||||||
State state = new State();
|
State state = state(ctx);
|
||||||
ctx.setAttachment(state);
|
|
||||||
|
// Avoid the case where destroy() is called before scheduling timeouts.
|
||||||
|
// See: https://github.com/netty/netty/issues/143
|
||||||
|
if (state.destroyed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
state.lastReadTime = state.lastWriteTime = System.currentTimeMillis();
|
state.lastReadTime = state.lastWriteTime = System.currentTimeMillis();
|
||||||
if (readerIdleTimeMillis > 0) {
|
if (readerIdleTimeMillis > 0) {
|
||||||
@ -308,25 +295,39 @@ public class IdleStateHandler extends SimpleChannelUpstreamHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void destroy(ChannelHandlerContext ctx) {
|
private void destroy(ChannelHandlerContext ctx) {
|
||||||
State state = (State) ctx.getAttachment();
|
State state;
|
||||||
// Check if the state was set before, it may not if the destroy method was called before the
|
|
||||||
// channelOpen(...) method.
|
synchronized (ctx) {
|
||||||
//
|
state = state(ctx);
|
||||||
// See #143
|
state.destroyed = true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private State state(ChannelHandlerContext ctx) {
|
||||||
|
State state;
|
||||||
|
synchronized (ctx) {
|
||||||
|
// FIXME: It could have been better if there is setAttachmentIfAbsent().
|
||||||
|
state = (State) ctx.getAttachment();
|
||||||
|
if (state != null) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
state = new State();
|
||||||
|
ctx.setAttachment(state);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void channelIdle(
|
protected void channelIdle(
|
||||||
@ -453,5 +454,7 @@ public class IdleStateHandler extends SimpleChannelUpstreamHandler
|
|||||||
volatile long lastWriteTime;
|
volatile long lastWriteTime;
|
||||||
|
|
||||||
volatile Timeout allIdleTimeout;
|
volatile Timeout allIdleTimeout;
|
||||||
|
|
||||||
|
volatile boolean destroyed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user