1 /*
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19 package org.apache.hadoop.hbase.util;
20
21 import java.util.concurrent.locks.ReentrantReadWriteLock;
22
23 import org.apache.hadoop.hbase.classification.InterfaceAudience;
24
25 /**
26 * Allows multiple concurrent clients to lock on a numeric id with ReentrantReadWriteLock. The
27 * intended usage for read lock is as follows:
28 *
29 * <pre>
30 * ReentrantReadWriteLock lock = idReadWriteLock.getLock(id);
31 * try {
32 * lock.readLock().lock();
33 * // User code.
34 * } finally {
35 * lock.readLock().unlock();
36 * }
37 * </pre>
38 *
39 * For write lock, use lock.writeLock()
40 */
41 @InterfaceAudience.Private
42 public class IdReadWriteLock {
43 // The number of lock we want to easily support. It's not a maximum.
44 private static final int NB_CONCURRENT_LOCKS = 1000;
45 // The pool to get entry from, entries are mapped by weak reference to make it able to be
46 // garbage-collected asap
47 private final WeakObjectPool<Long, ReentrantReadWriteLock> lockPool =
48 new WeakObjectPool<Long, ReentrantReadWriteLock>(
49 new WeakObjectPool.ObjectFactory<Long, ReentrantReadWriteLock>() {
50 @Override
51 public ReentrantReadWriteLock createObject(Long id) {
52 return new ReentrantReadWriteLock();
53 }
54 }, NB_CONCURRENT_LOCKS);
55
56 /**
57 * Get the ReentrantReadWriteLock corresponding to the given id
58 * @param id an arbitrary number to identify the lock
59 */
60 public ReentrantReadWriteLock getLock(long id) {
61 lockPool.purge();
62 ReentrantReadWriteLock readWriteLock = lockPool.get(id);
63 return readWriteLock;
64 }
65
66 /** For testing */
67 int purgeAndGetEntryPoolSize() {
68 gc();
69 Threads.sleep(200);
70 lockPool.purge();
71 return lockPool.size();
72 }
73
74 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="DM_GC", justification="Intentional")
75 private void gc() {
76 System.gc();
77 }
78
79 @edu.umd.cs.findbugs.annotations.SuppressWarnings(
80 value="JLM_JSR166_UTILCONCURRENT_MONITORENTER",
81 justification="Synchronization on rwlock is intentional")
82 public void waitForWaiters(long id, int numWaiters) throws InterruptedException {
83 for (ReentrantReadWriteLock readWriteLock;;) {
84 readWriteLock = lockPool.get(id);
85 if (readWriteLock != null) {
86 synchronized (readWriteLock) {
87 if (readWriteLock.getQueueLength() >= numWaiters) {
88 return;
89 }
90 }
91 }
92 Thread.sleep(50);
93 }
94 }
95 }