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.hbase.util;
019
020import java.lang.ref.Reference;
021import java.util.concurrent.locks.ReentrantReadWriteLock;
022import org.apache.yetus.audience.InterfaceAudience;
023
024@InterfaceAudience.Private
025public class IdReadWriteLockWithObjectPool<T> extends IdReadWriteLock<T> {
026  // The number of lock we want to easily support. It's not a maximum.
027  private static final int NB_CONCURRENT_LOCKS = 1000;
028  /**
029   * The pool to get entry from, entries are mapped by {@link Reference} and will be automatically
030   * garbage-collected by JVM
031   */
032  private final ObjectPool<T, ReentrantReadWriteLock> lockPool;
033  private final ReferenceType refType;
034
035  public IdReadWriteLockWithObjectPool() {
036    this(ReferenceType.WEAK);
037  }
038
039  /**
040   * Constructor of IdReadWriteLockWithObjectPool
041   * @param referenceType type of the reference used in lock pool, {@link ReferenceType#WEAK} by
042   *                      default. Use {@link ReferenceType#SOFT} if the key set is limited and the
043   *                      locks will be reused with a high frequency
044   */
045  public IdReadWriteLockWithObjectPool(ReferenceType referenceType) {
046    this.refType = referenceType;
047    switch (referenceType) {
048      case SOFT:
049        lockPool = new SoftObjectPool<>(new ObjectPool.ObjectFactory<T, ReentrantReadWriteLock>() {
050          @Override
051          public ReentrantReadWriteLock createObject(T id) {
052            return new ReentrantReadWriteLock();
053          }
054        }, NB_CONCURRENT_LOCKS);
055        break;
056      case WEAK:
057      default:
058        lockPool = new WeakObjectPool<>(new ObjectPool.ObjectFactory<T, ReentrantReadWriteLock>() {
059          @Override
060          public ReentrantReadWriteLock createObject(T id) {
061            return new ReentrantReadWriteLock();
062          }
063        }, NB_CONCURRENT_LOCKS);
064    }
065  }
066
067  public static enum ReferenceType {
068    WEAK,
069    SOFT
070  }
071
072  /**
073   * Get the ReentrantReadWriteLock corresponding to the given id
074   * @param id an arbitrary number to identify the lock
075   */
076  @Override
077  public ReentrantReadWriteLock getLock(T id) {
078    lockPool.purge();
079    ReentrantReadWriteLock readWriteLock = lockPool.get(id);
080    return readWriteLock;
081  }
082
083  /** For testing */
084  int purgeAndGetEntryPoolSize() {
085    gc();
086    Threads.sleep(200);
087    lockPool.purge();
088    return lockPool.size();
089  }
090
091  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "DM_GC", justification = "Intentional")
092  private void gc() {
093    System.gc();
094  }
095
096  public ReferenceType getReferenceType() {
097    return this.refType;
098  }
099}