netty/netty — pull request #1969 by trustin (2013-11-06 14:48:49+00:00)
Title: Add the event trigger methods to EventLoop and use it throughout the pipeline

Start looking from the following classes:

* `DefaultChannelHandlerContext`
* `SingleThreadEventLoop`
* `EventLoopUtil`
* `EmbeddedEventLoop`

Basically, this changeset simplifies `DefaultChannelHandlerContext` by extracting some of its roles to `EventLoop` (and `EventLoopUtil`.)

We use `EventExecutor.inEventLoop()` to decide to invoke a task directly or use `execute()` or `submit()` in many places. With this change, a user can implement and use the custom `EventLoop` implementation.

One caveat is that it makes the semantic of `inEventLoop()` less useful. For example, the custom `EventLoop` implementation could handle different types of messages and events in different threads. Then, from what thread should `ChannelHandler.handlerRemove()` and `ChannelFutureListener.operationComplete()` be invoked? What about the cleanup tasks triggered by some advanced handlers such as `SslHandler`? The custom implementation introduces uncertainty here.

To avoid this uncertainty, the custom `EventLoop` implementation still needs to have a dedicated thread that returns `true` when queried via `inEventLoop()`. It could be achieved by extending `DefaultEventLoop` and overriding some `invoke*()` methods or using decorator pattern.

```java
public class MyEventLoopGroup extends DefaultEventLoopGroup {
private final Executor highPriorityTaskExecutor = ...;

protected EventLoop newChild(...) {
return new MyEventLoop(highPriorityTaskExecutor, ...);
}
}

// We could also use the decorator pattern instead of inheritance.
public class MyEventLoop extends DefaultEventLoop {

private final Executor highPriorityTaskExecutor = ...; // Passed from MyEventLoopGroup.newChild(..)
...

public void invokeChannelRead(ctx, msg) {
if (msg instanceof HighPriorityMessage) {
highPriorityTaskExecutor.execute(() -> { EventLoopUtil.invokeChannelReadNow(ctx, msg); });
} else {
super.invokeChannelRead(ctx, msg);
}
}
}
```

Would this be enough? I thought about the following use case:

* a pipeline contains at least two handlers and they are both assigned to the same `MyEventLoop`, and
* the first handler handles a `HighPriorityMessage` and passes it to the next handler in verbatim.

The example implementation above will call `highPriorityTaskExecutor.execute()` twice per message, and it is inefficient because second `execute()` could have been replaced with direct invocation, although `highPriorityTaskExecutor` implementation could optimize this away fairly easily.

Another use case would be:

* Use the main event loop for most messages and events, but
* handle certain messages that takes long time in a separate thread pool.

It also can be achieved fairly easily by using decorator pattern; just wrap `channel.eventLoop()` and intercept the time consuming messages.

Needless to say, it is also possible to implement the thread model that is discussed [here](https://groups.google.com/forum/#!topic/netty/1kAS-FJWGRE/discussion).

This changeset also includes the following changes:

* `EventExecutorGroup` doesn't implement `Iterable` anymore. Instead I added `children()` method. Why? Because Java Generics sucks when it comes to inheritance.
* `LocalEventLoopGroup` has been deprecated by `DefaultEventLoopGroup`.