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

package io.netty.util.concurrent;

import java.util.Queue;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.jetbrains.annotations.Async;
import io.netty.util.internal.ObjectUtil;
import java.util.concurrent.TimeUnit;
import io.netty.util.internal.ThrowableUtil;
import io.netty.util.internal.ThreadExecutorMap;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.BlockingQueue;
import io.netty.util.internal.logging.InternalLogger;

public final class GlobalEventExecutor extends AbstractScheduledEventExecutor implements OrderedEventExecutor
{
    private static final InternalLogger logger;
    private static final long SCHEDULE_QUIET_PERIOD_INTERVAL;
    public static final GlobalEventExecutor INSTANCE;
    final BlockingQueue<Runnable> taskQueue;
    final ScheduledFutureTask<Void> quietPeriodTask;
    final ThreadFactory threadFactory;
    private final TaskRunner taskRunner;
    private final AtomicBoolean started;
    volatile Thread thread;
    private final Future<?> terminationFuture;
    
    private GlobalEventExecutor() {
        this.taskQueue = new LinkedBlockingQueue<Runnable>();
        this.quietPeriodTask = new ScheduledFutureTask<Void>(this, Executors.callable(new Runnable() {
            @Override
            public void run() {
            }
        }, (Void)null), AbstractScheduledEventExecutor.deadlineNanos(this.getCurrentTimeNanos(), GlobalEventExecutor.SCHEDULE_QUIET_PERIOD_INTERVAL), -GlobalEventExecutor.SCHEDULE_QUIET_PERIOD_INTERVAL);
        this.taskRunner = new TaskRunner();
        this.started = new AtomicBoolean();
        this.scheduleFromEventLoop(this.quietPeriodTask);
        this.threadFactory = ThreadExecutorMap.apply(new DefaultThreadFactory(DefaultThreadFactory.toPoolName(this.getClass()), false, 5, null), this);
        final UnsupportedOperationException terminationFailure = new UnsupportedOperationException();
        ThrowableUtil.unknownStackTrace(terminationFailure, GlobalEventExecutor.class, "terminationFuture");
        this.terminationFuture = new FailedFuture<Object>(this, terminationFailure);
    }
    
    Runnable takeTask() {
        final BlockingQueue<Runnable> taskQueue = this.taskQueue;
        while (true) {
            final ScheduledFutureTask<?> scheduledTask = this.peekScheduledTask();
            if (scheduledTask == null) {
                Runnable task = null;
                try {
                    task = taskQueue.take();
                }
                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) {
                return task2;
            }
        }
    }
    
    private void fetchFromScheduledTaskQueue() {
        final long nanoTime = this.getCurrentTimeNanos();
        ScheduledFutureTask scheduledTask;
        while ((scheduledTask = (ScheduledFutureTask)this.pollScheduledTask(nanoTime)) != null) {
            if (scheduledTask.isCancelled()) {
                continue;
            }
            this.taskQueue.add(scheduledTask);
        }
    }
    
    public int pendingTasks() {
        return this.taskQueue.size();
    }
    
    private void addTask(final Runnable task) {
        this.taskQueue.add(ObjectUtil.checkNotNull(task, "task"));
    }
    
    @Override
    public boolean inEventLoop(final Thread thread) {
        return thread == this.thread;
    }
    
    @Override
    public Future<?> shutdownGracefully(final long quietPeriod, final long timeout, final TimeUnit unit) {
        return this.terminationFuture();
    }
    
    @Override
    public Future<?> terminationFuture() {
        return this.terminationFuture;
    }
    
    @Deprecated
    @Override
    public void shutdown() {
        throw new UnsupportedOperationException();
    }
    
    @Override
    public boolean isShuttingDown() {
        return false;
    }
    
    @Override
    public boolean isShutdown() {
        return false;
    }
    
    @Override
    public boolean isTerminated() {
        return false;
    }
    
    @Override
    public boolean awaitTermination(final long timeout, final TimeUnit unit) {
        return false;
    }
    
    public boolean awaitInactivity(final long timeout, final TimeUnit unit) throws InterruptedException {
        ObjectUtil.checkNotNull(unit, "unit");
        final Thread thread = this.thread;
        if (thread == null) {
            throw new IllegalStateException("thread was not started");
        }
        thread.join(unit.toMillis(timeout));
        return !thread.isAlive();
    }
    
    @Override
    public void execute(final Runnable task) {
        this.execute0(task);
    }
    
    private void execute0(@Async.Schedule final Runnable task) {
        this.addTask(ObjectUtil.checkNotNull(task, "task"));
        if (!this.inEventLoop()) {
            this.startThread();
        }
    }
    
    private void startThread() {
        if (this.started.compareAndSet(false, true)) {
            final Thread callingThread = Thread.currentThread();
            final ClassLoader parentCCL = AccessController.doPrivileged((PrivilegedAction<ClassLoader>)new PrivilegedAction<ClassLoader>() {
                @Override
                public ClassLoader run() {
                    return callingThread.getContextClassLoader();
                }
            });
            setContextClassLoader(callingThread, null);
            try {
                final Thread t = this.threadFactory.newThread(this.taskRunner);
                setContextClassLoader(t, null);
                (this.thread = t).start();
            }
            finally {
                setContextClassLoader(callingThread, parentCCL);
            }
        }
    }
    
    private static void setContextClassLoader(final Thread t, final ClassLoader cl) {
        AccessController.doPrivileged((PrivilegedAction<Object>)new PrivilegedAction<Void>() {
            @Override
            public Void run() {
                t.setContextClassLoader(cl);
                return null;
            }
        });
    }
    
    static {
        logger = InternalLoggerFactory.getInstance(GlobalEventExecutor.class);
        int quietPeriod = SystemPropertyUtil.getInt("io.netty.globalEventExecutor.quietPeriodSeconds", 1);
        if (quietPeriod <= 0) {
            quietPeriod = 1;
        }
        GlobalEventExecutor.logger.debug("-Dio.netty.globalEventExecutor.quietPeriodSeconds: {}", (Object)quietPeriod);
        SCHEDULE_QUIET_PERIOD_INTERVAL = TimeUnit.SECONDS.toNanos(quietPeriod);
        INSTANCE = new GlobalEventExecutor();
    }
    
    final class TaskRunner implements Runnable
    {
        @Override
        public void run() {
            while (true) {
                final Runnable task = GlobalEventExecutor.this.takeTask();
                if (task != null) {
                    try {
                        AbstractEventExecutor.runTask(task);
                    }
                    catch (final Throwable t) {
                        GlobalEventExecutor.logger.warn("Unexpected exception from the global event executor: ", t);
                    }
                    if (task != GlobalEventExecutor.this.quietPeriodTask) {
                        continue;
                    }
                }
                final Queue<ScheduledFutureTask<?>> scheduledTaskQueue = GlobalEventExecutor.this.scheduledTaskQueue;
                if (GlobalEventExecutor.this.taskQueue.isEmpty() && (scheduledTaskQueue == null || scheduledTaskQueue.size() == 1)) {
                    final boolean stopped = GlobalEventExecutor.this.started.compareAndSet(true, false);
                    assert stopped;
                    if (GlobalEventExecutor.this.taskQueue.isEmpty()) {
                        break;
                    }
                    if (!GlobalEventExecutor.this.started.compareAndSet(false, true)) {
                        break;
                    }
                    continue;
                }
            }
        }
    }
}
