源码阅读之NioEventLoop的执行server channel注册与绑定任务。
上一篇追到了server channel的初始化以及注册但没有真正的完成注册,只是将server channel 包装成一个task r然后推进队列中,接着启动了EventLoop下的线程。肯定是通过这线程从队列的另一端将task取出来执行,都是这么个套路,现在就继续跟下去看它真正完成注册。
文章目录
- 源码阅读之NioEventLoop的执行server channel注册与绑定任务。
-
- SingleThreadEventExecutor类下的doStartThread方法
- NioEventLoop.run()方法
- 执行AbstractChannel.register0(ChannelPromise promise)
-
- 执行AbstractChannel.doRegister()开始注册
- 调用pipeline.invokeHandlerAddedIfNeeded();
- 调用ChannelInitializer对象的方法initChannel(ctx)。
- 回到ServerBootstrap的类下的init方法
- 调用DefaultChannelPipeline对象的addLast的方法1
- 调用DefaultChannelPipeline类的addLast方法2
- 将ServerBootstrapAcceptor添加到pipeline下。
- 接着DefaultPromise通知监听器去叫Serverbootstarp进行端口绑定。
- 紧跟着调用server handlers下的channelRegistered方法。
- 从队列里将封装有Acceptor的task取出来然后执行。
- 从队列里将用来执行channel 绑定的任务取出来然后执行。
- 绑定完后创建一个Task用于执行server Handlers下的channelActive方法。
- 总结
SingleThreadEventExecutor类下的doStartThread方法
由于这个run方法是重写了SingleThreadEventExecutor类下面的run方法,在执行这个EventLoop子类的run的方法是因为没有发现有现成的线程,所以需要通过executor下面的线程工厂创建一个线程。
回顾一下 SingleThreadEventExecutor类下的doStartThread方法
1.在给EventLoop分配一个线程的时候,顺带将thread的引用保存下来,这个用于到IneventLoop方法中,被反复用于判断一个task是不是在当前EventLoop线程下执行。
2.接着SingleThreadEventExecutor.this.run()方法执行是调用NioEventLoop的run方法,到此EvenLoop的run方法就此不停地执行下去。
private void doStartThread() {
assert thread == null;
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
for (;;) {
int oldState = STATE_UPDATER.get(SingleThreadEventExecutor.this);
if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
break;
}
}
// Check if confirmShutdown() was called at the end of the loop.
if (success && gracefulShutdownStartTime == 0) {
logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
"before run() implementation terminates.");
}
try {
// Run all remaining tasks and shutdown hooks.
for (;;) {
if (confirmShutdown()) {
break;
}
}
} finally {
try {
cleanup();
} finally {
STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
threadLock.release();
if (!taskQueue.isEmpty()) {
logger.warn(
"An event executor terminated with " +
"non-empty task queue (" + taskQueue.size() + ')');
}
terminationFuture.setSuccess(null);
}
}
}
}
});
}
NioEventLoop.run()方法
先上run方法的代码,这是一个死循环,反复从队列里轮询是否有可执行的任务。
@Override
protected void run() {
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
// 'wakenUp.compareAndSet(false, true)' is always evaluated
// before calling 'selector.wakeup()' to reduce the wake-up
// overhead. (Selector.wakeup() is an expensive operation.)
//
// However, there is a race condition in this approach.
// The race condition is triggered when 'wakenUp' is set to
// true too early.
//
// 'wakenUp' is set to true too early if:
// 1) Selector is waken up between 'wakenUp.set(false)' and
// 'selector.select(...)'. (BAD)
// 2) Selector is waken up between 'selector.select(...)' and
// 'if (wakenUp.get()) { ... }'. (OK)
//
// In the first case, 'wakenUp' is set to true and the
// following 'selector.select(...)' will wake up immediately.
// Until 'wakenUp' is set to false again in the next round,
// 'wakenUp.compareAndSet(false, true)' will fail, and therefore
// any attempt to wake up the Selector will fail, too, causing
// the following 'selector.select(...)' call to block
// unnecessarily.
//
// To fix this problem, we wake up the selector again if wakenUp
// is true immediately after selector.select(...).
// It is inefficient in that it wakes up the selector for both
// the first case (BAD - wake-up required) and the second case
// (OK - no wake-up required).
if (wakenUp.get()) {
selector.wakeup();
}
default: