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 java.util.concurrent.locks.*;
020
021/**
022 * An extension of {@link ReentrantReadWriteLock} with convenience methods for creating
023 * auto-closeable locks that work seamlessly with try-with-resources statements.
024 *
025 * <p>
026 * This class provides a simpler API than the standard {@link ReentrantReadWriteLock} by wrapping
027 * read and write locks in {@link SimpleLock} instances that implement {@link AutoCloseable}.
028 * This allows locks to be automatically released when used in try-with-resources blocks, reducing
029 * the risk of forgetting to unlock and preventing deadlocks.
030 *
031 * <p>
032 * The returned {@link SimpleLock} instances automatically acquire the lock when created and release
033 * it when {@link SimpleLock#close()} is called (either explicitly or automatically via try-with-resources).
034 *
035 * <h5 class='section'>Example:</h5>
036 * <p class='bjava'>
037 *    <jc>// Using read lock with try-with-resources</jc>
038 *    <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleReadWriteLock();
039 *    <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>lock</jv>.read()) {
040 *       <jc>// Read operations here - lock is automatically held</jc>
041 *       <jv>sharedResource</jv>.read();
042 *    }
043 *    <jc>// Lock is automatically released when exiting try block</jc>
044 *
045 *    <jc>// Using write lock with try-with-resources</jc>
046 *    <jk>try</jk> (<jk>var</jk> <jv>writeLock</jv> = <jv>lock</jv>.write()) {
047 *       <jc>// Write operations here - lock is automatically held</jc>
048 *       <jv>sharedResource</jv>.write(<js>"new value"</js>);
049 *    }
050 *    <jc>// Lock is automatically released when exiting try block</jc>
051 * </p>
052 *
053 * <h5 class='section'>Thread Safety:</h5>
054 * <p>
055 * This class inherits all thread-safety guarantees from {@link ReentrantReadWriteLock}.
056 * Multiple threads can hold read locks simultaneously, but write locks are exclusive.
057 * Read and write locks cannot be held simultaneously by different threads.
058 *
059 * <h5 class='section'>Notes:</h5><ul>
060 *    <li class='note'>The returned {@link SimpleLock} instances are not thread-safe themselves and should
061 *       not be shared between threads. Each thread should call {@link #read()} or {@link #write()} separately.
062 *    <li class='note'>Each call to {@link #read()} or {@link #write()} returns a new {@link SimpleLock} instance.
063 *    <li class='note'>The lock is automatically acquired when {@link SimpleLock} is created and released when
064 *       {@link SimpleLock#close()} is called.
065 *    <li class='note'>This class supports reentrant locking - the same thread can acquire multiple read locks
066 *       or upgrade from read to write lock (if no other threads hold read locks).
067 *    <li class='note'>For fair ordering, use {@link #SimpleReadWriteLock(boolean)} with <jk>true</jk>.
068 * </ul>
069 *
070 * <h5 class='section'>See Also:</h5><ul>
071 *    <li class='jc'>{@link SimpleLock} - The auto-closeable lock wrapper
072 *    <li class='jc'>{@link ReentrantReadWriteLock} - The underlying lock implementation
073 * </ul>
074 */
075@SuppressWarnings({
076   "resource" // Caller takes ownership of the returned lock via try-with-resources
077})
078public class SimpleReadWriteLock extends ReentrantReadWriteLock {
079   private static final long serialVersionUID = 1L;
080
081   /**
082    * A no-op lock instance that performs no actual locking operations.
083    *
084    * <p>
085    * This instance can be used when locking is conditionally disabled or when you need a
086    * {@link SimpleReadWriteLock} instance but don't want any actual synchronization overhead.
087    * All lock operations on this instance are no-ops and return {@link SimpleLock#NO_OP}.
088    *
089    * <h5 class='section'>Example:</h5>
090    * <p class='bjava'>
091    *    <jc>// Use NO_OP when locking is disabled</jc>
092    *    <jk>var</jk> <jv>lock</jv> = isLockingEnabled() ? <jk>new</jk> SimpleReadWriteLock() : SimpleReadWriteLock.NO_OP;
093    *    <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>lock</jv>.read()) {
094    *       <jc>// Code works the same whether locking is enabled or not</jc>
095    *       <jv>sharedResource</jv>.read();
096    *    }
097    * </p>
098    *
099    * <h5 class='section'>Notes:</h5><ul>
100    *    <li class='note'>Calling {@link #read()} or {@link #write()} on this instance returns {@link SimpleLock#NO_OP}.
101    *    <li class='note'>Closing the returned {@link SimpleLock} has no effect.
102    *    <li class='note'>This instance is thread-safe and can be shared across threads.
103    * </ul>
104    */
105   public static final SimpleReadWriteLock NO_OP = new SimpleReadWriteLock() {
106      private static final long serialVersionUID = 1L;
107
108      @Override
109      public SimpleLock read() {
110         return SimpleLock.NO_OP;
111      }
112
113      @Override
114      public SimpleLock write() {
115         return SimpleLock.NO_OP;
116      }
117   };
118
119   /**
120    * Constructor.
121    *
122    * <p>
123    * Creates a new read-write lock with an unfair ordering policy. Unfair locks generally
124    * provide better throughput under high contention but may not guarantee fairness.
125    */
126   public SimpleReadWriteLock() {}
127
128   /**
129    * Constructor.
130    *
131    * <p>
132    * Creates a new read-write lock with the specified fairness policy.
133    *
134    * <h5 class='section'>Fair vs Unfair:</h5><ul>
135    *    <li class='note'><b>Fair locks</b> (<jk>true</jk>): Threads acquire locks in approximately
136    *       FIFO order. Provides fairness but may have lower throughput under high contention.
137    *    <li class='note'><b>Unfair locks</b> (<jk>false</jk>): No ordering guarantee. Generally provides
138    *       better throughput but may starve some threads under high contention.
139    * </ul>
140    *
141    * @param fair <jk>true</jk> if this lock should use a fair ordering policy, <jk>false</jk> for unfair.
142    */
143   public SimpleReadWriteLock(boolean fair) {
144      super(fair);
145   }
146
147   /**
148    * Acquires and returns a read lock.
149    *
150    * <p>
151    * The returned {@link SimpleLock} automatically acquires the read lock when created and releases
152    * it when {@link SimpleLock#close()} is called. Multiple threads can hold read locks simultaneously,
153    * but a write lock cannot be acquired while any read locks are held.
154    *
155    * <h5 class='section'>Example:</h5>
156    * <p class='bjava'>
157    *    <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleReadWriteLock();
158    *    <jk>try</jk> (<jk>var</jk> <jv>readLock</jv> = <jv>lock</jv>.read()) {
159    *       <jc>// Multiple threads can hold read locks simultaneously</jc>
160    *       <jv>sharedResource</jv>.read();
161    *    }
162    *    <jc>// Lock is automatically released</jc>
163    * </p>
164    *
165    * <h5 class='section'>Notes:</h5><ul>
166    *    <li class='note'>This method blocks until the read lock can be acquired.
167    *    <li class='note'>Multiple threads can hold read locks concurrently.
168    *    <li class='note'>Read locks cannot be acquired while a write lock is held by another thread.
169    *    <li class='note'>The same thread can acquire multiple read locks (reentrant).
170    *    <li class='note'>Each call returns a new {@link SimpleLock} instance.
171    * </ul>
172    *
173    * @return A new {@link SimpleLock} that holds the read lock. The lock is automatically acquired.
174    */
175   public SimpleLock read() {
176      return new SimpleLock(readLock());
177   }
178
179   /**
180    * Acquires and returns a write lock.
181    *
182    * <p>
183    * The returned {@link SimpleLock} automatically acquires the write lock when created and releases
184    * it when {@link SimpleLock#close()} is called. Write locks are exclusive - only one thread can
185    * hold a write lock at a time, and no read locks can be held while a write lock is active.
186    *
187    * <h5 class='section'>Example:</h5>
188    * <p class='bjava'>
189    *    <jk>var</jk> <jv>lock</jv> = <jk>new</jk> SimpleReadWriteLock();
190    *    <jk>try</jk> (<jk>var</jk> <jv>writeLock</jv> = <jv>lock</jv>.write()) {
191    *       <jc>// Only one thread can hold a write lock at a time</jc>
192    *       <jv>sharedResource</jv>.write(<js>"new value"</js>);
193    *    }
194    *    <jc>// Lock is automatically released</jc>
195    * </p>
196    *
197    * <h5 class='section'>Notes:</h5><ul>
198    *    <li class='note'>This method blocks until the write lock can be acquired.
199    *    <li class='note'>Write locks are exclusive - only one thread can hold a write lock at a time.
200    *    <li class='note'>Write locks cannot be acquired while any read locks are held by other threads.
201    *    <li class='note'>Read locks cannot be acquired while a write lock is held.
202    *    <li class='note'>The same thread can acquire multiple write locks (reentrant).
203    *    <li class='note'>Each call returns a new {@link SimpleLock} instance.
204    * </ul>
205    *
206    * @return A new {@link SimpleLock} that holds the write lock. The lock is automatically acquired.
207    */
208   public SimpleLock write() {
209      return new SimpleLock(writeLock());
210   }
211}