1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.util;
19
20 import java.util.concurrent.atomic.AtomicBoolean;
21 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
22 import java.util.concurrent.atomic.AtomicReference;
23
24 import org.apache.hadoop.hbase.classification.InterfaceAudience;
25 import org.apache.hadoop.hbase.classification.InterfaceStability;
26
27
28
29
30 @InterfaceAudience.Public
31 @InterfaceStability.Evolving
32 public class Counter {
33 private static final int MAX_CELLS_LENGTH = 1 << 20;
34
35 private static class Cell {
36
37
38
39
40 @SuppressWarnings("unused")
41 volatile long p0, p1, p2, p3, p4, p5, p6;
42 volatile long value;
43 @SuppressWarnings("unused")
44 volatile long q0, q1, q2, q3, q4, q5, q6;
45
46 static final AtomicLongFieldUpdater<Cell> valueUpdater =
47 AtomicLongFieldUpdater.newUpdater(Cell.class, "value");
48
49 Cell() {}
50
51 Cell(long initValue) {
52 value = initValue;
53 }
54
55 long get() {
56 return value;
57 }
58
59 boolean add(long delta) {
60 long current = value;
61 return valueUpdater.compareAndSet(this, current, current + delta);
62 }
63 }
64
65 private static class Container {
66
67 final Cell[] cells;
68
69
70 final AtomicBoolean demoted = new AtomicBoolean();
71
72 Container(Cell cell) {
73 this(new Cell[] { cell });
74 }
75
76
77
78
79 Container(Cell[] cells) {
80 this.cells = cells;
81 }
82 }
83
84 private final AtomicReference<Container> containerRef;
85
86 public Counter() {
87 this(new Cell());
88 }
89
90 public Counter(long initValue) {
91 this(new Cell(initValue));
92 }
93
94 private Counter(Cell initCell) {
95 containerRef = new AtomicReference<Container>(new Container(initCell));
96 }
97
98 private static int hash() {
99
100
101 int h = System.identityHashCode(Thread.currentThread());
102
103
104
105
106 h ^= (h >>> 20) ^ (h >>> 12);
107 h ^= (h >>> 7) ^ (h >>> 4);
108 return h;
109 }
110
111 private static class IndexHolder {
112 int index = hash();
113 }
114
115 private final ThreadLocal<IndexHolder> indexHolderThreadLocal =
116 new ThreadLocal<IndexHolder>() {
117 @Override
118 protected IndexHolder initialValue() {
119 return new IndexHolder();
120 }
121 };
122
123 public void add(long delta) {
124 Container container = containerRef.get();
125 Cell[] cells = container.cells;
126 int mask = cells.length - 1;
127
128 IndexHolder indexHolder = indexHolderThreadLocal.get();
129 int baseIndex = indexHolder.index;
130 if(cells[baseIndex & mask].add(delta)) {
131 return;
132 }
133
134 int index = baseIndex + 1;
135 while(true) {
136 if(cells[index & mask].add(delta)) {
137 break;
138 }
139 index++;
140 }
141
142 indexHolder.index = index;
143
144 if(index - baseIndex >= cells.length &&
145 cells.length < MAX_CELLS_LENGTH &&
146 container.demoted.compareAndSet(false, true)) {
147
148 if(containerRef.get() == container) {
149 Cell[] newCells = new Cell[cells.length * 2];
150 System.arraycopy(cells, 0, newCells, 0, cells.length);
151 for(int i = cells.length; i < newCells.length; i++) {
152 newCells[i] = new Cell();
153
154
155
156
157
158 }
159 containerRef.compareAndSet(container, new Container(newCells));
160 }
161 }
162 }
163
164 public void increment() {
165 add(1);
166 }
167
168 public void decrement() {
169 add(-1);
170 }
171
172 public void set(long value) {
173 containerRef.set(new Container(new Cell(value)));
174 }
175
176 public long get() {
177 long sum = 0;
178 for(Cell cell : containerRef.get().cells) {
179 sum += cell.get();
180 }
181 return sum;
182 }
183
184 @Override
185 public String toString() {
186 Cell[] cells = containerRef.get().cells;
187
188 long min = Long.MAX_VALUE;
189 long max = Long.MIN_VALUE;
190 long sum = 0;
191
192 for(Cell cell : cells) {
193 long value = cell.get();
194 sum += value;
195 if(min > value) { min = value; }
196 if(max < value) { max = value; }
197 }
198
199 return new StringBuilder(100)
200 .append("[value=").append(sum)
201 .append(", cells=[length=").append(cells.length)
202 .append(", min=").append(min)
203 .append(", max=").append(max)
204 .append("]]").toString();
205 }
206 }