// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.util.concurrent;

import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Callable;
import org.jetbrains.annotations.Async;
import java.util.Iterator;
import java.util.List;
import java.util.Collection;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.ThreadExecutorMap;
import java.util.LinkedHashSet;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ThreadFactory;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.Executor;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import io.netty.util.internal.logging.InternalLogger;

public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor
{
    static final int DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
    private static final InternalLogger logger;
    private static final int ST_NOT_STARTED = 1;
    private static final int ST_SUSPENDING = 2;
    private static final int ST_SUSPENDED = 3;
    private static final int ST_STARTED = 4;
    private static final int ST_SHUTTING_DOWN = 5;
    private static final int ST_SHUTDOWN = 6;
    private static final int ST_TERMINATED = 7;
    private static final Runnable NOOP_TASK;
    private static final AtomicIntegerFieldUpdater<SingleThreadEventExecutor> STATE_UPDATER;
    private static final AtomicReferenceFieldUpdater<SingleThreadEventExecutor, ThreadProperties> PROPERTIES_UPDATER;
    private static final AtomicLongFieldUpdater<SingleThreadEventExecutor> ACCUMULATED_ACTIVE_TIME_NANOS_UPDATER;
    private static final AtomicIntegerFieldUpdater<SingleThreadEventExecutor> CONSECUTIVE_IDLE_CYCLES_UPDATER;
    private static final AtomicIntegerFieldUpdater<SingleThreadEventExecutor> CONSECUTIVE_BUSY_CYCLES_UPDATER;
    private final Queue<Runnable> taskQueue;
    private volatile Thread thread;
    private volatile ThreadProperties threadProperties;
    private final Executor executor;
    private volatile boolean interrupted;
    private final Lock processingLock;
    private final CountDownLatch threadLock;
    private final Set<Runnable> shutdownHooks;
    private final boolean addTaskWakesUp;
    private final int maxPendingTasks;
    private final RejectedExecutionHandler rejectedExecutionHandler;
    private final boolean supportSuspension;
    private volatile long accumulatedActiveTimeNanos;
    private volatile long lastActivityTimeNanos;
    private volatile int consecutiveIdleCycles;
    private volatile int consecutiveBusyCycles;
    private long lastExecutionTime;
    private volatile int state;
    private volatile long gracefulShutdownQuietPeriod;
    private volatile long gracefulShutdownTimeout;
    private long gracefulShutdownStartTime;
    private final Promise<?> terminationFuture;
    private static final long SCHEDULE_PURGE_INTERVAL;
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final ThreadFactory threadFactory, final boolean addTaskWakesUp) {
        this(parent, new ThreadPerTaskExecutor(threadFactory), addTaskWakesUp);
    }
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final ThreadFactory threadFactory, final boolean addTaskWakesUp, final int maxPendingTasks, final RejectedExecutionHandler rejectedHandler) {
        this(parent, new ThreadPerTaskExecutor(threadFactory), addTaskWakesUp, maxPendingTasks, rejectedHandler);
    }
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final ThreadFactory threadFactory, final boolean addTaskWakesUp, final boolean supportSuspension, final int maxPendingTasks, final RejectedExecutionHandler rejectedHandler) {
        this(parent, new ThreadPerTaskExecutor(threadFactory), addTaskWakesUp, supportSuspension, maxPendingTasks, rejectedHandler);
    }
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final Executor executor, final boolean addTaskWakesUp) {
        this(parent, executor, addTaskWakesUp, SingleThreadEventExecutor.DEFAULT_MAX_PENDING_EXECUTOR_TASKS, RejectedExecutionHandlers.reject());
    }
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final Executor executor, final boolean addTaskWakesUp, final int maxPendingTasks, final RejectedExecutionHandler rejectedHandler) {
        this(parent, executor, addTaskWakesUp, false, maxPendingTasks, rejectedHandler);
    }
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final Executor executor, final boolean addTaskWakesUp, final boolean supportSuspension, final int maxPendingTasks, final RejectedExecutionHandler rejectedHandler) {
        super(parent);
        this.processingLock = new ReentrantLock();
        this.threadLock = new CountDownLatch(1);
        this.shutdownHooks = new LinkedHashSet<Runnable>();
        this.state = 1;
        this.terminationFuture = new DefaultPromise<Object>(GlobalEventExecutor.INSTANCE);
        this.addTaskWakesUp = addTaskWakesUp;
        this.supportSuspension = supportSuspension;
        this.maxPendingTasks = Math.max(16, maxPendingTasks);
        this.executor = ThreadExecutorMap.apply(executor, this);
        this.taskQueue = this.newTaskQueue(this.maxPendingTasks);
        this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
        this.lastActivityTimeNanos = this.ticker().nanoTime();
    }
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final Executor executor, final boolean addTaskWakesUp, final Queue<Runnable> taskQueue, final RejectedExecutionHandler rejectedHandler) {
        this(parent, executor, addTaskWakesUp, false, taskQueue, rejectedHandler);
    }
    
    protected SingleThreadEventExecutor(final EventExecutorGroup parent, final Executor executor, final boolean addTaskWakesUp, final boolean supportSuspension, final Queue<Runnable> taskQueue, final RejectedExecutionHandler rejectedHandler) {
        super(parent);
        this.processingLock = new ReentrantLock();
        this.threadLock = new CountDownLatch(1);
        this.shutdownHooks = new LinkedHashSet<Runnable>();
        this.state = 1;
        this.terminationFuture = new DefaultPromise<Object>(GlobalEventExecutor.INSTANCE);
        this.addTaskWakesUp = addTaskWakesUp;
        this.supportSuspension = supportSuspension;
        this.maxPendingTasks = SingleThreadEventExecutor.DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
        this.executor = ThreadExecutorMap.apply(executor, this);
        this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
        this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }
    
    @Deprecated
    protected Queue<Runnable> newTaskQueue() {
        return this.newTaskQueue(this.maxPendingTasks);
    }
    
    protected Queue<Runnable> newTaskQueue(final int maxPendingTasks) {
        return new LinkedBlockingQueue<Runnable>(maxPendingTasks);
    }
    
    protected void interruptThread() {
        final Thread currentThread = this.thread;
        if (currentThread == null) {
            this.interrupted = true;
        }
        else {
            currentThread.interrupt();
        }
    }
    
    protected Runnable pollTask() {
        assert this.inEventLoop();
        return pollTaskFrom(this.taskQueue);
    }
    
    protected static Runnable pollTaskFrom(final Queue<Runnable> taskQueue) {
        Runnable task;
        do {
            task = taskQueue.poll();
        } while (task == SingleThreadEventExecutor.WAKEUP_TASK);
        return task;
    }
    
    protected Runnable takeTask() {
        assert this.inEventLoop();
        if (!(this.taskQueue instanceof BlockingQueue)) {
            throw new UnsupportedOperationException();
        }
        final BlockingQueue<Runnable> taskQueue = (BlockingQueue)this.taskQueue;
        while (true) {
            final ScheduledFutureTask<?> scheduledTask = this.peekScheduledTask();
            if (scheduledTask == null) {
                Runnable task = null;
                try {
                    task = taskQueue.take();
                    if (task == SingleThreadEventExecutor.WAKEUP_TASK) {
                        task = null;
                    }
                }
                catch (final InterruptedException ex) {}
                return task;
            }
            final long delayNanos = scheduledTask.delayNanos();
            Runnable task2 = null;
            if (delayNanos > 0L) {
                try {
                    task2 = taskQueue.poll(delayNanos, TimeUnit.NANOSECONDS);
                }
                catch (final InterruptedException e) {
                    return null;
                }
            }
            if (task2 == null) {
                this.fetchFromScheduledTaskQueue();
                task2 = taskQueue.poll();
            }
            if (task2 == null) {
                continue;
            }
            if (task2 == SingleThreadEventExecutor.WAKEUP_TASK) {
                return null;
            }
            return task2;
        }
    }
    
    private boolean fetchFromScheduledTaskQueue() {
        return this.fetchFromScheduledTaskQueue(this.taskQueue);
    }
    
    private boolean executeExpiredScheduledTasks() {
        if (this.scheduledTaskQueue == null || this.scheduledTaskQueue.isEmpty()) {
            return false;
        }
        final long nanoTime = this.getCurrentTimeNanos();
        Runnable scheduledTask = this.pollScheduledTask(nanoTime);
        if (scheduledTask == null) {
            return false;
        }
        do {
            AbstractEventExecutor.safeExecute(scheduledTask);
        } while ((scheduledTask = this.pollScheduledTask(nanoTime)) != null);
        return true;
    }
    
    protected Runnable peekTask() {
        assert this.inEventLoop();
        return this.taskQueue.peek();
    }
    
    protected boolean hasTasks() {
        assert this.inEventLoop();
        return !this.taskQueue.isEmpty();
    }
    
    public int pendingTasks() {
        return this.taskQueue.size();
    }
    
    protected void addTask(final Runnable task) {
        ObjectUtil.checkNotNull(task, "task");
        if (!this.offerTask(task)) {
            this.reject(task);
        }
    }
    
    final boolean offerTask(final Runnable task) {
        if (this.isShutdown()) {
            reject();
        }
        return this.taskQueue.offer(task);
    }
    
    protected boolean removeTask(final Runnable task) {
        return this.taskQueue.remove(ObjectUtil.checkNotNull(task, "task"));
    }
    
    protected boolean runAllTasks() {
        assert this.inEventLoop();
        boolean ranAtLeastOne = false;
        boolean fetchedAll;
        do {
            fetchedAll = this.fetchFromScheduledTaskQueue(this.taskQueue);
            if (this.runAllTasksFrom(this.taskQueue)) {
                ranAtLeastOne = true;
            }
        } while (!fetchedAll);
        if (ranAtLeastOne) {
            this.lastExecutionTime = this.getCurrentTimeNanos();
        }
        this.afterRunningAllTasks();
        return ranAtLeastOne;
    }
    
    protected final boolean runScheduledAndExecutorTasks(final int maxDrainAttempts) {
        assert this.inEventLoop();
        int drainAttempt = 0;
        boolean ranAtLeastOneTask;
        do {
            ranAtLeastOneTask = (this.runExistingTasksFrom(this.taskQueue) | this.executeExpiredScheduledTasks());
        } while (ranAtLeastOneTask && ++drainAttempt < maxDrainAttempts);
        if (drainAttempt > 0) {
            this.lastExecutionTime = this.getCurrentTimeNanos();
        }
        this.afterRunningAllTasks();
        return drainAttempt > 0;
    }
    
    protected final boolean runAllTasksFrom(final Queue<Runnable> taskQueue) {
        Runnable task = pollTaskFrom(taskQueue);
        if (task == null) {
            return false;
        }
        do {
            AbstractEventExecutor.safeExecute(task);
            task = pollTaskFrom(taskQueue);
        } while (task != null);
        return true;
    }
    
    private boolean runExistingTasksFrom(final Queue<Runnable> taskQueue) {
        Runnable task = pollTaskFrom(taskQueue);
        if (task == null) {
            return false;
        }
        int remaining = Math.min(this.maxPendingTasks, taskQueue.size());
        AbstractEventExecutor.safeExecute(task);
        while (remaining-- > 0 && (task = taskQueue.poll()) != null) {
            AbstractEventExecutor.safeExecute(task);
        }
        return true;
    }
    
    protected boolean runAllTasks(final long timeoutNanos) {
        this.fetchFromScheduledTaskQueue(this.taskQueue);
        Runnable task = this.pollTask();
        if (task == null) {
            this.afterRunningAllTasks();
            return false;
        }
        final long deadline = (timeoutNanos > 0L) ? (this.getCurrentTimeNanos() + timeoutNanos) : 0L;
        long runTasks = 0L;
        final long workStartTime = this.ticker().nanoTime();
        while (true) {
            do {
                AbstractEventExecutor.safeExecute(task);
                ++runTasks;
                if ((runTasks & 0x3FL) == 0x0L) {
                    final long lastExecutionTime = this.getCurrentTimeNanos();
                    if (lastExecutionTime >= deadline) {
                        final long workEndTime = this.ticker().nanoTime();
                        this.accumulatedActiveTimeNanos += workEndTime - workStartTime;
                        this.lastActivityTimeNanos = workEndTime;
                        this.afterRunningAllTasks();
                        this.lastExecutionTime = lastExecutionTime;
                        return true;
                    }
                }
                task = this.pollTask();
            } while (task != null);
            final long lastExecutionTime = this.getCurrentTimeNanos();
            continue;
        }
    }
    
    protected void afterRunningAllTasks() {
    }
    
    protected long delayNanos(long currentTimeNanos) {
        currentTimeNanos -= this.ticker().initialNanoTime();
        final ScheduledFutureTask<?> scheduledTask = this.peekScheduledTask();
        if (scheduledTask == null) {
            return SingleThreadEventExecutor.SCHEDULE_PURGE_INTERVAL;
        }
        return scheduledTask.delayNanos(currentTimeNanos);
    }
    
    protected long deadlineNanos() {
        final ScheduledFutureTask<?> scheduledTask = this.peekScheduledTask();
        if (scheduledTask == null) {
            return this.getCurrentTimeNanos() + SingleThreadEventExecutor.SCHEDULE_PURGE_INTERVAL;
        }
        return scheduledTask.deadlineNanos();
    }
    
    protected void updateLastExecutionTime() {
        final long now = this.getCurrentTimeNanos();
        this.lastExecutionTime = now;
        this.lastActivityTimeNanos = now;
    }
    
    protected int getNumOfRegisteredChannels() {
        return -1;
    }
    
    protected void reportActiveIoTime(final long nanos) {
        assert this.inEventLoop();
        if (nanos > 0L) {
            this.accumulatedActiveTimeNanos += nanos;
            this.lastActivityTimeNanos = this.ticker().nanoTime();
        }
    }
    
    protected long getAndResetAccumulatedActiveTimeNanos() {
        return SingleThreadEventExecutor.ACCUMULATED_ACTIVE_TIME_NANOS_UPDATER.getAndSet(this, 0L);
    }
    
    protected long getLastActivityTimeNanos() {
        return this.lastActivityTimeNanos;
    }
    
    protected int getAndIncrementIdleCycles() {
        return SingleThreadEventExecutor.CONSECUTIVE_IDLE_CYCLES_UPDATER.getAndIncrement(this);
    }
    
    protected void resetIdleCycles() {
        SingleThreadEventExecutor.CONSECUTIVE_IDLE_CYCLES_UPDATER.set(this, 0);
    }
    
    protected int getAndIncrementBusyCycles() {
        return SingleThreadEventExecutor.CONSECUTIVE_BUSY_CYCLES_UPDATER.getAndIncrement(this);
    }
    
    protected void resetBusyCycles() {
        SingleThreadEventExecutor.CONSECUTIVE_BUSY_CYCLES_UPDATER.set(this, 0);
    }
    
    protected boolean isSuspensionSupported() {
        return this.supportSuspension;
    }
    
    protected abstract void run();
    
    protected void cleanup() {
    }
    
    protected void wakeup(final boolean inEventLoop) {
        if (!inEventLoop) {
            this.taskQueue.offer(SingleThreadEventExecutor.WAKEUP_TASK);
        }
    }
    
    @Override
    public boolean inEventLoop(final Thread thread) {
        return thread == this.thread;
    }
    
    public void addShutdownHook(final Runnable task) {
        if (this.inEventLoop()) {
            this.shutdownHooks.add(task);
        }
        else {
            this.execute(new Runnable() {
                @Override
                public void run() {
                    SingleThreadEventExecutor.this.shutdownHooks.add(task);
                }
            });
        }
    }
    
    public void removeShutdownHook(final Runnable task) {
        if (this.inEventLoop()) {
            this.shutdownHooks.remove(task);
        }
        else {
            this.execute(new Runnable() {
                @Override
                public void run() {
                    SingleThreadEventExecutor.this.shutdownHooks.remove(task);
                }
            });
        }
    }
    
    private boolean runShutdownHooks() {
        boolean ran = false;
        while (!this.shutdownHooks.isEmpty()) {
            final List<Runnable> copy = new ArrayList<Runnable>(this.shutdownHooks);
            this.shutdownHooks.clear();
            for (final Runnable task : copy) {
                try {
                    AbstractEventExecutor.runTask(task);
                }
                catch (final Throwable t) {
                    SingleThreadEventExecutor.logger.warn("Shutdown hook raised an exception.", t);
                }
                finally {
                    ran = true;
                }
            }
        }
        if (ran) {
            this.lastExecutionTime = this.getCurrentTimeNanos();
        }
        return ran;
    }
    
    private void shutdown0(final long quietPeriod, final long timeout, final int shutdownState) {
        if (this.isShuttingDown()) {
            return;
        }
        final boolean inEventLoop = this.inEventLoop();
        while (!this.isShuttingDown()) {
            boolean wakeup = true;
            final int oldState = this.state;
            int newState = 0;
            if (inEventLoop) {
                newState = shutdownState;
            }
            else {
                switch (oldState) {
                    case 1:
                    case 2:
                    case 3:
                    case 4: {
                        newState = shutdownState;
                        break;
                    }
                    default: {
                        newState = oldState;
                        wakeup = false;
                        break;
                    }
                }
            }
            if (SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(this, oldState, newState)) {
                if (quietPeriod != -1L) {
                    this.gracefulShutdownQuietPeriod = quietPeriod;
                }
                if (timeout != -1L) {
                    this.gracefulShutdownTimeout = timeout;
                }
                if (this.ensureThreadStarted(oldState)) {
                    return;
                }
                if (wakeup) {
                    this.taskQueue.offer(SingleThreadEventExecutor.WAKEUP_TASK);
                    if (!this.addTaskWakesUp) {
                        this.wakeup(inEventLoop);
                    }
                }
            }
        }
    }
    
    @Override
    public Future<?> shutdownGracefully(final long quietPeriod, final long timeout, final TimeUnit unit) {
        ObjectUtil.checkPositiveOrZero(quietPeriod, "quietPeriod");
        if (timeout < quietPeriod) {
            throw new IllegalArgumentException("timeout: " + timeout + " (expected >= quietPeriod (" + quietPeriod + "))");
        }
        ObjectUtil.checkNotNull(unit, "unit");
        this.shutdown0(unit.toNanos(quietPeriod), unit.toNanos(timeout), 5);
        return this.terminationFuture();
    }
    
    @Override
    public Future<?> terminationFuture() {
        return this.terminationFuture;
    }
    
    @Deprecated
    @Override
    public void shutdown() {
        this.shutdown0(-1L, -1L, 6);
    }
    
    @Override
    public boolean isShuttingDown() {
        return this.state >= 5;
    }
    
    @Override
    public boolean isShutdown() {
        return this.state >= 6;
    }
    
    @Override
    public boolean isTerminated() {
        return this.state == 7;
    }
    
    @Override
    public boolean isSuspended() {
        final int currentState = this.state;
        return currentState == 3 || currentState == 2;
    }
    
    @Override
    public boolean trySuspend() {
        if (!this.supportSuspension) {
            return false;
        }
        if (SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(this, 4, 2)) {
            this.wakeup(this.inEventLoop());
            return true;
        }
        if (SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(this, 1, 3)) {
            return true;
        }
        final int currentState = this.state;
        return currentState == 3 || currentState == 2;
    }
    
    protected boolean canSuspend() {
        return this.canSuspend(this.state);
    }
    
    protected boolean canSuspend(final int state) {
        assert this.inEventLoop();
        return this.supportSuspension && (state == 3 || state == 2) && !this.hasTasks() && this.nextScheduledTaskDeadlineNanos() == -1L;
    }
    
    protected boolean confirmShutdown() {
        if (!this.isShuttingDown()) {
            return false;
        }
        if (!this.inEventLoop()) {
            throw new IllegalStateException("must be invoked from an event loop");
        }
        this.cancelScheduledTasks();
        if (this.gracefulShutdownStartTime == 0L) {
            this.gracefulShutdownStartTime = this.getCurrentTimeNanos();
        }
        if (this.runAllTasks() || this.runShutdownHooks()) {
            if (this.isShutdown()) {
                return true;
            }
            if (this.gracefulShutdownQuietPeriod == 0L) {
                return true;
            }
            this.taskQueue.offer(SingleThreadEventExecutor.WAKEUP_TASK);
            return false;
        }
        else {
            final long nanoTime = this.getCurrentTimeNanos();
            if (this.isShutdown() || nanoTime - this.gracefulShutdownStartTime > this.gracefulShutdownTimeout) {
                return true;
            }
            if (nanoTime - this.lastExecutionTime <= this.gracefulShutdownQuietPeriod) {
                this.taskQueue.offer(SingleThreadEventExecutor.WAKEUP_TASK);
                try {
                    Thread.sleep(100L);
                }
                catch (final InterruptedException ex) {}
                return false;
            }
            return true;
        }
    }
    
    @Override
    public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
        ObjectUtil.checkNotNull(unit, "unit");
        if (this.inEventLoop()) {
            throw new IllegalStateException("cannot await termination of the current thread");
        }
        this.threadLock.await(timeout, unit);
        return this.isTerminated();
    }
    
    @Override
    public void execute(final Runnable task) {
        this.execute0(task);
    }
    
    @Override
    public void lazyExecute(final Runnable task) {
        this.lazyExecute0(task);
    }
    
    private void execute0(@Async.Schedule final Runnable task) {
        ObjectUtil.checkNotNull(task, "task");
        this.execute(task, this.wakesUpForTask(task));
    }
    
    private void lazyExecute0(@Async.Schedule final Runnable task) {
        this.execute(ObjectUtil.checkNotNull(task, "task"), false);
    }
    
    @Override
    void scheduleRemoveScheduled(final ScheduledFutureTask<?> task) {
        ObjectUtil.checkNotNull(task, "task");
        final int currentState = this.state;
        if (this.supportSuspension && currentState == 3) {
            this.execute(new Runnable() {
                @Override
                public void run() {
                    task.run();
                    if (SingleThreadEventExecutor.this.canSuspend(3)) {
                        SingleThreadEventExecutor.this.trySuspend();
                    }
                }
            }, true);
        }
        else {
            this.execute(task, false);
        }
    }
    
    private void execute(final Runnable task, final boolean immediate) {
        final boolean inEventLoop = this.inEventLoop();
        this.addTask(task);
        if (!inEventLoop) {
            this.startThread();
            if (this.isShutdown()) {
                boolean reject = false;
                try {
                    if (this.removeTask(task)) {
                        reject = true;
                    }
                }
                catch (final UnsupportedOperationException ex) {}
                if (reject) {
                    reject();
                }
            }
        }
        if (!this.addTaskWakesUp && immediate) {
            this.wakeup(inEventLoop);
        }
    }
    
    @Override
    public <T> T invokeAny(final Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
        this.throwIfInEventLoop("invokeAny");
        return super.invokeAny(tasks);
    }
    
    @Override
    public <T> T invokeAny(final Collection<? extends Callable<T>> tasks, final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        this.throwIfInEventLoop("invokeAny");
        return super.invokeAny(tasks, timeout, unit);
    }
    
    @Override
    public <T> List<java.util.concurrent.Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks) throws InterruptedException {
        this.throwIfInEventLoop("invokeAll");
        return super.invokeAll(tasks);
    }
    
    @Override
    public <T> List<java.util.concurrent.Future<T>> invokeAll(final Collection<? extends Callable<T>> tasks, final long timeout, final TimeUnit unit) throws InterruptedException {
        this.throwIfInEventLoop("invokeAll");
        return super.invokeAll(tasks, timeout, unit);
    }
    
    private void throwIfInEventLoop(final String method) {
        if (this.inEventLoop()) {
            throw new RejectedExecutionException("Calling " + method + " from within the EventLoop is not allowed");
        }
    }
    
    public final ThreadProperties threadProperties() {
        ThreadProperties threadProperties = this.threadProperties;
        if (threadProperties == null) {
            Thread thread = this.thread;
            if (thread == null) {
                assert !this.inEventLoop();
                this.submit(SingleThreadEventExecutor.NOOP_TASK).syncUninterruptibly();
                thread = this.thread;
                assert thread != null;
            }
            threadProperties = new DefaultThreadProperties(thread);
            if (!SingleThreadEventExecutor.PROPERTIES_UPDATER.compareAndSet(this, null, threadProperties)) {
                threadProperties = this.threadProperties;
            }
        }
        return threadProperties;
    }
    
    protected boolean wakesUpForTask(final Runnable task) {
        return true;
    }
    
    protected static void reject() {
        throw new RejectedExecutionException("event executor terminated");
    }
    
    protected final void reject(final Runnable task) {
        this.rejectedExecutionHandler.rejected(task, this);
    }
    
    private void startThread() {
        final int currentState = this.state;
        if ((currentState == 1 || currentState == 3) && SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(this, currentState, 4)) {
            this.resetIdleCycles();
            this.resetBusyCycles();
            boolean success = false;
            try {
                this.doStartThread();
                success = true;
            }
            finally {
                if (!success) {
                    SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(this, 4, 1);
                }
            }
        }
    }
    
    private boolean ensureThreadStarted(final int oldState) {
        if (oldState != 1) {
            if (oldState != 3) {
                return false;
            }
        }
        try {
            this.doStartThread();
        }
        catch (final Throwable cause) {
            SingleThreadEventExecutor.STATE_UPDATER.set(this, 7);
            this.terminationFuture.tryFailure(cause);
            if (!(cause instanceof Exception)) {
                PlatformDependent.throwException(cause);
            }
            return true;
        }
        return false;
    }
    
    private void doStartThread() {
        this.executor.execute(new Runnable() {
            @Override
            public void run() {
                SingleThreadEventExecutor.this.processingLock.lock();
                assert SingleThreadEventExecutor.this.thread == null;
                SingleThreadEventExecutor.this.thread = Thread.currentThread();
                if (SingleThreadEventExecutor.this.interrupted) {
                    SingleThreadEventExecutor.this.thread.interrupt();
                    SingleThreadEventExecutor.this.interrupted = false;
                }
                boolean success = false;
                Throwable unexpectedException = null;
                SingleThreadEventExecutor.this.updateLastExecutionTime();
                boolean suspend = false;
                try {
                    while (true) {
                        SingleThreadEventExecutor.this.run();
                        success = true;
                        final int currentState = SingleThreadEventExecutor.this.state;
                        if (!SingleThreadEventExecutor.this.canSuspend(currentState)) {
                            break;
                        }
                        if (!SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, 2, 3)) {
                            continue;
                        }
                        if (!SingleThreadEventExecutor.this.canSuspend(3) && SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, 3, 4)) {
                            continue;
                        }
                        suspend = true;
                        break;
                    }
                }
                catch (final Throwable t) {
                    unexpectedException = t;
                    SingleThreadEventExecutor.logger.warn("Unexpected exception from an event executor: ", t);
                    final boolean shutdown = !suspend;
                    if (shutdown) {
                        int oldState;
                        do {
                            oldState = SingleThreadEventExecutor.this.state;
                        } while (oldState < 5 && !SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, oldState, 5));
                        if (success && SingleThreadEventExecutor.this.gracefulShutdownStartTime == 0L && SingleThreadEventExecutor.logger.isErrorEnabled()) {
                            SingleThreadEventExecutor.logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called before run() implementation terminates.");
                        }
                    }
                    try {
                        if (shutdown) {
                            while (!SingleThreadEventExecutor.this.confirmShutdown()) {}
                            int currentState2;
                            do {
                                currentState2 = SingleThreadEventExecutor.this.state;
                            } while (currentState2 < 6 && !SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, currentState2, 6));
                            SingleThreadEventExecutor.this.confirmShutdown();
                        }
                    }
                    finally {
                        try {
                            if (shutdown) {
                                try {
                                    SingleThreadEventExecutor.this.cleanup();
                                }
                                finally {
                                    FastThreadLocal.removeAll();
                                    SingleThreadEventExecutor.STATE_UPDATER.set(SingleThreadEventExecutor.this, 7);
                                    SingleThreadEventExecutor.this.threadLock.countDown();
                                    final int numUserTasks = SingleThreadEventExecutor.this.drainTasks();
                                    if (numUserTasks > 0 && SingleThreadEventExecutor.logger.isWarnEnabled()) {
                                        SingleThreadEventExecutor.logger.warn("An event executor terminated with non-empty task queue (" + numUserTasks + ')');
                                    }
                                    if (unexpectedException == null) {
                                        SingleThreadEventExecutor.this.terminationFuture.setSuccess(null);
                                    }
                                    else {
                                        SingleThreadEventExecutor.this.terminationFuture.setFailure(unexpectedException);
                                    }
                                }
                            }
                            else {
                                FastThreadLocal.removeAll();
                                SingleThreadEventExecutor.this.threadProperties = null;
                            }
                        }
                        finally {
                            SingleThreadEventExecutor.this.thread = null;
                            SingleThreadEventExecutor.this.processingLock.unlock();
                        }
                    }
                }
                finally {
                    final boolean shutdown2 = !suspend;
                    if (shutdown2) {
                        int oldState2;
                        do {
                            oldState2 = SingleThreadEventExecutor.this.state;
                        } while (oldState2 < 5 && !SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, oldState2, 5));
                        if (success && SingleThreadEventExecutor.this.gracefulShutdownStartTime == 0L && SingleThreadEventExecutor.logger.isErrorEnabled()) {
                            SingleThreadEventExecutor.logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called before run() implementation terminates.");
                        }
                    }
                    try {
                        if (shutdown2) {
                            while (!SingleThreadEventExecutor.this.confirmShutdown()) {}
                            int currentState3;
                            do {
                                currentState3 = SingleThreadEventExecutor.this.state;
                            } while (currentState3 < 6 && !SingleThreadEventExecutor.STATE_UPDATER.compareAndSet(SingleThreadEventExecutor.this, currentState3, 6));
                            SingleThreadEventExecutor.this.confirmShutdown();
                        }
                    }
                    finally {
                        try {
                            if (shutdown2) {
                                try {
                                    SingleThreadEventExecutor.this.cleanup();
                                }
                                finally {
                                    FastThreadLocal.removeAll();
                                    SingleThreadEventExecutor.STATE_UPDATER.set(SingleThreadEventExecutor.this, 7);
                                    SingleThreadEventExecutor.this.threadLock.countDown();
                                    final int numUserTasks2 = SingleThreadEventExecutor.this.drainTasks();
                                    if (numUserTasks2 > 0 && SingleThreadEventExecutor.logger.isWarnEnabled()) {
                                        SingleThreadEventExecutor.logger.warn("An event executor terminated with non-empty task queue (" + numUserTasks2 + ')');
                                    }
                                    if (unexpectedException == null) {
                                        SingleThreadEventExecutor.this.terminationFuture.setSuccess(null);
                                    }
                                    else {
                                        SingleThreadEventExecutor.this.terminationFuture.setFailure(unexpectedException);
                                    }
                                }
                            }
                            else {
                                FastThreadLocal.removeAll();
                                SingleThreadEventExecutor.this.threadProperties = null;
                            }
                        }
                        finally {
                            SingleThreadEventExecutor.this.thread = null;
                            SingleThreadEventExecutor.this.processingLock.unlock();
                        }
                    }
                }
            }
        });
    }
    
    final int drainTasks() {
        int numTasks = 0;
        while (true) {
            final Runnable runnable = this.taskQueue.poll();
            if (runnable == null) {
                break;
            }
            if (SingleThreadEventExecutor.WAKEUP_TASK == runnable) {
                continue;
            }
            ++numTasks;
        }
        return numTasks;
    }
    
    static {
        DEFAULT_MAX_PENDING_EXECUTOR_TASKS = Math.max(16, SystemPropertyUtil.getInt("io.netty.eventexecutor.maxPendingTasks", Integer.MAX_VALUE));
        logger = InternalLoggerFactory.getInstance(SingleThreadEventExecutor.class);
        NOOP_TASK = new Runnable() {
            @Override
            public void run() {
            }
        };
        STATE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "state");
        PROPERTIES_UPDATER = AtomicReferenceFieldUpdater.newUpdater(SingleThreadEventExecutor.class, ThreadProperties.class, "threadProperties");
        ACCUMULATED_ACTIVE_TIME_NANOS_UPDATER = AtomicLongFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "accumulatedActiveTimeNanos");
        CONSECUTIVE_IDLE_CYCLES_UPDATER = AtomicIntegerFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "consecutiveIdleCycles");
        CONSECUTIVE_BUSY_CYCLES_UPDATER = AtomicIntegerFieldUpdater.newUpdater(SingleThreadEventExecutor.class, "consecutiveBusyCycles");
        SCHEDULE_PURGE_INTERVAL = TimeUnit.SECONDS.toNanos(1L);
    }
    
    private static final class DefaultThreadProperties implements ThreadProperties
    {
        private final Thread t;
        
        DefaultThreadProperties(final Thread t) {
            this.t = t;
        }
        
        @Override
        public Thread.State state() {
            return this.t.getState();
        }
        
        @Override
        public int priority() {
            return this.t.getPriority();
        }
        
        @Override
        public boolean isInterrupted() {
            return this.t.isInterrupted();
        }
        
        @Override
        public boolean isDaemon() {
            return this.t.isDaemon();
        }
        
        @Override
        public String name() {
            return this.t.getName();
        }
        
        @Override
        public long id() {
            return this.t.getId();
        }
        
        @Override
        public StackTraceElement[] stackTrace() {
            return this.t.getStackTrace();
        }
        
        @Override
        public boolean isAlive() {
            return this.t.isAlive();
        }
    }
    
    @Deprecated
    protected interface NonWakeupRunnable extends LazyRunnable
    {
    }
}
