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

package io.sentry.transport;

import java.util.concurrent.CancellationException;
import io.sentry.DateUtils;
import java.util.concurrent.RejectedExecutionException;
import io.sentry.SentryLevel;
import java.util.concurrent.Future;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import io.sentry.SentryDateProvider;
import org.jetbrains.annotations.NotNull;
import io.sentry.ILogger;
import org.jetbrains.annotations.Nullable;
import io.sentry.SentryDate;
import java.util.concurrent.ThreadPoolExecutor;

final class QueuedThreadPoolExecutor extends ThreadPoolExecutor
{
    private final int maxQueueSize;
    @Nullable
    private SentryDate lastRejectTimestamp;
    @NotNull
    private final ILogger logger;
    @NotNull
    private final SentryDateProvider dateProvider;
    @NotNull
    private final ReusableCountLatch unfinishedTasksCount;
    private static final long RECENT_THRESHOLD;
    
    public QueuedThreadPoolExecutor(final int corePoolSize, final int maxQueueSize, @NotNull final ThreadFactory threadFactory, @NotNull final RejectedExecutionHandler rejectedExecutionHandler, @NotNull final ILogger logger, @NotNull final SentryDateProvider dateProvider) {
        super(corePoolSize, corePoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory, rejectedExecutionHandler);
        this.lastRejectTimestamp = null;
        this.unfinishedTasksCount = new ReusableCountLatch();
        this.maxQueueSize = maxQueueSize;
        this.logger = logger;
        this.dateProvider = dateProvider;
    }
    
    @Override
    public Future<?> submit(@NotNull final Runnable task) {
        if (this.isSchedulingAllowed()) {
            this.unfinishedTasksCount.increment();
            try {
                return super.submit(task);
            }
            catch (final RejectedExecutionException e) {
                this.unfinishedTasksCount.decrement();
                this.lastRejectTimestamp = this.dateProvider.now();
                this.logger.log(SentryLevel.WARNING, "Submit rejected by thread pool executor", e);
                return new CancelledFuture<Object>();
            }
        }
        this.lastRejectTimestamp = this.dateProvider.now();
        this.logger.log(SentryLevel.WARNING, "Submit cancelled", new Object[0]);
        return new CancelledFuture<Object>();
    }
    
    @Override
    protected void afterExecute(@NotNull final Runnable r, @Nullable final Throwable t) {
        try {
            super.afterExecute(r, t);
        }
        finally {
            this.unfinishedTasksCount.decrement();
        }
    }
    
    void waitTillIdle(final long timeoutMillis) {
        try {
            this.unfinishedTasksCount.waitTillZero(timeoutMillis, TimeUnit.MILLISECONDS);
        }
        catch (final InterruptedException e) {
            this.logger.log(SentryLevel.ERROR, "Failed to wait till idle", e);
            Thread.currentThread().interrupt();
        }
    }
    
    public boolean isSchedulingAllowed() {
        return this.unfinishedTasksCount.getCount() < this.maxQueueSize;
    }
    
    public boolean didRejectRecently() {
        final SentryDate lastReject = this.lastRejectTimestamp;
        if (lastReject == null) {
            return false;
        }
        final long diff = this.dateProvider.now().diff(lastReject);
        return diff < QueuedThreadPoolExecutor.RECENT_THRESHOLD;
    }
    
    static {
        RECENT_THRESHOLD = DateUtils.millisToNanos(2000L);
    }
    
    static final class CancelledFuture<T> implements Future<T>
    {
        @Override
        public boolean cancel(final boolean mayInterruptIfRunning) {
            return true;
        }
        
        @Override
        public boolean isCancelled() {
            return true;
        }
        
        @Override
        public boolean isDone() {
            return true;
        }
        
        @Override
        public T get() {
            throw new CancellationException();
        }
        
        @Override
        public T get(final long timeout, @NotNull final TimeUnit unit) {
            throw new CancellationException();
        }
    }
}
