1 /**
2  *  Copyright 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(getTime() << SAMPLE_TIME_FLAG_BITS);
34
35   private volatile float rateSample = 0.0f;
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 = getTime();
57       long previous = startIncrementTime(now);
58       try {
59         if (now != previous && value > previousSample) {
60           float nowRate = ((float)(value - previousSample)) / (now - previous);
61           rateSample = iterateMovingAverage(nowRate, now, rateSample, previous);
62           previousSample = value;
63           long suggestedSampleRateMask = Long.highestOneBit(Math.max(1L, (long)(getRateAveragePeriod() * rateSample))) - 1;
64           if (suggestedSampleRateMask != sampleRateMask) {
65             sampleRateMask = suggestedSampleRateMask;
66           }
67         }
68       } finally {
69         finishIncrementTime(now);
70       }
71     }
72   }
73
74   /**
75    * {@inheritDoc}
76    */

77   public long getCount() {
78     return count.get();
79   }
80
81   /**
82    * {@inheritDoc}
83    */

84   public float getRate() {
85     long then;
86     long lastSample;
87     float thenAverage;
88     do {
89       then = startReadTime();
90       lastSample = previousSample;
91       thenAverage = rateSample;
92     } while (!validateTimeRead(then));
93
94     long now = getTime();
95     if (now == then) {
96       return thenAverage;
97     } else {
98       float nowValue = ((float) (count.get() - lastSample)) / (now - then);
99       final float rate = iterateMovingAverage(nowValue, now, thenAverage, then) * TimeUnit.SECONDS.toNanos(1);
100       if (Float.isNaN(rate)) {
101         if (Float.isNaN(thenAverage)) {
102           return 0f;
103         } else {
104           return thenAverage;
105         }
106       } else {
107         return rate;
108       }
109     }
110   }
111
112   private long startIncrementTime(long newTime) {
113     while (true) {
114       long current = rateSampleTime.get();
115       if (((current & CALCULATION_FLAG) == 0) && rateSampleTime.compareAndSet(current, (newTime << SAMPLE_TIME_FLAG_BITS) | CALCULATION_FLAG)) {
116         return current >>> SAMPLE_TIME_FLAG_BITS;
117       }
118     }
119   }
120
121   private void finishIncrementTime(long value) {
122     if (!rateSampleTime.compareAndSet((value << SAMPLE_TIME_FLAG_BITS) | CALCULATION_FLAG, value << SAMPLE_TIME_FLAG_BITS)) {
123       throw new AssertionError();
124     }
125   }
126
127   private long startReadTime() {
128     while (true) {
129       long current = rateSampleTime.get();
130       if ((current & CALCULATION_FLAG) == 0) {
131         return current >>> SAMPLE_TIME_FLAG_BITS;
132       }
133     }
134   }
135
136   private boolean validateTimeRead(long current) {
137     return rateSampleTime.get() == (current << SAMPLE_TIME_FLAG_BITS);
138   }
139
140
141   private static long getTime() {
142     return System.nanoTime();
143   }
144 }
145