001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.concurrent;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.concurrent.locks.*;
022
023/**
024 * A simple auto-closeable wrapper around a {@link Lock} that automatically acquires the lock
025 * when created and releases it when closed.
026 *
027 * <p>
028 * This class wraps any {@link Lock} implementation and makes it work seamlessly with try-with-resources
029 * statements. The lock is automatically acquired in the constructor and released when {@link #close()}
030 * is called (either explicitly or automatically via try-with-resources).
031 *
032 * <p>
033 * This class is typically used indirectly through {@link SimpleReadWriteLock#read()} and
034 * {@link SimpleReadWriteLock#write()}, but can also be used directly with any {@link Lock} instance.
035 *
036 * <h5 class='section'>Example:</h5>
037 * <p class='bjava'>
038 *    <jc>// Using with a ReentrantLock</jc>
039 *    <jk>var</jk> <jv>lock</jv> = <jk>new</jk> ReentrantLock();
040 *    <jk>try</jk> (<jk>var</jk> <jv>simpleLock</jv> = <jk>new</jk> SimpleLock(<jv>lock</jv>)) {
041 *       <jc>// Lock is automatically held - perform critical section operations</jc>
042 *       <jv>sharedResource</jv>.modify();
043 *    }
044 *    <jc>// Lock is automatically released when exiting try block</jc>
045 *
046 *    <jc>// Using with SimpleReadWriteLock</jc>
047 *    <jk>var</jk> <jv>rwLock</jv> = <jk>new</jk> SimpleReadWriteLock();
048 *    <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>rwLock</jv>.read()) {
049 *       <jc>// Read lock is automatically held</jc>
050 *       <jv>sharedResource</jv>.read();
051 *    }
052 *    <jc>// Read lock is automatically released</jc>
053 * </p>
054 *
055 * <h5 class='section'>Thread Safety:</h5>
056 * <p>
057 * This class is not thread-safe itself. Each instance wraps a single {@link Lock} and should be used
058 * by a single thread. The underlying lock's thread-safety guarantees apply to the wrapped lock.
059 *
060 * <h5 class='section'>Notes:</h5><ul>
061 *    <li class='note'>The lock is automatically acquired in the constructor and released in {@link #close()}.
062 *    <li class='note'>If <jk>null</jk> is passed to the constructor, no locking operations are performed
063 *       (useful for creating no-op locks).
064 *    <li class='note'>This class implements {@link AutoCloseable}, making it ideal for use with
065 *       try-with-resources statements.
066 *    <li class='note'>Multiple calls to {@link #close()} are safe - subsequent calls have no effect if
067 *       the lock has already been released.
068 *    <li class='note'>The underlying lock's reentrancy behavior is preserved - if the lock supports
069 *       reentrant locking, the same thread can acquire it multiple times.
070 * </ul>
071 *
072 * <h5 class='section'>See Also:</h5><ul>
073 *    <li class='jc'>{@link SimpleReadWriteLock} - A read-write lock that returns {@link SimpleLock} instances
074 *    <li class='jc'>{@link Lock} - The underlying lock interface
075 * </ul>
076 */
077@SuppressWarnings({
078   "resource" // Intentional singleton; wraps a null lock so close() is a no-op
079})
080public class SimpleLock implements AutoCloseable {
081
082   /**
083    * A no-op lock instance that performs no actual locking operations.
084    *
085    * <p>
086    * This instance can be used when locking is conditionally disabled or when you need a
087    * {@link SimpleLock} instance but don't want any actual synchronization overhead.
088    * All lock operations on this instance are no-ops.
089    *
090    * <h5 class='section'>Example:</h5>
091    * <p class='bjava'>
092    *    <jc>// Use NO_OP when locking is disabled</jc>
093    *    <jk>var</jk> <jv>lock</jv> = isLockingEnabled() ? <jk>new</jk> SimpleLock(<jk>new</jk> ReentrantLock()) : SimpleLock.NO_OP;
094    *    <jk>try</jk> (<jk>var</jk> <jv>simpleLock</jv> = <jv>lock</jv>) {
095    *       <jc>// Code works the same whether locking is enabled or not</jc>
096    *       <jv>sharedResource</jv>.modify();
097    *    }
098    * </p>
099    *
100    * <h5 class='section'>Notes:</h5><ul>
101    *    <li class='note'>Calling {@link #close()} on this instance has no effect.
102    *    <li class='note'>This instance is thread-safe and can be shared across threads.
103    *    <li class='note'>This instance wraps a <jk>null</jk> lock, so no actual locking occurs.
104    * </ul>
105    */
106   public static final SimpleLock NO_OP = new SimpleLock(null);
107
108   private final Lock lock;
109
110   /**
111    * Constructor.
112    *
113    * <p>
114    * Creates a new {@link SimpleLock} wrapper around the specified {@link Lock}. The lock is
115    * automatically acquired during construction. If <jk>null</jk> is passed, no locking operations
116    * are performed (useful for creating no-op locks).
117    *
118    * <h5 class='section'>Example:</h5>
119    * <p class='bjava'>
120    *    <jc>// Wrap a ReentrantLock</jc>
121    *    <jk>var</jk> <jv>underlyingLock</jv> = <jk>new</jk> ReentrantLock();
122    *    <jk>var</jk> <jv>simpleLock</jv> = <jk>new</jk> SimpleLock(<jv>underlyingLock</jv>);
123    *    <jc>// Lock is now held - use try-with-resources to ensure release</jc>
124    *    <jk>try</jk> (<jv>simpleLock</jv>) {
125    *       <jc>// Critical section</jc>
126    *    }
127    * </p>
128    *
129    * <h5 class='section'>Notes:</h5><ul>
130    *    <li class='note'>The lock is acquired immediately in the constructor. This method may block
131    *       if the lock is not available.
132    *    <li class='note'>If <jk>null</jk> is passed, no lock is acquired and {@link #close()} will have no effect.
133    *    <li class='note'>The underlying lock's behavior (fairness, reentrancy, etc.) is preserved.
134    * </ul>
135    *
136    * @param lock The {@link Lock} being wrapped. Can be <jk>null</jk> to create a no-op lock.
137    */
138   @SuppressWarnings({
139      "java:S2222" // Lock is intentionally held past constructor return and released in close() per the AutoCloseable contract.
140   })
141   public SimpleLock(Lock lock) {
142      this.lock = lock;
143      if (nn(lock))
144         lock.lock();
145   }
146
147   /**
148    * Releases the lock.
149    *
150    * <p>
151    * This method is called automatically when used in a try-with-resources statement, or can be
152    * called explicitly to release the lock early. If the lock was <jk>null</jk> (no-op lock),
153    * this method has no effect.
154    *
155    * <h5 class='section'>Example:</h5>
156    * <p class='bjava'>
157    *    <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleLock(<jk>new</jk> ReentrantLock());
158    *    <jc>// Lock is held</jc>
159    *    <jv>lock</jv>.close();  <jc>// Explicitly release the lock</jc>
160    * </p>
161    *
162    * <h5 class='section'>Notes:</h5><ul>
163    *    <li class='note'>This method is called automatically when exiting a try-with-resources block.
164    *    <li class='note'>Multiple calls to this method are safe - subsequent calls have no effect if
165    *       the lock has already been released.
166    *    <li class='note'>If the lock was <jk>null</jk> (no-op lock), this method returns immediately
167    *       without performing any operations.
168    *    <li class='note'>The underlying lock's unlock behavior is preserved (e.g., reentrant locks
169    *       must be unlocked the same number of times they were locked).
170    * </ul>
171    *
172    * @throws IllegalMonitorStateException If the current thread does not hold this lock
173    *    (only applies to certain lock implementations).
174    */
175   @Override
176   public void close() {
177      if (nn(lock))
178         lock.unlock();
179   }
180}