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 }