001/* 002 * ============================================================================ 003 * Copyright © 2002-2024 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * 007 * Licensed to the public under the agreements of the GNU Lesser General Public 008 * License, version 3.0 (the "License"). You may obtain a copy of the License at 009 * 010 * http://www.gnu.org/licenses/lgpl.html 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 014 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 015 * License for the specific language governing permissions and limitations 016 * under the License. 017 */ 018 019package org.tquadrat.foundation.lang; 020 021import static org.apiguardian.api.API.Status.STABLE; 022 023import java.io.Serial; 024import java.util.Optional; 025import java.util.concurrent.locks.Condition; 026import java.util.concurrent.locks.Lock; 027 028import org.apiguardian.api.API; 029import org.tquadrat.foundation.annotation.ClassVersion; 030import org.tquadrat.foundation.lang.internal.AutoLockImpl; 031 032/** 033 * <p>{@summary A wrapper for locks that supports the 034 * {@code try-with-resources} feature of Java 7.}</p> 035 * <p>Instead of</p> 036 * <div class="source-container"><pre> m_Lock.lock(); 037 * try 038 * { 039 * … 040 * } 041 * finally 042 * { 043 * m_Lock.unlock(); 044 * }</pre></div> 045 * <p>you can write now</p> 046 * <div class="source-container"><pre> private final AutoLock m_AutoLock = AutoLock.of(); 047 * 048 * … 049 * 050 * try( final var ignored = m_AutoLock.lock() ) 051 * { 052 * … 053 * }</pre></div> 054 * <p>The creation of the local reference to the wrapper object means some 055 * overhead but in very most scenarios this is negligible.</p> 056 * <p>{@code AutoLock} will only expose the methods 057 * {@link #lock()} 058 * and 059 * {@link #lockInterruptibly()} 060 * of the interface 061 * {@link java.util.concurrent.locks.Lock Lock}, 062 * but with a return value. Exposing other methods is not reasonable.</p> 063 * <p>Calling 064 * {@link #close()} 065 * on the {@code AutoLock} instance or 066 * {@link Lock#unlock()} 067 * on the wrapped {@code Lock} object inside the {@code try} block may cause 068 * unpredictable effects.</p> 069 * 070 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 071 * @version $Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $ 072 * @since 0.1.0 073 * 074 * @see java.util.concurrent.locks.Lock 075 * 076 * @UMLGraph.link 077 */ 078@ClassVersion( sourceVersion = "$Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $" ) 079@API( status = STABLE, since = "0.1.0" ) 080public sealed interface AutoLock extends AutoCloseable 081 permits org.tquadrat.foundation.lang.internal.AutoLockImpl 082{ 083 /*---------------*\ 084 ====** Inner Classes **==================================================== 085 \*---------------*/ 086 /** 087 * <p>{@summary This exception is thrown when an operation fails.} The 088 * 089 * @version $Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $ 090 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 091 * @UMLGraph.link 092 * @since 0.1.0 093 */ 094 @SuppressWarnings( "InnerClassOfInterface" ) 095 @ClassVersion( sourceVersion = "$Id: AutoLock.java 1185 2026-04-06 10:26:47Z tquadrat $" ) 096 @API( status = STABLE, since = "0.1.0" ) 097 public static final class ExecutionFailedException extends RuntimeException 098 { 099 /*------------------------*\ 100 ====** Static Initialisations **======================================= 101 \*------------------------*/ 102 /** 103 * The serial version UID for objects of this class: {@value}. 104 * 105 * @hidden 106 */ 107 @Serial 108 private static final long serialVersionUID = 1L; 109 110 /*--------------*\ 111 ====** Constructors **================================================= 112 \*--------------*/ 113 /** 114 * Creates a new instance of {@code ExecutionFailedException} from the 115 * cause of the failure. 116 * 117 * @param cause The failure cause. 118 */ 119 public ExecutionFailedException( final Throwable cause ) { super( cause ); } 120 } // class ExecutionFailedException 121 122 /*---------*\ 123 ====** Methods **========================================================== 124 \*---------*/ 125 /** 126 * {@inheritDoc} 127 */ 128 @Override 129 public void close(); 130 131 /** 132 * Evaluates the given 133 * {@link Constraint} 134 * after obtaining the lock, and returns its result. 135 * 136 * @param constraint The constraint 137 * @return The evaluation result. 138 * @throws ExecutionFailedException The evaluation failed for some 139 * reason. 140 */ 141 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 142 public boolean evaluate( final Constraint constraint ) throws ExecutionFailedException; 143 144 /** 145 * Executes the given action after obtaining the lock. 146 * 147 * @param action The action. 148 * @throws ExecutionFailedException The action failed for some reason. 149 * @deprecated Use 150 * {@link #perform(Action)} 151 * instead; basically this method was renamed to avoid the unintended 152 * use of 153 * {@link #execute(Operation)} 154 * when an 155 * {@link Action} 156 * should be used instead. 157 */ 158 @Deprecated( since = "0.25.3", forRemoval = true ) 159 public default void execute( final Action action ) throws ExecutionFailedException 160 { 161 perform( action ); 162 } // execute() 163 164 /** 165 * Executes the given operation after obtaining the lock, and returns its 166 * result. 167 * 168 * @param <R> The type of the operation's result. 169 * @param operation The operation. 170 * @return An instance of 171 * {@link Optional} 172 * that holds the result of the operation. 173 * @throws ExecutionFailedException The operation failed for some 174 * reason. 175 */ 176 public <R> Optional<R> execute( final Operation<? extends R> operation ) throws ExecutionFailedException; 177 178 /** 179 * Returns the wrapped lock. 180 * 181 * @return The wrapped lock. 182 */ 183 public Lock getWrappedLockInstance(); 184 185 /** 186 * Calls 187 * {@link java.util.concurrent.locks.Lock#lock() lock()} 188 * on the wrapped 189 * {@link java.util.concurrent.locks.Lock} 190 * instance. 191 * 192 * @return The reference to this {@code AutoLock} instance. 193 */ 194 public AutoLock lock(); 195 196 /** 197 * Calls 198 * {@link java.util.concurrent.locks.Lock#lockInterruptibly() lockInterruptibly()} 199 * on the wrapped 200 * {@link java.util.concurrent.locks.Lock} 201 * instance. 202 * 203 * @return The reference to this {@code AutoLock} instance. 204 * @throws InterruptedException The current thread was interrupted while 205 * acquiring the lock (and interruption of lock acquisition is 206 * supported). 207 */ 208 public AutoLock lockInterruptibly() throws InterruptedException; 209 210 /** 211 * Returns a new 212 * {@link Condition} 213 * instance that is bound to the instance of 214 * {@link java.util.concurrent.locks.Lock} 215 * that is wrapped by this {@code AutoLock} instance. 216 * 217 * @return The new condition. 218 * @throws UnsupportedOperationException The wrapped lock's 219 * implementation does not support conditions. 220 * 221 * @see java.util.concurrent.locks.Lock#newCondition() 222 */ 223 public Condition newCondition(); 224 225 /** 226 * Creates a new {@code AutoLock} instance with an internal lock object. 227 * 228 * @return The new instance. 229 */ 230 @API( status = STABLE, since = "0.0.5" ) 231 public static AutoLock of() { return new AutoLockImpl(); } 232 233 /** 234 * Creates a new {@code AutoLock} instance from the given 235 * {@link Lock} 236 * instance. 237 * 238 * @param lock The wrapped lock. 239 * @return The new instance. 240 */ 241 @API( status = STABLE, since = "0.0.5" ) 242 public static AutoLock of( final Lock lock ) { return new AutoLockImpl( lock ); } 243 244 /** 245 * <p>{@summary Performs the given action after obtaining the lock.}</p> 246 * <p>This differs from 247 * {@link #execute(Operation)} 248 * as 249 * {@link Action} 250 * does not return something, and a warning regarding "Result of 251 * assignment expression used" can be avoided in case the 252 * {@code action} is used to set an attribute.</p> 253 * 254 * @param action The action. 255 * @throws ExecutionFailedException The action failed for some reason. 256 */ 257 public void perform( final Action action ) throws ExecutionFailedException; 258} 259// interface AutoLock 260 261/* 262 * End of File 263 */