blob: 02e62bf45c9728163aa7d9e65f7921069373e794 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
toma7083182014-09-25 21:38:03 -070019package org.onlab.util;
20
21import java.util.Objects;
22
23import static com.google.common.base.MoreObjects.toStringHelper;
24import static com.google.common.base.Preconditions.checkArgument;
25
26/**
27 * Counting mechanism capable of tracking occurrences and rates.
28 */
29public class Counter {
30
31 private long total = 0;
32 private long start = System.currentTimeMillis();
33 private long end = 0;
34
35 /**
36 * Creates a new counter.
37 */
38 public Counter() {
39 }
40
41 /**
42 * Creates a new counter in a specific state. If non-zero end time is
43 * specified, the counter will be frozen.
44 *
45 * @param start start time
46 * @param total total number of items to start with
47 * @param end end time; if non-ze
48 */
49 public Counter(long start, long total, long end) {
50 checkArgument(start <= end, "Malformed interval: start > end");
51 checkArgument(total >= 0, "Total must be non-negative");
52 this.start = start;
53 this.total = total;
54 this.end = end;
55 }
56
57 /**
58 * Resets the counter, by zeroing out the count and restarting the timer.
59 */
60 public synchronized void reset() {
61 end = 0;
62 total = 0;
63 start = System.currentTimeMillis();
64 }
65
66 /**
67 * Freezes the counter in the current state including the counts and times.
68 */
69 public synchronized void freeze() {
70 end = System.currentTimeMillis();
71 }
72
73 /**
74 * Adds the specified number of occurrences to the counter. No-op if the
75 * counter has been frozen.
76 *
77 * @param count number of occurrences
78 */
79 public synchronized void add(long count) {
80 checkArgument(count >= 0, "Count must be non-negative");
81 if (end == 0L) {
82 total += count;
83 }
84 }
85
86 /**
87 * Returns the number of occurrences per second.
88 *
89 * @return throughput in occurrences per second
90 */
91 public synchronized double throughput() {
92 return total / duration();
93 }
94
95 /**
96 * Returns the total number of occurrences counted.
97 *
98 * @return number of counted occurrences
99 */
100 public synchronized long total() {
101 return total;
102 }
103
104 /**
105 * Returns the duration expressed in fractional number of seconds.
106 *
107 * @return fractional number of seconds since the last reset
108 */
109 public synchronized double duration() {
110 // Protect against 0 return by artificially setting duration to 1ms
111 long duration = (end == 0L ? System.currentTimeMillis() : end) - start;
112 return (duration == 0 ? 1 : duration) / 1000.0;
113 }
114
115 @Override
116 public int hashCode() {
117 return Objects.hash(total, start, end);
118 }
119
120 @Override
121 public boolean equals(Object obj) {
122 if (this == obj) {
123 return true;
124 }
125 if (obj instanceof Counter) {
126 final Counter other = (Counter) obj;
127 return Objects.equals(this.total, other.total) &&
128 Objects.equals(this.start, other.start) &&
129 Objects.equals(this.end, other.end);
130 }
131 return false;
132 }
133
134 @Override
135 public String toString() {
136 return toStringHelper(this)
137 .add("total", total)
138 .add("start", start)
139 .add("end", end)
140 .toString();
141 }
142}