001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 *  Copyright (C) 1999-2025, QOS.ch. All rights reserved.
004 *
005 * This program and the accompanying materials are dual-licensed under
006 * either the terms of the Eclipse Public License v1.0 as published by
007 * the Eclipse Foundation
008 *
009 *     or (per the licensee's choosing)
010 *
011 * under the terms of the GNU Lesser General Public License version 2.1
012 * as published by the Free Software Foundation.
013 */
014
015package ch.qos.logback.core.util;
016
017/**
018 * Guard used to prevent re-entrant (recursive) appender invocations on a per-thread basis.
019 *
020 * <p>Implementations are used by appenders and other components that must avoid
021 * recursively calling back into themselves (for example when an error causes
022 * logging while handling a logging event). Typical usage: check {@link #isLocked()}
023 * before proceeding and call {@link #lock()} / {@link #unlock()} around the
024 * guarded region.</p>
025 *
026 * <p>Concurrency: guards operate on a per-thread basis; callers should treat the
027 * guard as thread-local state. Implementations must document their semantics;
028 * the provided {@link ReentryGuardImpl} uses a {@link ThreadLocal} to track the
029 * locked state for the current thread.</p>
030*
031 * @since 1.5.21
032 */
033public interface ReentryGuard {
034
035    /**
036     * Return true if the current thread holds the guard (i.e. is inside a guarded region).
037     *
038     * <p>Implementations typically return {@code false} if the current thread has not
039     * previously called {@link #lock()} or if the stored value is {@code null}.</p>
040     *
041     * @return {@code true} if the guard is locked for the current thread, {@code false} otherwise
042     */
043    boolean isLocked();
044
045    /**
046     * Mark the guard as locked for the current thread.
047     *
048     * <p>Callers must ensure {@link #unlock()} is invoked in a finally block to
049     * avoid leaving the guard permanently locked for the thread.</p>
050     */
051    void lock();
052
053    /**
054     * Release the guard for the current thread.
055     *
056     * <p>After calling {@code unlock()} the {@link #isLocked()} should return
057     * {@code false} for the current thread (unless {@code lock()} is called again).</p>
058     */
059    void unlock();
060
061
062    /**
063     * Default per-thread implementation backed by a {@link ThreadLocal<Boolean>}.
064     *
065     * <p>Semantics: a value of {@link Boolean#TRUE} indicates the current thread
066     * is inside a guarded region. If the ThreadLocal has no value ({@code null}),
067     * {@link #isLocked()} treats this as unlocked (returns {@code false}).</p>
068     *
069     * <p>Note: this implementation intentionally uses {@code ThreadLocal<Boolean>}
070     * to avoid global synchronization. The initial state is unlocked.</p>
071     *
072     * Typical usage:
073     * <pre>
074     * if (!guard.isLocked()) {
075     *   guard.lock();
076     *   try {
077     *     // guarded work
078     *   } finally {
079     *     guard.unlock();
080     *   }
081     * }
082     * </pre>
083     *
084     */
085    class ReentryGuardImpl implements ReentryGuard {
086
087        private ThreadLocal<Boolean> guard = new ThreadLocal<Boolean>();
088
089
090        @Override
091        public boolean isLocked() {
092            // the guard is considered locked if the ThreadLocal contains Boolean.TRUE
093            // note that initially the ThreadLocal contains null
094            return (Boolean.TRUE.equals(guard.get()));
095        }
096
097        @Override
098        public void lock() {
099            guard.set(Boolean.TRUE);
100        }
101
102        @Override
103        public void unlock() {
104            guard.set(Boolean.FALSE);
105        }
106    }
107
108    /**
109     * No-op implementation that never locks. Useful in contexts where re-entrancy
110     * protection is not required.
111     *
112     * <p>{@link #isLocked()} always returns {@code false}. {@link #lock()} and
113     * {@link #unlock()} are no-ops.</p>
114     *
115     * <p>Use this implementation when the caller explicitly wants to disable
116     * reentrancy protection (for example in tests or in environments where the
117     * cost of thread-local checks is undesirable and re-entrancy cannot occur).</p>
118     *
119     */
120    class NOPRentryGuard implements ReentryGuard {
121        @Override
122        public boolean isLocked() {
123            return false;
124        }
125
126        @Override
127        public void lock() {
128            // NOP
129        }
130
131        @Override
132        public void unlock() {
133            // NOP
134        }
135    }
136
137}