Thanks @wgallagher for a feed back.  I do agree that this is not ideal.  Do you have some suggestions for alternative ways to implement the idea?

Here's another idea:

Introduce an interface like this:

```java
public interface ChannelEventInvoker {
  // Move the new methods in EventLoop here.
  void fireChannelRead(ctx, msg);
  ...
}
```

A user specifies `ChannelEventInvoker` instead of `EventLoopGroup` when calling `ChannelPipeline.addLast(..)`.  If unspecified, the default one would be something like `new DefaultChannelEventInvoker(pipeline.channel().eventLoop())`. `DefaultChannelEventInvoker` would look like the new methods in `SingleThreadEventLoop`.

An embedded channel will use an alternative implementation that always performs direct invocation.

One caveat of this idea is that it'll be difficult to pool the `Runnable`s.  Previously, we could have defined a `Runnable` implementation that is capable of keeping all event method parameters into its fields while hiding the `Runnable` implementation from users, and pool them all.  It's not that it's impossible though and it might not be a big deal.

What do you think? (/cc @normanmaurer and @jpinner)