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}