View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.util.ratestatistics;
18  
19  import java.util.concurrent.TimeUnit;
20  import java.util.concurrent.atomic.AtomicLong;
21  
22  /***
23   * A thread-safe rate statistic implementation.
24   *
25   * @author Chris Dennis
26   */
27  public class AtomicRateStatistic extends AbstractRateStatistic {
28  
29    private static final int SAMPLE_TIME_FLAG_BITS = 1;
30    private static final long CALCULATION_FLAG = 0x1L;
31  
32    private final AtomicLong count = new AtomicLong(0L);
33    private final AtomicLong rateSampleTime = new AtomicLong(System.nanoTime() << SAMPLE_TIME_FLAG_BITS);
34  
35    private volatile float rateSample = Float.NaN;
36  
37    private volatile long sampleRateMask;
38    private volatile long previousSample;
39  
40    /***
41     * Create an AtomicRateStatistic instance with the given average period.
42     *
43     * @param averagePeriod average period
44     * @param unit period time unit
45     */
46    public AtomicRateStatistic(long averagePeriod, TimeUnit unit) {
47      super(averagePeriod, unit);
48    }
49  
50    /***
51     * {@inheritDoc}
52     */
53    public void event() {
54      long value = count.incrementAndGet();
55      if ((value & sampleRateMask) == 0L) {
56        long now = System.nanoTime();
57        long previous = startIncrementTime(now);
58        try {
59          float nowRate = ((float) (value - previousSample)) / (now - previous);
60          rateSample = iterateMovingAverage(nowRate, now, rateSample, previous);
61          previousSample = value;
62          long suggestedSampleRateMask = Long.highestOneBit(Math.max(1L, (long) (getRateAveragePeriod() * rateSample))) - 1;
63          if (suggestedSampleRateMask != sampleRateMask) {
64            sampleRateMask = suggestedSampleRateMask;
65          }
66        } finally {
67          finishIncrementTime(now);
68        }
69      }
70    }
71  
72    /***
73     * {@inheritDoc}
74     */
75    public long getCount() {
76      return count.get();
77    }
78  
79    /***
80     * {@inheritDoc}
81     */
82    public float getRate() {
83      long then;
84      long lastSample;
85      float thenAverage;
86      do {
87        then = startReadTime();
88        lastSample = previousSample;
89        thenAverage = rateSample;
90      } while (!validateTimeRead(then));
91  
92      long now = System.nanoTime();
93      float nowValue = ((float) (count.get() - lastSample)) / (now - then);
94  
95      final float rate = iterateMovingAverage(nowValue, now, thenAverage, then) * TimeUnit.SECONDS.toNanos(1);
96      if (Float.isNaN(rate)) {
97        if (Float.isNaN(thenAverage)) {
98          return 0f;
99        } else {
100         return thenAverage;
101       }
102     } else {
103       return rate;
104     }
105   }
106 
107   private long startIncrementTime(long newTime) {
108     while (true) {
109       long current = rateSampleTime.get();
110       if (((current & CALCULATION_FLAG) == 0) && rateSampleTime.compareAndSet(current, (newTime << SAMPLE_TIME_FLAG_BITS) | CALCULATION_FLAG)) {
111         return current >>> SAMPLE_TIME_FLAG_BITS;
112       }
113     }
114   }
115 
116   private void finishIncrementTime(long value) {
117     if (!rateSampleTime.compareAndSet((value << SAMPLE_TIME_FLAG_BITS) | CALCULATION_FLAG, value << SAMPLE_TIME_FLAG_BITS)) {
118       throw new AssertionError();
119     }
120   }
121 
122   private long startReadTime() {
123     while (true) {
124       long current = rateSampleTime.get();
125       if ((current & CALCULATION_FLAG) == 0) {
126         return current >>> SAMPLE_TIME_FLAG_BITS;
127       }
128     }
129   }
130 
131   private boolean validateTimeRead(long current) {
132     return rateSampleTime.get() == (current << SAMPLE_TIME_FLAG_BITS);
133   }
134 }