2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.util;
10 import com.google.common.primitives.UnsignedLong;
11 import java.lang.invoke.MethodHandles;
12 import java.lang.invoke.VarHandle;
15 * Concurrent version of {@link DurationStatisticsTracker}.
17 // TODO: once DurationStatsTracker is gone make this class final
18 class ConcurrentDurationStatisticsTracker extends DurationStatisticsTracker {
19 private static final VarHandle SUM;
20 private static final VarHandle COUNT;
21 private static final VarHandle LONGEST;
22 private static final VarHandle SHORTEST;
25 final var lookup = MethodHandles.lookup();
27 SUM = lookup.findVarHandle(ConcurrentDurationStatisticsTracker.class, "sum", long.class);
28 COUNT = lookup.findVarHandle(ConcurrentDurationStatisticsTracker.class, "count", long.class);
29 LONGEST = lookup.findVarHandle(
30 ConcurrentDurationStatisticsTracker.class, "longest", DurationWithTime.class);
31 SHORTEST = lookup.findVarHandle(
32 ConcurrentDurationStatisticsTracker.class, "shortest", DurationWithTime.class);
33 } catch (NoSuchFieldException | IllegalAccessException e) {
34 throw new ExceptionInInitializerError(e);
38 private volatile long sum;
39 private volatile long count;
40 private volatile DurationWithTime longest;
41 private volatile DurationWithTime shortest;
43 ConcurrentDurationStatisticsTracker() {
48 public final void addDuration(final long duration) {
49 // First update the quick stats
50 SUM.getAndAdd(this, duration);
51 COUNT.getAndAdd(this, 1L);
54 * Now the hairy 'min/max' things. The notion of "now" we cache, so the first time we use it, we do not call it
55 * twice. We populate it lazily, though.
57 * The longest/shortest stats both are encapsulated in an object, so we update them atomically and we minimize
58 * the number of volatile operations.
60 final var currentShortest = (DurationWithTime) SHORTEST.getAcquire(this);
61 if (currentShortest == null || duration < currentShortest.duration()) {
62 updateShortest(currentShortest, duration);
64 final var currentLongest = (DurationWithTime) LONGEST.getAcquire(this);
65 if (currentLongest == null || duration > currentLongest.duration()) {
66 updateLongest(currentLongest, duration);
70 private void updateShortest(final DurationWithTime prev, final long duration) {
71 final var newObj = new DurationWithTime(duration, System.currentTimeMillis());
75 final var witness = (DurationWithTime) SHORTEST.compareAndExchangeRelease(this, expected, newObj);
76 if (witness == expected || duration >= witness.duration()) {
83 private void updateLongest(final DurationWithTime prev, final long duration) {
84 final var newObj = new DurationWithTime(duration, System.currentTimeMillis());
88 final var witness = (DurationWithTime) LONGEST.compareAndExchangeRelease(this, expected, newObj);
89 if (witness == expected || duration <= witness.duration()) {
97 public final long getTotalDurations() {
102 public final double getAverageDuration() {
103 final long myCount = count;
104 return myCount == 0 ? 0 : UnsignedLong.fromLongBits(sum).doubleValue() / myCount;
108 public final synchronized void reset() {
109 // Synchronized is just to make sure we do not have concurrent resets :)
117 protected final DurationWithTime getLongest() {
122 protected final DurationWithTime getShortest() {