From 119383873d84e0da24916e4240e5fdfc4dc0016d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 18 Apr 2017 07:52:23 +0200 Subject: [PATCH] VoidChannelPromise not notified when exception is thrown. Motivation: When a VoidChannelPromise is used by the user we need to ensure we propergate the exception through the ChannelPipeline otherwise the exception will just be swallowed and so the user has no idea whats going on. Modifications: - Always call tryFailure / trySuccess even when we use the VoidChannelPromise - Add unit test Result: Fixes [#6622]. --- .../AbstractChannelHandlerContext.java | 6 ++-- .../netty/channel/ChannelOutboundBuffer.java | 12 ++++---- .../channel/DefaultChannelPipelineTest.java | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java index 55e596f7f6..796bddf0ff 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java @@ -832,9 +832,9 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap } private static void notifyOutboundHandlerException(Throwable cause, ChannelPromise promise) { - if (!(promise instanceof VoidChannelPromise)) { - PromiseNotificationUtil.tryFailure(promise, cause, logger); - } + // Only log if the given promise is not of type VoidChannelPromise as tryFailure(...) is expected to return + // false. + PromiseNotificationUtil.tryFailure(promise, cause, promise instanceof VoidChannelPromise ? null : logger); } private void notifyHandlerException(Throwable cause) { diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java index 52bf57d8da..34fbde3556 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java @@ -664,15 +664,15 @@ public final class ChannelOutboundBuffer { } private static void safeSuccess(ChannelPromise promise) { - if (!(promise instanceof VoidChannelPromise)) { - PromiseNotificationUtil.trySuccess(promise, null, logger); - } + // Only log if the given promise is not of type VoidChannelPromise as trySuccess(...) is expected to return + // false. + PromiseNotificationUtil.trySuccess(promise, null, promise instanceof VoidChannelPromise ? null : logger); } private static void safeFail(ChannelPromise promise, Throwable cause) { - if (!(promise instanceof VoidChannelPromise)) { - PromiseNotificationUtil.tryFailure(promise, cause, logger); - } + // Only log if the given promise is not of type VoidChannelPromise as tryFailure(...) is expected to return + // false. + PromiseNotificationUtil.tryFailure(promise, cause, promise instanceof VoidChannelPromise ? null : logger); } @Deprecated diff --git a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java index 4f745e439c..39f6752b2d 100644 --- a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java +++ b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java @@ -1073,6 +1073,35 @@ public class DefaultChannelPipelineTest { group.shutdownGracefully(0, 0, TimeUnit.SECONDS); } + @Test(timeout = 3000) + public void testVoidPromiseNotify() throws Throwable { + ChannelPipeline pipeline1 = new LocalChannel().pipeline(); + + EventLoopGroup defaultGroup = new DefaultEventLoopGroup(1); + EventLoop eventLoop1 = defaultGroup.next(); + final Promise promise = eventLoop1.newPromise(); + final Exception exception = new IllegalArgumentException(); + try { + eventLoop1.register(pipeline1.channel()).syncUninterruptibly(); + pipeline1.addLast(new ChannelDuplexHandler() { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + throw exception; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + promise.setSuccess(cause); + } + }); + pipeline1.write("test", pipeline1.voidPromise()); + assertSame(exception, promise.syncUninterruptibly().getNow()); + } finally { + pipeline1.channel().close().syncUninterruptibly(); + defaultGroup.shutdownGracefully(); + } + } + private static final class TestTask implements Runnable { private final ChannelPipeline pipeline;