001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
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,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.util;
019
020import java.util.concurrent.locks.Lock;
021import java.util.concurrent.locks.Condition;
022import java.util.concurrent.locks.ReentrantLock;
023
024import com.google.common.annotations.VisibleForTesting;
025
026/**
027 * This is a wrap class of a ReentrantLock. Extending AutoCloseable
028 * interface such that the users can use a try-with-resource syntax.
029 */
030public class AutoCloseableLock implements AutoCloseable {
031
032  private final Lock lock;
033
034  /**
035   * Creates an instance of {@code AutoCloseableLock}, initializes
036   * the underlying lock instance with a new {@code ReentrantLock}.
037   */
038  public AutoCloseableLock() {
039    this(new ReentrantLock());
040  }
041
042  /**
043   * Wrap provided Lock instance.
044   * @param lock Lock instance to wrap in AutoCloseable API.
045   */
046  public AutoCloseableLock(Lock lock) {
047    this.lock = lock;
048  }
049
050  /**
051   * A wrapper method that makes a call to {@code lock()} of the underlying
052   * {@code ReentrantLock} object.
053   *
054   * Acquire teh lock it is not held by another thread, then sets
055   * lock held count to one, then returns immediately.
056   *
057   * If the current thread already holds the lock, increase the lock
058   * help count by one and returns immediately.
059   *
060   * If the lock is held by another thread, the current thread is
061   * suspended until the lock has been acquired by current thread.
062   *
063   * @return The {@code ReentrantLock} object itself. This is to
064   * support try-with-resource syntax.
065   */
066  public AutoCloseableLock acquire() {
067    lock.lock();
068    return this;
069  }
070
071  /**
072   * A wrapper method that makes a call to {@code unlock()} of the
073   * underlying {@code ReentrantLock} object.
074   *
075   * Attempts to release the lock.
076   *
077   * If the current thread holds the lock, decrements the hold
078   * count. If the hold count reaches zero, the lock is released.
079   *
080   * If the current thread does not hold the lock, then
081   * {@link IllegalMonitorStateException} is thrown.
082   */
083  public void release() {
084    lock.unlock();
085  }
086
087  /**
088   * Attempts to release the lock by making a call to {@code release()}.
089   *
090   * This is to implement {@code close()} method from {@code AutoCloseable}
091   * interface. This allows users to user a try-with-resource syntax, where
092   * the lock can be automatically released.
093   */
094  @Override
095  public void close() {
096    release();
097  }
098
099  /**
100   * A wrapper method that makes a call to {@code tryLock()} of
101   * the underlying {@code Lock} object.
102   *
103   * If the lock is not held by another thread, acquires the lock, set the
104   * hold count to one and returns {@code true}.
105   *
106   * If the current thread already holds the lock, the increment the hold
107   * count by one and returns {@code true}.
108   *
109   * If the lock is held by another thread then the method returns
110   * immediately with {@code false}.
111   *
112   * @return {@code true} if the lock was free and was acquired by the
113   *          current thread, or the lock was already held by the current
114   *          thread; and {@code false} otherwise.
115   */
116  public boolean tryLock() {
117    return lock.tryLock();
118  }
119
120  /**
121   * A wrapper method that makes a call to {@code isLocked()} of
122   * the underlying {@code ReentrantLock} object.
123   *
124   * Queries if this lock is held by any thread. This method is
125   * designed for use in monitoring of the system state,
126   * not for synchronization control.
127   *
128   * @return {@code true} if any thread holds this lock and
129   *         {@code false} otherwise
130   */
131  @VisibleForTesting
132  boolean isLocked() {
133    if (lock instanceof ReentrantLock) {
134      return ((ReentrantLock)lock).isLocked();
135    }
136    throw new UnsupportedOperationException();
137  }
138
139  /**
140   * See {@link ReentrantLock#newCondition()}.
141   * @return the Condition object
142   */
143  public Condition newCondition() {
144    return lock.newCondition();
145  }
146}