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/** 025 * Allows multiple concurrent clients to lock on a numeric id with ReentrantReadWriteLock. The 026 * intended usage for read lock is as follows: 027 * 028 * <pre> 029 * ReentrantReadWriteLock lock = idReadWriteLock.getLock(id); 030 * try { 031 * lock.readLock().lock(); 032 * // User code. 033 * } finally { 034 * lock.readLock().unlock(); 035 * } 036 * </pre> 037 * 038 * For write lock, use lock.writeLock() 039 */ 040@InterfaceAudience.Private 041public class IdReadWriteLock<T> { 042 // The number of lock we want to easily support. It's not a maximum. 043 private static final int NB_CONCURRENT_LOCKS = 1000; 044 /** 045 * The pool to get entry from, entries are mapped by {@link Reference} and will be automatically 046 * garbage-collected by JVM 047 */ 048 private final ObjectPool<T, ReentrantReadWriteLock> lockPool; 049 private final ReferenceType refType; 050 051 public IdReadWriteLock() { 052 this(ReferenceType.WEAK); 053 } 054 055 /** 056 * Constructor of IdReadWriteLock 057 * @param referenceType type of the reference used in lock pool, {@link ReferenceType#WEAK} by 058 * default. Use {@link ReferenceType#SOFT} if the key set is limited and the 059 * locks will be reused with a high frequency 060 */ 061 public IdReadWriteLock(ReferenceType referenceType) { 062 this.refType = referenceType; 063 switch (referenceType) { 064 case SOFT: 065 lockPool = new SoftObjectPool<>(new ObjectPool.ObjectFactory<T, ReentrantReadWriteLock>() { 066 @Override 067 public ReentrantReadWriteLock createObject(T id) { 068 return new ReentrantReadWriteLock(); 069 } 070 }, NB_CONCURRENT_LOCKS); 071 break; 072 case WEAK: 073 default: 074 lockPool = new WeakObjectPool<>(new ObjectPool.ObjectFactory<T, ReentrantReadWriteLock>() { 075 @Override 076 public ReentrantReadWriteLock createObject(T id) { 077 return new ReentrantReadWriteLock(); 078 } 079 }, NB_CONCURRENT_LOCKS); 080 } 081 } 082 083 public static enum ReferenceType { 084 WEAK, 085 SOFT 086 } 087 088 /** 089 * Get the ReentrantReadWriteLock corresponding to the given id 090 * @param id an arbitrary number to identify the lock 091 */ 092 public ReentrantReadWriteLock getLock(T id) { 093 lockPool.purge(); 094 ReentrantReadWriteLock readWriteLock = lockPool.get(id); 095 return readWriteLock; 096 } 097 098 /** For testing */ 099 int purgeAndGetEntryPoolSize() { 100 gc(); 101 Threads.sleep(200); 102 lockPool.purge(); 103 return lockPool.size(); 104 } 105 106 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "DM_GC", justification = "Intentional") 107 private void gc() { 108 System.gc(); 109 } 110 111 public void waitForWaiters(T id, int numWaiters) throws InterruptedException { 112 for (ReentrantReadWriteLock readWriteLock;;) { 113 readWriteLock = lockPool.get(id); 114 if (readWriteLock != null) { 115 synchronized (readWriteLock) { 116 if (readWriteLock.getQueueLength() >= numWaiters) { 117 return; 118 } 119 } 120 } 121 Thread.sleep(50); 122 } 123 } 124 125 public ReferenceType getReferenceType() { 126 return this.refType; 127 } 128}