View Javadoc

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  }