Add ChannelHandlerContext.invoker()

Motivation:

Being able to access the invoker() is useful when adding additional
handlers that should be running in the same thread. Since an application
may be using a threading model unsupported by the default invoker, they
can specify their own. Because of that, in a handler that auto-adds
other handlers:

// This is a good pattern
ctx.pipeline().addBefore(ctx.invoker(), ctx.name(), null, newHandler);
// This will generally work, but prevents using custom invoker.
ctx.pipeline().addBefore(ctx.executor(), ctx.name(), null, newHandler);

That's why I believe in commit 110745b0, for the now-defunct 5.0 branch,
when ChannelHandlerAppender was added the invoker() method was also
necessary.

There is a side-benefit to exposing the invoker: in certain advanced
use-cases using the invoker for a particular handler is useful. Using
the invoker you are able to invoke a _particular_ handler, from possibly
a different thread yet still using standard exception processing.

ChannelHandlerContext does part of that, but is unwieldy when trying to
invoke a particular handler because it invokes the prev or next handler,
not the one the context is for. A workaround is to use the next or prev
context (respectively), but this breaks when the pipeline changes.

This came up during writing the Http2MultiplexCodec which uses a
separate child channel for each http/2 stream and wants to send messages
from the child channel directly to the Http2MultiplexCodec handler that
created it.

Modifications:

Add the invoker() method to ChannelHandlerContext. It was already being
implemented by AbstractChannelHandlerContext. The two other
implementations of ChannelHandlerContext needed minor tweaks.

Result:

Access to the invoker used for a particular handler, for either reusing
for other handlers or for advanced use-cases. Fixes #4738
This commit is contained in:
Eric Anderson 2016-01-21 12:32:57 -08:00 committed by Norman Maurer
parent d1ef33b8f4
commit 6dbb610f5b
4 changed files with 24 additions and 3 deletions

View File

@ -20,9 +20,11 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandlerInvoker;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
@ -33,7 +35,7 @@ import java.net.SocketAddress;
public abstract class EmbeddedChannelWriteReleaseHandlerContext implements ChannelHandlerContext {
private static final String HANDLER_NAME = "microbench-delegator-ctx";
private final EventExecutor executor;
private final EventLoop eventLoop;
private final Channel channel;
private final ByteBufAllocator alloc;
private final ChannelHandler handler;
@ -48,7 +50,7 @@ public abstract class EmbeddedChannelWriteReleaseHandlerContext implements Chann
this.alloc = checkNotNull(alloc, "alloc");
this.channel = checkNotNull(channel, "channel");
this.handler = checkNotNull(handler, "handler");
this.executor = checkNotNull(channel.eventLoop(), "executor");
this.eventLoop = checkNotNull(channel.eventLoop(), "eventLoop");
}
protected abstract void handleException(Throwable t);
@ -70,7 +72,12 @@ public abstract class EmbeddedChannelWriteReleaseHandlerContext implements Chann
@Override
public EventExecutor executor() {
return executor;
return eventLoop;
}
@Override
public ChannelHandlerInvoker invoker() {
return eventLoop.asInvoker();
}
@Override

View File

@ -82,6 +82,7 @@ abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, R
return invoker().executor();
}
@Override
public ChannelHandlerInvoker invoker() {
if (invoker == null) {
return channel().unsafe().invoker();

View File

@ -136,6 +136,14 @@ public interface ChannelHandlerContext extends AttributeMap {
*/
EventExecutor executor();
/**
* Returns the {@link ChannelHandlerInvoker} which is used to trigger an event for the associated
* {@link ChannelHandler}. Note that the methods in {@link ChannelHandlerInvoker} are not intended to be called
* by a user. Use this method only to obtain the reference to the {@link ChannelHandlerInvoker}
* (and not calling its methods) unless you know what you are doing.
*/
ChannelHandlerInvoker invoker();
/**
* The unique name of the {@link ChannelHandlerContext}.The name was used when then {@link ChannelHandler}
* was added to the {@link ChannelPipeline}. This name can also be used to access the registered

View File

@ -372,6 +372,11 @@ public class CombinedChannelDuplexHandler<I extends ChannelInboundHandler, O ext
return ctx.executor();
}
@Override
public ChannelHandlerInvoker invoker() {
return ctx.invoker();
}
@Override
public String name() {
return ctx.name();