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  
20  
21  package org.apache.hadoop.hbase.metrics.impl;
22  
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.Set;
26  import java.util.concurrent.ConcurrentHashMap;
27  import java.util.concurrent.atomic.AtomicInteger;
28  
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  
31  import com.google.common.base.Supplier;
32  
33  /**
34   * A map of K to V, but does ref counting for added and removed values. The values are
35   * not added directly, but instead requested from the given Supplier if ref count == 0. Each put()
36   * call will increment the ref count, and each remove() will decrement it. The values are removed
37   * from the map iff ref count == 0.
38   */
39  @InterfaceAudience.Private
40  class RefCountingMap<K, V> {
41  
42    private ConcurrentHashMap<K, Payload<V>> map = new ConcurrentHashMap<>();
43    private static class Payload<V> {
44      V v;
45      final AtomicInteger refCount = new AtomicInteger(1); // create with ref count = 1
46      Payload(V v) {
47        this.v = v;
48      }
49    }
50  
51    @edu.umd.cs.findbugs.annotations.SuppressWarnings(
52      value="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION",
53      justification="We use the object monitor to serialize operations on the concurrent map")
54    V put(K key, Supplier<V> supplier) {
55      synchronized (this) {
56        Payload<V> oldValue = map.get(key);
57        if (oldValue == null) {
58          oldValue = new Payload<V>(supplier.get());
59          map.put(key, oldValue);
60          return oldValue.v;
61        }
62        oldValue.refCount.incrementAndGet();
63        return oldValue.v;
64      }
65    }
66  
67    V get(K k) {
68      Payload<V> p = map.get(k);
69      return p == null ? null : p.v;
70    }
71  
72    /**
73     * Decrements the ref count of k, and removes from map if ref count == 0.
74     * @param k the key to remove
75     * @return the value associated with the specified key or null if key is removed from map.
76     */
77    V remove(K key) {
78      synchronized (this) {
79        Payload<V> oldValue = map.get(key);
80        if (oldValue != null) {
81          if (oldValue.refCount.decrementAndGet() == 0) {
82            map.remove(key);
83            return null;
84          }
85          return oldValue.v;
86        }
87      }
88      return null;
89    }
90  
91    void clear() {
92      map.clear();
93    }
94  
95    Set<K> keySet() {
96      return map.keySet();
97    }
98  
99    Collection<V> values() {
100     ArrayList<V> values = new ArrayList<V>(map.size());
101     for (Payload<V> v : map.values()) {
102       values.add(v.v);
103     }
104     return values;
105   }
106 
107   int size() {
108     return map.size();
109   }
110 }