001/*
002 * Copyright 2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.util;
022
023
024
025import java.io.Closeable;
026import java.util.concurrent.TimeUnit;
027import java.util.concurrent.TimeoutException;
028import java.util.concurrent.locks.ReentrantLock;
029
030import static com.unboundid.util.UtilityMessages.*;
031
032
033
034/**
035 * This class provides an implementation of a reentrant lock that can be used
036 * with the Java try-with-resources facility.  It does not implement the
037 * {@code java.util.concurrent.locks.Lock} interface in order to ensure that it
038 * can only be used through lock-with-resources mechanism, but it uses a
039 * {@code java.util.concurrent.locks.ReentrantLock} behind the scenes to provide
040 * its functionality.
041 * <BR><BR>
042 * <H2>Example</H2>
043 * The following example demonstrates how to use this lock using the Java
044 * try-with-resources facility:
045 * <PRE>
046 * // Wait for up to 5 seconds to acquire the lock.
047 * try (final CloseableLock.Lock lock =
048 *           closeableLock.tryLock(5L, TimeUnit.SECONDS))
049 * {
050 *   // NOTE:  If you don't reference the lock object inside the try block, the
051 *   // compiler will issue a warning.
052 *   lock.avoidCompilerWarning();
053 *
054 *   // Do something while the lock is held.  The lock will automatically be
055 *   // released once code execution leaves this block.
056 * }
057 * catch (final InterruptedException e)
058 * {
059 *   // The thread was interrupted before the lock could be acquired.
060 * }
061 * catch (final TimeoutException)
062 * {
063 *   // The lock could not be acquired within the specified 5-second timeout.
064 * }
065 * </PRE>
066 */
067@Mutable()
068@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
069public final class CloseableLock
070{
071  // The {@code Closeable} object that will be returned by all of the methods
072  // used to acquire the lock.
073  private final Lock lock;
074
075  // The reentrant lock that will be used to actually perform the locking.
076  private final ReentrantLock reentrantLock;
077
078
079
080  /**
081   * Creates a new instance of this lock with a non-fair ordering policy.
082   */
083  public CloseableLock()
084  {
085    this(false);
086  }
087
088
089
090  /**
091   * Creates a new instance of this lock with the specified ordering policy.
092   *
093   * @param  fair  Indicates whether the lock should use fair ordering.  If
094   *               {@code true}, then if multiple threads are waiting on the
095   *               lock, then the one that has been waiting the longest is the
096   *               one that will get it.  If {@code false}, then no guarantee
097   *               will be made about the order.  Fair ordering can incur a
098   *               performance penalty.
099   */
100  public CloseableLock(final boolean fair)
101  {
102    reentrantLock = new ReentrantLock(fair);
103    lock = new Lock(reentrantLock);
104  }
105
106
107
108  /**
109   * Acquires this lock, blocking until the lock is available.
110   *
111   * @return  The {@link Lock} instance that may be used to perform the
112   *          unlock via the try-with-resources facility.
113   */
114  public Lock lock()
115  {
116    reentrantLock.lock();
117    return lock;
118  }
119
120
121
122  /**
123   * Acquires this lock, blocking until the lock is available.
124   *
125   * @return  The {@link Lock} instance that may be used to perform the unlock
126   *          via the try-with-resources facility.
127   *
128   * @throws  InterruptedException  If the thread is interrupted while waiting
129   *                                to acquire the lock.
130   */
131  public Lock lockInterruptibly()
132         throws InterruptedException
133  {
134    reentrantLock.lockInterruptibly();
135    return lock;
136  }
137
138
139
140  /**
141   * Tries to acquire the lock, waiting up to the specified length of time for
142   * it to become available.
143   *
144   * @param  waitTime  The maximum length of time to wait for the lock.  It must
145   *                   be greater than zero.
146   * @param  timeUnit  The time unit that should be used when evaluating the
147   *                   {@code waitTime} value.
148   *
149   * @return  The {@link Lock} instance that may be used to perform the unlock
150   *          via the try-with-resources facility.
151   *
152   * @throws  InterruptedException  If the thread is interrupted while waiting
153   *                                to acquire the lock.
154   *
155   * @throws  TimeoutException  If the lock could not be acquired within the
156   *                            specified length of time.
157   */
158  public Lock tryLock(final long waitTime, final TimeUnit timeUnit)
159         throws InterruptedException, TimeoutException
160  {
161    if (waitTime <= 0)
162    {
163      Validator.violation(
164           "CloseableLock.tryLock.waitTime must be greater than zero.  The " +
165                "provided value was " + waitTime);
166    }
167
168    if (reentrantLock.tryLock(waitTime, timeUnit))
169    {
170      return lock;
171    }
172    else
173    {
174      throw new TimeoutException(ERR_CLOSEABLE_LOCK_TRY_LOCK_TIMEOUT.get(
175           StaticUtils.millisToHumanReadableDuration(
176                timeUnit.toMillis(waitTime))));
177    }
178  }
179
180
181
182  /**
183   * Indicates whether this lock uses fair ordering.
184   *
185   * @return  {@code true} if this lock uses fair ordering, or {@code false} if
186   *          not.
187   */
188  public boolean isFair()
189  {
190    return reentrantLock.isFair();
191  }
192
193
194
195  /**
196   * Indicates whether this lock is currently held by any thread.
197   *
198   * @return  {@code true} if this lock is currently held by any thread, or
199   *          {@code false} if not.
200   */
201  public boolean isLocked()
202  {
203    return reentrantLock.isLocked();
204  }
205
206
207
208  /**
209   * Indicates whether this lock is currently held by the current thread.
210   *
211   * @return  {@code true} if this lock is currently held by the current thread,
212   *          or {@code false} if not.
213   */
214  public boolean isHeldByCurrentThread()
215  {
216    return reentrantLock.isHeldByCurrentThread();
217  }
218
219
220
221  /**
222   * Retrieves the number of holds that the current thread has on the lock.
223   *
224   * @return  The number of holds that the current thread has on the lock.
225   */
226  public int getHoldCount()
227  {
228    return reentrantLock.getHoldCount();
229  }
230
231
232
233  /**
234   * Indicates whether any threads are currently waiting to acquire this lock.
235   *
236   * @return  {@code true} if any threads are currently waiting to acquire this
237   *          lock, or {@code false} if not.
238   */
239  public boolean hasQueuedThreads()
240  {
241    return reentrantLock.hasQueuedThreads();
242  }
243
244
245
246  /**
247   * Indicates whether the specified thread is currently waiting to acquire this
248   * lock, or {@code false} if not.
249   *
250   * @param  thread  The thread for which to make the determination.  It must
251   *                 not be {@code null}.
252   *
253   * @return  {@code true} if the specified thread is currently waiting to
254   *          acquire this lock, or {@code false} if not.
255   */
256  public boolean hasQueuedThread(final Thread thread)
257  {
258    Validator.ensureNotNull(thread);
259
260    return reentrantLock.hasQueuedThread(thread);
261  }
262
263
264
265  /**
266   * Retrieves an estimate of the number of threads currently waiting to acquire
267   * this lock.
268   *
269   * @return  An estimate of the number of threads currently waiting to acquire
270   *          this lock.
271   */
272  public int getQueueLength()
273  {
274    return reentrantLock.getQueueLength();
275  }
276
277
278
279  /**
280   * Retrieves a string representation of this lock.
281   *
282   * @return  A string representation of this lock.
283   */
284  @Override()
285  public String toString()
286  {
287    return "CloseableLock(lock=" + reentrantLock.toString() + ')';
288  }
289
290
291
292  /**
293   * This class provides a {@code Closeable} implementation that may be used to
294   * unlock a {@link CloseableLock} via Java's try-with-resources
295   * facility.
296   */
297  public final class Lock
298         implements Closeable
299  {
300    // The associated reentrant lock.
301    private final ReentrantLock lock;
302
303
304
305    /**
306     * Creates a new instance with the provided lock.
307     *
308     * @param  lock  The lock that will be unlocked when the [@link #close()}
309     *               method is called.  This must not be {@code null}.
310     */
311    private Lock(final ReentrantLock lock)
312    {
313      this.lock = lock;
314    }
315
316
317
318    /**
319     * This method does nothing.  However, calling it inside a try block when
320     * used in the try-with-resources framework can help avoid a compiler
321     * warning that the JVM will give you if you don't reference the
322     * {@code Closeable} object inside the try block.
323     */
324    public void avoidCompilerWarning()
325    {
326      // No implementation is required.
327    }
328
329
330
331    /**
332     * Unlocks the associated lock.
333     */
334    @Override()
335    public void close()
336    {
337      lock.unlock();
338    }
339  }
340}