[#1442] Make sure closing the channel will not cause an UnsupportedOperationException

This commit is contained in:
Norman Maurer 2013-06-13 18:10:56 +02:00
parent e5ca6518ba
commit a1a86b9de4
2 changed files with 13 additions and 11 deletions

View File

@ -125,7 +125,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
DefaultChannelHandlerContext prev = this.prev; DefaultChannelHandlerContext prev = this.prev;
if (prev != null) { if (prev != null) {
synchronized (pipeline) { synchronized (pipeline) {
pipeline.remove0(this); pipeline.remove0(this, true);
} }
prev.teardown(); prev.teardown();
} }

View File

@ -316,14 +316,14 @@ final class DefaultChannelPipeline implements ChannelPipeline {
synchronized (this) { synchronized (this) {
if (!ctx.channel().isRegistered() || ctx.executor().inEventLoop()) { if (!ctx.channel().isRegistered() || ctx.executor().inEventLoop()) {
remove0(ctx); remove0(ctx, false);
return ctx; return ctx;
} else { } else {
future = ctx.executor().submit(new Runnable() { future = ctx.executor().submit(new Runnable() {
@Override @Override
public void run() { public void run() {
synchronized (DefaultChannelPipeline.this) { synchronized (DefaultChannelPipeline.this) {
remove0(ctx); remove0(ctx, false);
} }
} }
}); });
@ -339,13 +339,13 @@ final class DefaultChannelPipeline implements ChannelPipeline {
return context; return context;
} }
void remove0(DefaultChannelHandlerContext ctx) { void remove0(DefaultChannelHandlerContext ctx, boolean tearDown) {
DefaultChannelHandlerContext prev = ctx.prev; DefaultChannelHandlerContext prev = ctx.prev;
DefaultChannelHandlerContext next = ctx.next; DefaultChannelHandlerContext next = ctx.next;
prev.next = next; prev.next = next;
next.prev = prev; next.prev = prev;
name2ctx.remove(ctx.name()); name2ctx.remove(ctx.name());
callHandlerRemoved(ctx); callHandlerRemoved(ctx, tearDown);
} }
@Override @Override
@ -450,7 +450,7 @@ final class DefaultChannelPipeline implements ChannelPipeline {
// because callHandlerRemoved() will trigger inboundBufferUpdated() or flush() on newHandler and those // because callHandlerRemoved() will trigger inboundBufferUpdated() or flush() on newHandler and those
// event handlers must be called after handlerAdded(). // event handlers must be called after handlerAdded().
callHandlerAdded(newCtx); callHandlerAdded(newCtx);
callHandlerRemoved(oldCtx); callHandlerRemoved(oldCtx, false);
} }
private static void checkMultiplicity(ChannelHandlerContext ctx) { private static void checkMultiplicity(ChannelHandlerContext ctx) {
@ -505,24 +505,26 @@ final class DefaultChannelPipeline implements ChannelPipeline {
} }
} }
private void callHandlerRemoved(final DefaultChannelHandlerContext ctx) { private void callHandlerRemoved(final DefaultChannelHandlerContext ctx, final boolean tearDown) {
if (ctx.channel().isRegistered() && !ctx.executor().inEventLoop()) { if (ctx.channel().isRegistered() && !ctx.executor().inEventLoop()) {
ctx.executor().execute(new Runnable() { ctx.executor().execute(new Runnable() {
@Override @Override
public void run() { public void run() {
callHandlerRemoved0(ctx); callHandlerRemoved0(ctx, tearDown);
} }
}); });
return; return;
} }
callHandlerRemoved0(ctx); callHandlerRemoved0(ctx, tearDown);
} }
private void callHandlerRemoved0(final DefaultChannelHandlerContext ctx) { private void callHandlerRemoved0(final DefaultChannelHandlerContext ctx, boolean tearDown) {
// Notify the complete removal. // Notify the complete removal.
try { try {
ctx.handler().handlerRemoved(ctx); ctx.handler().handlerRemoved(ctx);
if (!tearDown) {
ctx.setRemoved(); ctx.setRemoved();
}
} catch (Throwable t) { } catch (Throwable t) {
fireExceptionCaught(new ChannelPipelineException( fireExceptionCaught(new ChannelPipelineException(
ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t)); ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));