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.util.concurrent.atomic.AtomicBoolean;
021import java.util.concurrent.atomic.AtomicLongFieldUpdater;
022import java.util.concurrent.atomic.AtomicReference;
023
024import org.apache.yetus.audience.InterfaceAudience;
025
026/**
027 * High scalable counter. Thread safe.
028 * @deprecated use {@link java.util.concurrent.atomic.LongAdder} instead.
029 */
030@InterfaceAudience.Public
031@Deprecated
032public class Counter {
033  private static final int MAX_CELLS_LENGTH = 1 << 20;
034
035  private static class Cell {
036    // Pads are added around the value to avoid cache-line contention with
037    // another cell's value. The cache-line size is expected to be equal to or
038    // less than about 128 Bytes (= 64 Bits * 16).
039
040    @SuppressWarnings("unused")
041    volatile long p0, p1, p2, p3, p4, p5, p6;
042    volatile long value;
043    @SuppressWarnings("unused")
044    volatile long q0, q1, q2, q3, q4, q5, q6;
045
046    static final AtomicLongFieldUpdater<Cell> valueUpdater =
047        AtomicLongFieldUpdater.newUpdater(Cell.class, "value");
048
049    Cell() {}
050
051    Cell(long initValue) {
052      value = initValue;
053    }
054
055    long get() {
056      return value;
057    }
058
059    boolean add(long delta) {
060      long current = value;
061      return valueUpdater.compareAndSet(this, current, current + delta);
062    }
063  }
064
065  private static class Container {
066    /** The length should be a power of 2. */
067    final Cell[] cells;
068
069    /** True if a new extended container is going to replace this. */
070    final AtomicBoolean demoted = new AtomicBoolean();
071
072    Container(Cell cell) {
073      this(new Cell[] { cell });
074    }
075
076    /**
077     * @param cells the length should be a power of 2
078     */
079    Container(Cell[] cells) {
080      this.cells = cells;
081    }
082  }
083
084  private final AtomicReference<Container> containerRef;
085
086  public Counter() {
087    this(new Cell());
088  }
089
090  public Counter(long initValue) {
091    this(new Cell(initValue));
092  }
093
094  private Counter(Cell initCell) {
095    containerRef = new AtomicReference<>(new Container(initCell));
096  }
097
098  private static int hash() {
099    // The logic is borrowed from high-scale-lib's ConcurrentAutoTable.
100
101    int h = System.identityHashCode(Thread.currentThread());
102    // You would think that System.identityHashCode on the current thread
103    // would be a good hash fcn, but actually on SunOS 5.8 it is pretty lousy
104    // in the low bits.
105
106    h ^= (h >>> 20) ^ (h >>> 12); // Bit spreader, borrowed from Doug Lea
107    h ^= (h >>>  7) ^ (h >>>  4);
108    return h;
109  }
110
111  public void add(long delta) {
112    Container container = containerRef.get();
113    Cell[] cells = container.cells;
114    int mask = cells.length - 1;
115
116    int baseIndex = hash();
117    if(cells[baseIndex & mask].add(delta)) {
118      return;
119    }
120
121    int index = baseIndex + 1;
122    while(true) {
123      if(cells[index & mask].add(delta)) {
124        break;
125      }
126      index++;
127    }
128
129    if(index - baseIndex >= cells.length &&
130        cells.length < MAX_CELLS_LENGTH &&
131        container.demoted.compareAndSet(false, true)) {
132
133      if(containerRef.get() == container) {
134        Cell[] newCells = new Cell[cells.length * 2];
135        System.arraycopy(cells, 0, newCells, 0, cells.length);
136        for(int i = cells.length; i < newCells.length; i++) {
137          newCells[i] = new Cell();
138          // Fill all of the elements with instances. Creating a cell on demand
139          // and putting it into the array makes a concurrent problem about
140          // visibility or, in other words, happens-before relation, because
141          // each element of the array is not volatile so that you should
142          // establish the relation by some piggybacking.
143        }
144        containerRef.compareAndSet(container, new Container(newCells));
145      }
146    }
147  }
148
149  public void increment() {
150    add(1);
151  }
152
153  public void decrement() {
154    add(-1);
155  }
156
157  public void set(long value) {
158    containerRef.set(new Container(new Cell(value)));
159  }
160
161  public long get() {
162    long sum = 0;
163    for(Cell cell : containerRef.get().cells) {
164      sum += cell.get();
165    }
166    return sum;
167  }
168
169  @Override
170  public String toString() {
171    Cell[] cells = containerRef.get().cells;
172
173    long min = Long.MAX_VALUE;
174    long max = Long.MIN_VALUE;
175    long sum = 0;
176
177    for(Cell cell : cells) {
178      long value = cell.get();
179      sum += value;
180      if(min > value) { min = value; }
181      if(max < value) { max = value; }
182    }
183
184    return new StringBuilder(100)
185    .append("[value=").append(sum)
186    .append(", cells=[length=").append(cells.length)
187    .append(", min=").append(min)
188    .append(", max=").append(max)
189    .append("]]").toString();
190  }
191}