Repository: netty/netty
Issue: Add ChannelHandlerContext.invoker() to 4.1
Labels: feature

The now-defunct 5.0 had a [ChannelHandlerContext.invoker()](https://github.com/netty/netty/blob/master_deprecated/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java#L148) method. I found it to be useful, and the method is already implemented; it just needs to be exposed on the interface. It's my understanding of Netty API stability policy that new methods can be added to interfaces between minor versions (thus, 4.1, since it isn't final _yet_). If 4.1 is too close to release to add the API, I understand.

I will grant that needing to use the invoker is rare and quite advanced, but in some ways it isn't that strange. When writing Http2MultiplexCodec I used the invoker because: 1) I needed to call a particular handler 2) from possibly a different thread 3) while ideally not having to deal with exceptions. Parts 2 and 3 are well-served by ChannelHandlerContext, but 1 isn't because it will call the _next_ or _previous_ handler and using the previous or next context (respectively) to compensate is unstable as the pipeline is modified.

It is possible to write your own invoker on top of `ctx.executor()`, but that seems like needless boilerplate and cause for trouble if someone has actually specified their own ChannelHandlerInvoker for the channel.

Some additional context: https://github.com/netty/netty/pull/4503#discussion_r45705972


Issue comments:
@normanmaurer @trustin - Any objections?

@ejona86 works for me. Please open a pr


Pull request review comments:

Patch that resolved the issue:
diff --git a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteReleaseHandlerContext.java b/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteReleaseHandlerContext.java
index b8984ea488d..9a74f98d97d 100644
--- a/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteReleaseHandlerContext.java
+++ b/microbench/src/main/java/io/netty/microbench/channel/EmbeddedChannelWriteReleaseHandlerContext.java
@@ -20,9 +20,11 @@
 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 @@
 
 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 EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, Channel
         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 Channel channel() {
 
     @Override
     public EventExecutor executor() {
-        return executor;
+        return eventLoop;
+    }
+
+    @Override
+    public ChannelHandlerInvoker invoker() {
+        return eventLoop.asInvoker();
     }
 
     @Override
diff --git a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java
index f9e7b80286d..f3b4b12c14e 100644
--- a/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java
+++ b/transport/src/main/java/io/netty/channel/AbstractChannelHandlerContext.java
@@ -82,6 +82,7 @@ public EventExecutor executor() {
         return invoker().executor();
     }
 
+    @Override
     public ChannelHandlerInvoker invoker() {
         if (invoker == null) {
             return channel().unsafe().invoker();
diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java
index 7b8e7c4e339..0fdb8476ad7 100644
--- a/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java
+++ b/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java
@@ -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
diff --git a/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java b/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java
index 4abc4884687..bf6d266b1d2 100644
--- a/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java
+++ b/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java
@@ -372,6 +372,11 @@ public EventExecutor executor() {
             return ctx.executor();
         }
 
+        @Override
+        public ChannelHandlerInvoker invoker() {
+            return ctx.invoker();
+        }
+
         @Override
         public String name() {
             return ctx.name();


Test patch: