Repository: netty/netty
File: transport/src/main/java/io/netty/channel/ManualIoEventLoop.java (Java)

Code before:
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadExecutorMap;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * {@link IoEventLoop} implementation that is owned by the user and so needs to be driven by the user manually with the
 * given {@link Thread}. This means that the user is responsible to call either {@link #runNow()} or {@link #run(long)}
 * to execute IO and tasks that were submitted to this {@link IoEventLoop}.
 * <p>
 * This is for <strong>advanced use-cases only</strong>, where the user wants to own the {@link Thread} that drives the
 * {@link IoEventLoop} to also do other work. Care must be taken that the {@link #runNow() or
 * {@link #waitAndRun()}} methods are called in a timely fashion.
 */
public class ManualIoEventLoop extends AbstractScheduledEventExecutor implements IoEventLoop {
    private static final int ST_STARTED = 0;
    private static final int ST_SHUTTING_DOWN = 1;
    private static final int ST_SHUTDOWN = 2;
    private static final int ST_TERMINATED = 3;

    private final AtomicInteger state;
    private final Promise<?> terminationFuture = new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE);
    private final Queue<Runnable> taskQueue = PlatformDependent.newMpscQueue();
    private final IoHandlerContext nonBlockingContext = new IoHandlerContext() {
        @Override
        public boolean canBlock() {
            assert inEventLoop();
            return false;
        }

        @Override
        public long delayNanos(long currentTimeNanos) {
            assert inEventLoop();
            return 0;
        }

        @Override
        public long deadlineNanos() {
            assert inEventLoop();
            return -1;

Review comment (suggestion):
If you remove the final, add a comment about sub class can refine the behaviors, otherwise, one day someone will add the final back again 

Code after:
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThreadExecutorMap;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * {@link IoEventLoop} implementation that is owned by the user and so needs to be driven by the user manually with the
 * given {@link Thread}. This means that the user is responsible to call either {@link #runNow()} or {@link #run(long)}
 * to execute IO and tasks that were submitted to this {@link IoEventLoop}.
 * <p>
 * This is for <strong>advanced use-cases only</strong>, where the user wants to own the {@link Thread} that drives the
 * {@link IoEventLoop} to also do other work. Care must be taken that the {@link #runNow() or
 * {@link #waitAndRun()}} methods are called in a timely fashion.
 */
public class ManualIoEventLoop extends AbstractScheduledEventExecutor implements IoEventLoop {
    private static final Runnable WAKEUP_TASK = () -> {
        // NOOP
    };
    private static final int ST_STARTED = 0;
    private static final int ST_SHUTTING_DOWN = 1;
    private static final int ST_SHUTDOWN = 2;
    private static final int ST_TERMINATED = 3;

    private final AtomicInteger state;
    private final Promise<?> terminationFuture = new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE);
    private final Queue<Runnable> taskQueue = PlatformDependent.newMpscQueue();
    private final IoHandlerContext nonBlockingContext = new IoHandlerContext() {
        @Override
        public boolean canBlock() {
            assert inEventLoop();
            return false;
        }

        @Override
        public long delayNanos(long currentTimeNanos) {
            assert inEventLoop();
            return 0;
        }

        @Override