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].
This commit is contained in:
Norman Maurer 2017-04-18 07:52:23 +02:00
parent 32aa8f980d
commit c62564cf4c
3 changed files with 38 additions and 9 deletions

View File

@ -825,9 +825,9 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap impleme
}
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) {

View File

@ -665,15 +665,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

View File

@ -1069,6 +1069,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 LocalEventLoopGroup(1);
EventLoop eventLoop1 = defaultGroup.next();
final Promise<Throwable> 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.channel().voidPromise());
assertSame(exception, promise.syncUninterruptibly().getNow());
} finally {
pipeline1.channel().close().syncUninterruptibly();
defaultGroup.shutdownGracefully();
}
}
private static final class TestTask implements Runnable {
private final ChannelPipeline pipeline;