NOTE

4. Register Read Events with the Selector

1. Code to analyze 2. Set a breakpoint and connect with nc 3. Propagate channelActive 4. Reach HeadContext 5. Read through AbstractUnsafe

JavaCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. Code to Analyze

  • The registration operation in io.netty.channel.AbstractChannel.AbstractUnsafe#register.
//...
register0(promise);//...
//...

2. Set a Breakpoint and Connect with nc

This is actually similar to registering accept with the selector when the server starts. Look at io.netty.channel.AbstractChannel.AbstractUnsafe#register0.

Connect using nc, and execution eventually reaches pipeline.fireChannelActive().

private void register0(ChannelPromise promise) {
    try {
       //.....
       // Set a breakpoint on this line.
        if (isActive()) {
            if (firstRegistration) {
                // Call pipeline.fireChannelActive.
                pipeline.fireChannelActive();
            } else if (config().isAutoRead()) {
                // This channel was registered before and autoRead() is set. This means we need to begin read
                // again so that we process inbound data.
                //
                // See https://github.com/netty/netty/issues/4805
                beginRead();
            }
        }
    } catch (Throwable t) {
        // Close the channel directly to avoid FD leak.
        closeForcibly();
        closeFuture.setClosed();
        safeSetFailure(promise, t);
    }
}

3. Propagate the channelActive Event Through the Pipeline

  • DefaultChannelPipeline#fireChannelActive
public final ChannelPipeline fireChannelActive() {
    // io.netty.channel.AbstractChannelHandlerContext#invokeChannelActive(io.netty.channel.AbstractChannelHandlerContext)
    // Propagate through AbstractChannelHandlerContext.
    AbstractChannelHandlerContext.invokeChannelActive(head);
    return this;
}

3.1. Propagate Through AbstractChannelHandlerContext

AbstractChannelHandlerContext#invokeChannelActive

static void invokeChannelActive(final AbstractChannelHandlerContext next) {
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        // Enters here.
        // io.netty.channel.AbstractChannelHandlerContext#invokeChannelActive()
        next.invokeChannelActive();
    } else {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                next.invokeChannelActive();
            }
        });
    }
}
  • AbstractChannelHandlerContext#invokeChannelActive()
private void invokeChannelActive() {
    if (invokeHandler()) {
        try {
            // io.netty.channel.DefaultChannelPipeline.HeadContext#channelActive
            ((ChannelInboundHandler) handler()).channelActive(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    } else {
        fireChannelActive();
    }
}

4. Reach HeadContext First

  • DefaultChannelPipeline.HeadContext#channelActive
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelActive();
    // io.netty.channel.DefaultChannelPipeline.HeadContext#readIfIsAutoRead
    readIfIsAutoRead();
}

4.1. Call Channel.read

  • DefaultChannelPipeline.HeadContext#readIfIsAutoRead
private void readIfIsAutoRead() {
    if (channel.config().isAutoRead()) {
        // io.netty.channel.AbstractChannel#read
        channel.read();
    }
}
  • AbstractChannel#read
public Channel read() {
    // Propagate from pipeline tail to head, eventually reaching
    // io.netty.channel.AbstractChannel.AbstractUnsafe#beginRead.
    pipeline.read();
    return this;
}

5. AbstractUnsafe Performs the Read Operation

  • AbstractChannel.AbstractUnsafe#beginRead
public final void beginRead() {
    assertEventLoop();

    if (!isActive()) {
        return;
    }

    try {
        // io.netty.channel.nio.AbstractNioChannel#doBeginRead
        doBeginRead();
    } catch (final Exception e) {
        invokeLater(new Runnable() {
            @Override
            public void run() {
                pipeline.fireExceptionCaught(e);
            }
        });
        close(voidPromise());
    }
}

5.1. Register the Read Event

  • AbstractNioChannel#doBeginRead
protected void doBeginRead() throws Exception {
    // Channel.read() or ChannelHandlerContext.read() was called
    final SelectionKey selectionKey = this.selectionKey;
    if (!selectionKey.isValid()) {
        return;
    }

    readPending = true;
    // Register the read event.
    final int interestOps = selectionKey.interestOps();
    if ((interestOps & readInterestOp) == 0) {
        selectionKey.interestOps(interestOps | readInterestOp);
    }
}

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub