ReentryGuard.java

/*
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v2.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */

package ch.qos.logback.core.util;

/**
 * Guard used to prevent re-entrant (recursive) appender invocations on a per-thread basis.
 *
 * <p>Implementations are used by appenders and other components that must avoid
 * recursively calling back into themselves (for example when an error causes
 * logging while handling a logging event). Typical usage: check {@link #isLocked()}
 * before proceeding and call {@link #lock()} / {@link #unlock()} around the
 * guarded region.</p>
 *
 * <p>Concurrency: guards operate on a per-thread basis; callers should treat the
 * guard as thread-local state. Implementations must document their semantics;
 * the provided {@link ReentryGuardImpl} uses a {@link ThreadLocal} to track the
 * locked state for the current thread.</p>
*
 * @since 1.5.21
 */
public interface ReentryGuard {

    /**
     * Return true if the current thread holds the guard (i.e. is inside a guarded region).
     *
     * <p>Implementations typically return {@code false} if the current thread has not
     * previously called {@link #lock()} or if the stored value is {@code null}.</p>
     *
     * @return {@code true} if the guard is locked for the current thread, {@code false} otherwise
     */
    boolean isLocked();

    /**
     * Mark the guard as locked for the current thread.
     *
     * <p>Callers must ensure {@link #unlock()} is invoked in a finally block to
     * avoid leaving the guard permanently locked for the thread.</p>
     */
    void lock();

    /**
     * Release the guard for the current thread.
     *
     * <p>After calling {@code unlock()} the {@link #isLocked()} should return
     * {@code false} for the current thread (unless {@code lock()} is called again).</p>
     */
    void unlock();


    /**
     * Default per-thread implementation backed by a {@link ThreadLocal<Boolean>}.
     *
     * <p>Semantics: a value of {@link Boolean#TRUE} indicates the current thread
     * is inside a guarded region. If the ThreadLocal has no value ({@code null}),
     * {@link #isLocked()} treats this as unlocked (returns {@code false}).</p>
     *
     * <p>Note: this implementation intentionally uses {@code ThreadLocal<Boolean>}
     * to avoid global synchronization. The initial state is unlocked.</p>
     *
     * Typical usage:
     * <pre>
     * if (!guard.isLocked()) {
     *   guard.lock();
     *   try {
     *     // guarded work
     *   } finally {
     *     guard.unlock();
     *   }
     * }
     * </pre>
     *
     */
    class ReentryGuardImpl implements ReentryGuard {

        private ThreadLocal<Boolean> guard = new ThreadLocal<Boolean>();


        @Override
        public boolean isLocked() {
            // the guard is considered locked if the ThreadLocal contains Boolean.TRUE
            // note that initially the ThreadLocal contains null
            return (Boolean.TRUE.equals(guard.get()));
        }

        @Override
        public void lock() {
            guard.set(Boolean.TRUE);
        }

        @Override
        public void unlock() {
            guard.set(Boolean.FALSE);
        }
    }

    /**
     * No-op implementation that never locks. Useful in contexts where re-entrancy
     * protection is not required.
     *
     * <p>{@link #isLocked()} always returns {@code false}. {@link #lock()} and
     * {@link #unlock()} are no-ops.</p>
     *
     * <p>Use this implementation when the caller explicitly wants to disable
     * reentrancy protection (for example in tests or in environments where the
     * cost of thread-local checks is undesirable and re-entrancy cannot occur).</p>
     *
     */
    class NOPRentryGuard implements ReentryGuard {
        @Override
        public boolean isLocked() {
            return false;
        }

        @Override
        public void lock() {
            // NOP
        }

        @Override
        public void unlock() {
            // NOP
        }
    }

}