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

package io.netty.util.concurrent;

import io.netty.util.internal.LongLongHashMap;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.util.internal.InternalThreadLocalMap;
import java.util.concurrent.atomic.AtomicReference;
import io.netty.util.internal.logging.InternalLogger;

public class FastThreadLocalThread extends Thread
{
    private static final InternalLogger logger;
    private static final AtomicReference<FallbackThreadSet> fallbackThreads;
    private final boolean cleanupFastThreadLocals;
    private InternalThreadLocalMap threadLocalMap;
    
    public FastThreadLocalThread() {
        this.cleanupFastThreadLocals = false;
    }
    
    public FastThreadLocalThread(final Runnable target) {
        super(FastThreadLocalRunnable.wrap(target));
        this.cleanupFastThreadLocals = true;
    }
    
    public FastThreadLocalThread(final ThreadGroup group, final Runnable target) {
        super(group, FastThreadLocalRunnable.wrap(target));
        this.cleanupFastThreadLocals = true;
    }
    
    public FastThreadLocalThread(final String name) {
        super(name);
        this.cleanupFastThreadLocals = false;
    }
    
    public FastThreadLocalThread(final ThreadGroup group, final String name) {
        super(group, name);
        this.cleanupFastThreadLocals = false;
    }
    
    public FastThreadLocalThread(final Runnable target, final String name) {
        super(FastThreadLocalRunnable.wrap(target), name);
        this.cleanupFastThreadLocals = true;
    }
    
    public FastThreadLocalThread(final ThreadGroup group, final Runnable target, final String name) {
        super(group, FastThreadLocalRunnable.wrap(target), name);
        this.cleanupFastThreadLocals = true;
    }
    
    public FastThreadLocalThread(final ThreadGroup group, final Runnable target, final String name, final long stackSize) {
        super(group, FastThreadLocalRunnable.wrap(target), name, stackSize);
        this.cleanupFastThreadLocals = true;
    }
    
    public final InternalThreadLocalMap threadLocalMap() {
        if (this != Thread.currentThread() && FastThreadLocalThread.logger.isWarnEnabled()) {
            FastThreadLocalThread.logger.warn(new RuntimeException("It's not thread-safe to get 'threadLocalMap' which doesn't belong to the caller thread"));
        }
        return this.threadLocalMap;
    }
    
    public final void setThreadLocalMap(final InternalThreadLocalMap threadLocalMap) {
        if (this != Thread.currentThread() && FastThreadLocalThread.logger.isWarnEnabled()) {
            FastThreadLocalThread.logger.warn(new RuntimeException("It's not thread-safe to set 'threadLocalMap' which doesn't belong to the caller thread"));
        }
        this.threadLocalMap = threadLocalMap;
    }
    
    @Deprecated
    public boolean willCleanupFastThreadLocals() {
        return this.cleanupFastThreadLocals;
    }
    
    @Deprecated
    public static boolean willCleanupFastThreadLocals(final Thread thread) {
        return thread instanceof FastThreadLocalThread && ((FastThreadLocalThread)thread).willCleanupFastThreadLocals();
    }
    
    public static boolean currentThreadWillCleanupFastThreadLocals() {
        final Thread currentThread = Thread.currentThread();
        if (currentThread instanceof FastThreadLocalThread) {
            return ((FastThreadLocalThread)currentThread).willCleanupFastThreadLocals();
        }
        return isFastThreadLocalVirtualThread();
    }
    
    public static boolean currentThreadHasFastThreadLocal() {
        return currentThread() instanceof FastThreadLocalThread || isFastThreadLocalVirtualThread();
    }
    
    private static boolean isFastThreadLocalVirtualThread() {
        return FastThreadLocalThread.fallbackThreads.get().contains(Thread.currentThread().getId());
    }
    
    public static void runWithFastThreadLocal(final Runnable runnable) {
        final Thread current = Thread.currentThread();
        if (current instanceof FastThreadLocalThread) {
            throw new IllegalStateException("Caller is a real FastThreadLocalThread");
        }
        final long id = current.getId();
        FastThreadLocalThread.fallbackThreads.updateAndGet(set -> {
            if (set.contains(id)) {
                throw new IllegalStateException("Reentrant call to run()");
            }
            else {
                return set.add(id);
            }
        });
        try {
            runnable.run();
        }
        finally {
            FastThreadLocalThread.fallbackThreads.getAndUpdate(set -> set.remove(id));
            FastThreadLocal.removeAll();
        }
    }
    
    public boolean permitBlockingCalls() {
        return false;
    }
    
    static {
        logger = InternalLoggerFactory.getInstance(FastThreadLocalThread.class);
        fallbackThreads = new AtomicReference<FallbackThreadSet>(FallbackThreadSet.EMPTY);
    }
    
    private static final class FallbackThreadSet
    {
        static final FallbackThreadSet EMPTY;
        private static final long EMPTY_VALUE = 0L;
        private final LongLongHashMap map;
        
        private FallbackThreadSet() {
            this.map = new LongLongHashMap(0L);
        }
        
        private FallbackThreadSet(final LongLongHashMap map) {
            this.map = map;
        }
        
        public boolean contains(final long threadId) {
            final long key = threadId >>> 6;
            final long bit = 1L << (int)(threadId & 0x3FL);
            final long bitmap = this.map.get(key);
            return (bitmap & bit) != 0x0L;
        }
        
        public FallbackThreadSet add(final long threadId) {
            final long key = threadId >>> 6;
            final long bit = 1L << (int)(threadId & 0x3FL);
            final LongLongHashMap newMap = new LongLongHashMap(this.map);
            final long oldBitmap = newMap.get(key);
            final long newBitmap = oldBitmap | bit;
            newMap.put(key, newBitmap);
            return new FallbackThreadSet(newMap);
        }
        
        public FallbackThreadSet remove(final long threadId) {
            final long key = threadId >>> 6;
            final long bit = 1L << (int)(threadId & 0x3FL);
            final long oldBitmap = this.map.get(key);
            if ((oldBitmap & bit) == 0x0L) {
                return this;
            }
            final LongLongHashMap newMap = new LongLongHashMap(this.map);
            final long newBitmap = oldBitmap & ~bit;
            if (newBitmap != 0L) {
                newMap.put(key, newBitmap);
            }
            else {
                newMap.remove(key);
            }
            return new FallbackThreadSet(newMap);
        }
        
        static {
            EMPTY = new FallbackThreadSet();
        }
    }
}
