Repository: netty/netty
File: common/src/main/java/io/netty/util/concurrent/FastThreadLocalThread.java (Java)

Code before:
     * Returns {@code true} if this thread supports {@link FastThreadLocal}.
     */
    public static boolean currentThreadHasFastThreadLocal() {
        // intentionally doesn't accept a thread parameter to work with ScopedValue in the future
        return currentThread() instanceof FastThreadLocalThread || isFastThreadLocalVirtualThread();
    }

    private static boolean isFastThreadLocalVirtualThread() {
        return fallbackThreads.containsKey(currentThread().getId());
    }

    /**
     * Run the given task with {@link FastThreadLocal} support. This call should wrap the runnable for any thread that
     * is long-running enough to make treating it as a {@link FastThreadLocalThread} reasonable, but that can't
     * actually extend this class (e.g. because it's a virtual thread). Netty will use optimizations for recyclers and
     * allocators as if this was a {@link FastThreadLocalThread}.
     * <p>This method will clean up any {@link FastThreadLocal}s at the end, and
     * {@link #currentThreadWillCleanupFastThreadLocals()} will return {@code true}.
     * <p>At the moment, {@link FastThreadLocal} uses normal {@link ThreadLocal} as the backing storage here, but in
     * the future this may be replaced with scoped values, if semantics can be preserved and performance is good.
     *
     * @param runnable The task to run
     */
    public static void runWithFastThreadLocal(Runnable runnable) {
        long id = currentThread().getId();
        if (fallbackThreads.put(id, "") != null || currentThread() instanceof FastThreadLocalThread) {
            throw new IllegalStateException("Reentrant call to run()");
        }
        try {
            runnable.run();
        } finally {
            fallbackThreads.remove(id);
            FastThreadLocal.removeAll();
        }
    }

    /**
     * Query whether this thread is allowed to perform blocking calls or not.
     * {@link FastThreadLocalThread}s are often used in event-loops, where blocking calls are forbidden in order to
     * prevent event-loop stalls, so this method returns {@code false} by default.
     * <p>
     * Subclasses of {@link FastThreadLocalThread} can override this method if they are not meant to be used for
     * running event-loops.
     *
     * @return {@code false}, unless overriden by a subclass.
     */
    public boolean permitBlockingCalls() {
        return false;
    }
}


Review comment (suggestion):
you will need to flip this as otherwise you would leak if the currentThread is and instance of FastThreadLocalThread (as the put will have added something to the map and you will not remove it anymore).



Code after:
        return isFastThreadLocalVirtualThread();
    }

    /**
     * Returns {@code true} if this thread supports {@link FastThreadLocal}.
     */
    public static boolean currentThreadHasFastThreadLocal() {
        // intentionally doesn't accept a thread parameter to work with ScopedValue in the future
        return currentThread() instanceof FastThreadLocalThread || isFastThreadLocalVirtualThread();
    }

    private static boolean isFastThreadLocalVirtualThread() {
        long[] arr = fallbackThreads.get();
        if (arr == null) {
            return false;
        }
        return Arrays.binarySearch(arr, Thread.currentThread().getId()) >= 0;
    }

    /**
     * Run the given task with {@link FastThreadLocal} support. This call should wrap the runnable for any thread that
     * is long-running enough to make treating it as a {@link FastThreadLocalThread} reasonable, but that can't
     * actually extend this class (e.g. because it's a virtual thread). Netty will use optimizations for recyclers and
     * allocators as if this was a {@link FastThreadLocalThread}.
     * <p>This method will clean up any {@link FastThreadLocal}s at the end, and
     * {@link #currentThreadWillCleanupFastThreadLocals()} will return {@code true}.
     * <p>At the moment, {@link FastThreadLocal} uses normal {@link ThreadLocal} as the backing storage here, but in
     * the future this may be replaced with scoped values, if semantics can be preserved and performance is good.
     *
     * @param runnable The task to run
     */
    public static void runWithFastThreadLocal(Runnable runnable) {
        Thread current = currentThread();
        if (current instanceof FastThreadLocalThread) {
            throw new IllegalStateException("Caller is a real FastThreadLocalThread");
        }
        long id = current.getId();
        fallbackThreads.updateAndGet(arr -> {
            if (arr == null) {
                return new long[] { id };
            }
            int index = Arrays.binarySearch(arr, id);
            if (index >= 0) {
                throw new IllegalStateException("Reentrant call to run()");
            }
            index = ~index; // same as -(index + 1)
            long[] next = new long[arr.length + 1];
            System.arraycopy(arr, 0, next, 0, index);
            next[index] = id;
            System.arraycopy(arr, index, next, index + 1, arr.length - index);
            return next;