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

package io.netty.util;

import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.concurrent.atomic.LongAdder;
import java.io.Closeable;
import java.util.concurrent.atomic.AtomicBoolean;
import io.netty.util.internal.SystemPropertyUtil;
import java.util.function.Supplier;

public class LeakPresenceDetector<T> extends ResourceLeakDetector<T>
{
    private static final String TRACK_CREATION_STACK_PROPERTY = "io.netty.util.LeakPresenceDetector.trackCreationStack";
    private static final boolean TRACK_CREATION_STACK;
    private static final ResourceScope GLOBAL;
    private static int staticInitializerCount;
    
    private static boolean inStaticInitializerSlow(final StackTraceElement[] stackTrace) {
        for (final StackTraceElement element : stackTrace) {
            if (element.getMethodName().equals("<clinit>")) {
                return true;
            }
        }
        return false;
    }
    
    private static boolean inStaticInitializerFast() {
        return LeakPresenceDetector.staticInitializerCount != 0 && inStaticInitializerSlow(Thread.currentThread().getStackTrace());
    }
    
    public static <R> R staticInitializer(final Supplier<R> supplier) {
        if (!inStaticInitializerSlow(Thread.currentThread().getStackTrace())) {
            throw new IllegalStateException("Not in static initializer.");
        }
        synchronized (LeakPresenceDetector.class) {
            ++LeakPresenceDetector.staticInitializerCount;
        }
        try {
            return supplier.get();
        }
        finally {
            synchronized (LeakPresenceDetector.class) {
                --LeakPresenceDetector.staticInitializerCount;
            }
        }
    }
    
    public LeakPresenceDetector(final Class<?> resourceType) {
        super(resourceType, 0);
    }
    
    @Deprecated
    public LeakPresenceDetector(final Class<?> resourceType, final int samplingInterval) {
        this(resourceType);
    }
    
    public LeakPresenceDetector(final Class<?> resourceType, final int samplingInterval, final long maxActive) {
        this(resourceType);
    }
    
    protected ResourceScope currentScope() throws AllocationProhibitedException {
        return LeakPresenceDetector.GLOBAL;
    }
    
    @Override
    public final ResourceLeakTracker<T> track(final T obj) {
        if (inStaticInitializerFast()) {
            return null;
        }
        return this.trackForcibly(obj);
    }
    
    @Override
    public final ResourceLeakTracker<T> trackForcibly(final T obj) {
        return new PresenceTracker<T>(this.currentScope());
    }
    
    @Override
    public final boolean isRecordEnabled() {
        return false;
    }
    
    public static void check() {
        final ResourceLeakDetector<Object> detector = ResourceLeakDetectorFactory.instance().newResourceLeakDetector(Object.class);
        if (!(detector instanceof LeakPresenceDetector)) {
            throw new IllegalStateException("LeakPresenceDetector not in use. Please register it using -Dio.netty.customResourceLeakDetector=" + LeakPresenceDetector.class.getName());
        }
        ((LeakPresenceDetector)detector).currentScope().check();
    }
    
    static {
        TRACK_CREATION_STACK = SystemPropertyUtil.getBoolean("io.netty.util.LeakPresenceDetector.trackCreationStack", false);
        GLOBAL = new ResourceScope("global");
    }
    
    private static final class PresenceTracker<T> extends AtomicBoolean implements ResourceLeakTracker<T>
    {
        private final ResourceScope scope;
        
        PresenceTracker(final ResourceScope scope) {
            super(false);
            (this.scope = scope).checkOpen();
            scope.openResourceCounter.increment();
            if (LeakPresenceDetector.TRACK_CREATION_STACK) {
                scope.creationStacks.put(this, new LeakCreation());
            }
        }
        
        @Override
        public void record() {
        }
        
        @Override
        public void record(final Object hint) {
        }
        
        @Override
        public boolean close(final Object trackedObject) {
            if (this.compareAndSet(false, true)) {
                this.scope.openResourceCounter.decrement();
                if (LeakPresenceDetector.TRACK_CREATION_STACK) {
                    this.scope.creationStacks.remove(this);
                }
                this.scope.checkOpen();
                return true;
            }
            return false;
        }
    }
    
    public static final class ResourceScope implements Closeable
    {
        final String name;
        final LongAdder openResourceCounter;
        final Map<PresenceTracker<?>, Throwable> creationStacks;
        boolean closed;
        
        public ResourceScope(final String name) {
            this.openResourceCounter = new LongAdder();
            this.creationStacks = (LeakPresenceDetector.TRACK_CREATION_STACK ? new ConcurrentHashMap<PresenceTracker<?>, Throwable>() : null);
            this.name = name;
        }
        
        void checkOpen() {
            if (this.closed) {
                throw new AllocationProhibitedException("Resource scope '" + this.name + "' already closed");
            }
        }
        
        void check() {
            final long n = this.openResourceCounter.sumThenReset();
            if (n == 0L) {
                return;
            }
            final StringBuilder msg = new StringBuilder("Possible memory leak detected for resource scope '").append(this.name).append("'. ");
            if (n < 0L) {
                msg.append("Resource count was negative: A resource previously reported as a leak was released after all. Please ensure that that resource is released before its test finishes.");
                throw new IllegalStateException(msg.toString());
            }
            if (LeakPresenceDetector.TRACK_CREATION_STACK) {
                msg.append("Creation stack traces:");
                final IllegalStateException ise = new IllegalStateException(msg.toString());
                int i = 0;
                for (final Throwable t : this.creationStacks.values()) {
                    ise.addSuppressed(t);
                    if (i++ > 5) {
                        break;
                    }
                }
                this.creationStacks.clear();
                throw ise;
            }
            msg.append("Please use paranoid leak detection to get more information, or set -Dio.netty.util.LeakPresenceDetector.trackCreationStack=true");
            throw new IllegalStateException(msg.toString());
        }
        
        public boolean hasOpenResources() {
            return this.openResourceCounter.sum() > 0L;
        }
        
        @Override
        public void close() {
            this.closed = true;
            this.check();
        }
    }
    
    private static final class LeakCreation extends Throwable
    {
        final Thread thread;
        String message;
        
        private LeakCreation() {
            this.thread = Thread.currentThread();
        }
        
        @Override
        public synchronized String getMessage() {
            if (this.message == null) {
                if (inStaticInitializerSlow(this.getStackTrace())) {
                    this.message = "Resource created in static initializer. Please wrap the static initializer in LeakPresenceDetector.staticInitializer so that this resource is excluded.";
                }
                else {
                    this.message = "Resource created outside static initializer on thread '" + this.thread.getName() + "' (" + this.thread.getState() + "), likely leak.";
                }
            }
            return this.message;
        }
    }
    
    public static final class AllocationProhibitedException extends IllegalStateException
    {
        public AllocationProhibitedException(final String s) {
            super(s);
        }
    }
}
