1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    *  Copyright (C) 1999-2025, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *     or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.core.util;
16  
17  /**
18   * Guard used to prevent re-entrant (recursive) appender invocations on a per-thread basis.
19   *
20   * <p>Implementations are used by appenders and other components that must avoid
21   * recursively calling back into themselves (for example when an error causes
22   * logging while handling a logging event). Typical usage: check {@link #isLocked()}
23   * before proceeding and call {@link #lock()} / {@link #unlock()} around the
24   * guarded region.</p>
25   *
26   * <p>Concurrency: guards operate on a per-thread basis; callers should treat the
27   * guard as thread-local state. Implementations must document their semantics;
28   * the provided {@link ReentryGuardImpl} uses a {@link ThreadLocal} to track the
29   * locked state for the current thread.</p>
30  *
31   * @since 1.5.21
32   */
33  public interface ReentryGuard {
34  
35      /**
36       * Return true if the current thread holds the guard (i.e. is inside a guarded region).
37       *
38       * <p>Implementations typically return {@code false} if the current thread has not
39       * previously called {@link #lock()} or if the stored value is {@code null}.</p>
40       *
41       * @return {@code true} if the guard is locked for the current thread, {@code false} otherwise
42       */
43      boolean isLocked();
44  
45      /**
46       * Mark the guard as locked for the current thread.
47       *
48       * <p>Callers must ensure {@link #unlock()} is invoked in a finally block to
49       * avoid leaving the guard permanently locked for the thread.</p>
50       */
51      void lock();
52  
53      /**
54       * Release the guard for the current thread.
55       *
56       * <p>After calling {@code unlock()} the {@link #isLocked()} should return
57       * {@code false} for the current thread (unless {@code lock()} is called again).</p>
58       */
59      void unlock();
60  
61  
62      /**
63       * Default per-thread implementation backed by a {@link ThreadLocal<Boolean>}.
64       *
65       * <p>Semantics: a value of {@link Boolean#TRUE} indicates the current thread
66       * is inside a guarded region. If the ThreadLocal has no value ({@code null}),
67       * {@link #isLocked()} treats this as unlocked (returns {@code false}).</p>
68       *
69       * <p>Note: this implementation intentionally uses {@code ThreadLocal<Boolean>}
70       * to avoid global synchronization. The initial state is unlocked.</p>
71       *
72       * Typical usage:
73       * <pre>
74       * if (!guard.isLocked()) {
75       *   guard.lock();
76       *   try {
77       *     // guarded work
78       *   } finally {
79       *     guard.unlock();
80       *   }
81       * }
82       * </pre>
83       *
84       */
85      class ReentryGuardImpl implements ReentryGuard {
86  
87          private ThreadLocal<Boolean> guard = new ThreadLocal<Boolean>();
88  
89  
90          @Override
91          public boolean isLocked() {
92              // the guard is considered locked if the ThreadLocal contains Boolean.TRUE
93              // note that initially the ThreadLocal contains null
94              return (Boolean.TRUE.equals(guard.get()));
95          }
96  
97          @Override
98          public void lock() {
99              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 }