netty single-threaded event loop

netty event executor group and event executor definition and abstract implementation: http://donald-draper.iteye.com/blog/2391257
netty multi-threaded event executor group: http://donald-draper.iteye.com/blog /2391270
netty multithreaded event loop group: http://donald-draper.iteye.com/blog/2391276
netty abstract scheduling event executor: http://donald-draper.iteye.com/blog/2391379
netty single-threaded event Executor initialization: http://donald-draper.iteye.com/blog/2391895
netty single-threaded event executor executes tasks with graceful shutdown: http://donald-draper.iteye.com/blog/2392051
Introduction :
above An article saw the task execution and executor shutdown of the single-threaded event executor. Let’s review it first:
      single-threaded event executor, execute tasks, first determine whether the task is null, throw a null pointer exception if it is null, otherwise, judge Whether the thread is in the current event loop, then add the task to the task queue, otherwise open the current single-threaded event executor and add the task to the task queue, if the event executor is closed at this time and the task can be removed, throw Reject the executor task exception; if you need to start the event executor wake-up thread, add the wake-up thread to the task queue.
      Add, remove, poll task operations are actually delegated to the task queue, and add, remove hook thread operations are delegated to the closing hooks thread collection.
      The single-threaded event executor takes a task. First, it schedules the task from the head of the scheduling task queue peek. If the task is not empty, it obtains the scheduling task delay time. If the delay time is greater than 0, it will timeout the poll task from the task queue. The scheduling task queue grabs the scheduling task, adds it to the task queue, and polls the task from the task queue; if the scheduling task is empty, it takes a task from the task queue, and if it is a wake-up task, it is ignored.
      To close a single-threaded executor, first check the interval, timeout, and time unit parameters, and the interval should be less than the timeout. If it has been closed, return the result of the asynchronous shutdown task, otherwise check whether the thread is in the current transaction loop, and if so, update The status is closing, and the closing interval and timeout are calculated.
Let's take a look at the single-threaded event loop SingleThreadEventLoop today:
package io.netty.channel;

import io.netty.util.concurrent.RejectedExecutionHandler;
import io.netty.util.concurrent.RejectedExecutionHandlers;
import io.netty.util.concurrent.SingleThreadEventExecutor;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.UnstableApi;

import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;

/**
 * Abstract base class for {@link EventLoop}s that execute all its submitted tasks in a single thread.
 Single-threaded event loop executes all submitted tasks in a single thread
 *
 */
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
 protected static final int DEFAULT_MAX_PENDING_TASKS = Math.max(16,
            SystemPropertyUtil.getInt("io.netty.eventLoop.maxPendingTasks", Integer.MAX_VALUE));
    //Current circular task queue
    private final Queue<Runnable> tailTasks;
    //The following construction methods are basically the same as the single-threaded event executor, so I won't talk about it
    protected SingleThreadEventLoop(EventLoopGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp) {
        this(parent, threadFactory, addTaskWakesUp, DEFAULT_MAX_PENDING_TASKS, RejectedExecutionHandlers.reject());
    }

    protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor, boolean addTaskWakesUp) {
        this(parent, executor, addTaskWakesUp, DEFAULT_MAX_PENDING_TASKS, RejectedExecutionHandlers.reject());
    }

    protected SingleThreadEventLoop(EventLoopGroup parent, ThreadFactory threadFactory,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, threadFactory, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
        tailTasks = newTaskQueue(maxPendingTasks);
    }

    protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
        tailTasks = newTaskQueue(maxPendingTasks);
    }
    //Get the event loop group to which it belongs
    @Override
    public EventLoopGroup parent() {
        return (EventLoopGroup) super.parent();
    }
    //Get the current event loop
    @Override
    public EventLoop next() {
        return (EventLoop) super.next();
    }
   // register channel
    @Override
    public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }
    // register channel
    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
	//Delegated to the UnSafe associated with the channel, we will talk about this later
        promise.channel().unsafe().register(this, promise);
        return promise;
    }
    //This registration method has been discarded
    @Deprecated
    @Override
    public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        if (promise == null) {
            throw new NullPointerException("promise");
        }

        channel.unsafe().register(this, promise);
        return promise;
    }

    /**
     * Adds a task to be run once at the end of next (or current) {@code eventloop} iteration.
     * After the event loop iterates, run the specified task
     * @param task to be added.
     */
    @UnstableApi
    public final void executeAfterEventLoopIteration(Runnable task) {
        ObjectUtil.checkNotNull(task, "task");
        if (isShutdown()) {
	   // When the event loop is closed, a refusal to execute exception is thrown
            reject();
        }
        //If adding a task to the task queue fails, refuse to execute the task
        if (!tailTasks.offer(task)) {
            reject(task);
        }
        //If you need to start a wake-up thread for a task, add the wake-up thread to the task queue
        if (wakesUpForTask(task)) {
            wakeup(inEventLoop());
        }
    }

    /**
     * Removes a task that was added previously via {@link #executeAfterEventLoopIteration(Runnable)}.
     * After iterating through the event loop, remove the specified task
     * @param task to be removed.
     *
     * @return {@code true} if the task was removed as a result of this call.
     */
    @UnstableApi
    final boolean removeAfterEventLoopIterationTask(Runnable task) {
        return tailTasks.remove(ObjectUtil.checkNotNull(task, "task"));
    }
    //Whether to start the wakeup task thread
    @Override
    protected boolean wakesUpForTask(Runnable task) {
        return !(task instanceof NonWakeupRunnable);
    }
    //After running all tasks, execute the tasks in the tailTasks task queue
    @Override
    protected void afterRunningAllTasks() {
        runAllTasksFrom(tailTasks);
    }
    / / Determine whether there is a task in the tailTasks task queue
    @Override
    protected boolean hasTasks() {
        return super.hasTasks() || !tailTasks.isEmpty();
    }
    //Get the number of tasks in the task queue
    @Override
    public int pendingTasks() {
        return super.pendingTasks() + tailTasks.size();
    }

    /**
     * Marker interface for {@link Runnable} that will not trigger an {@link #wakeup(boolean)} in all cases.
     Marking a thread does not trigger a wake-up thread
     */
    interface NonWakeupRunnable extends Runnable { }
}

From the above point of view, the single-threaded event loop SingleThreadEventLoop inherits the single-threaded event executor, implements the event loop interface, and has an internal event loop task queue. We can regard the single-threaded event loop as a simple event executor, a single-threaded event loop. There is an additional channel registration method in the event loop, and the actual registration work is delegated to the UnSafe associated with the channel.

Let's look at the definition of the Nio event loop again:
/**
 * {@link SingleThreadEventLoop} implementation which register the {@link Channel}'s to a
 * {@link Selector} and so does the multi-plexing of these in the event loop.
 *
 */
public final class NioEventLoop extends SingleThreadEventLoop {


The nio event loop is actually a single-threaded event loop. The purpose of this is that the channel associated with the event loop is registered with a
selector , which can reuse loop events, that is, to ensure the thread safety of the channel's IO operation.

Summary:

The single-threaded event loop SingleThreadEventLoop inherits the single-threaded event executor, implements the event loop interface, and
has an internal event loop task queue. We can regard the single-threaded event loop as a simple event executor. In the single-threaded event loop There is an additional method for channel registration, and the actual registration work is delegated to the UnSafe associated with the channel.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326270905&siteId=291194637