/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.util; import com.google.common.primitives.UnsignedLong; import java.util.concurrent.atomic.AtomicLongFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; /** * Concurrent version of {@link DurationStatisticsTracker}. */ // TODO: once DurationStatsTracker is gone make this class final class ConcurrentDurationStatisticsTracker extends DurationStatisticsTracker { private static final AtomicReferenceFieldUpdater<ConcurrentDurationStatisticsTracker, DurationWithTime> LONGEST_UPDATER = AtomicReferenceFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, DurationWithTime.class, "longest"); private static final AtomicReferenceFieldUpdater<ConcurrentDurationStatisticsTracker, DurationWithTime> SHORTEST_UPDATER = AtomicReferenceFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, DurationWithTime.class, "shortest"); private static final AtomicLongFieldUpdater<ConcurrentDurationStatisticsTracker> COUNT_UPDATER = AtomicLongFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, "count"); private static final AtomicLongFieldUpdater<ConcurrentDurationStatisticsTracker> SUM_UPDATER = AtomicLongFieldUpdater.newUpdater(ConcurrentDurationStatisticsTracker.class, "sum"); private volatile long sum = 0; private volatile long count = 0; private volatile DurationWithTime longest = null; private volatile DurationWithTime shortest = null; ConcurrentDurationStatisticsTracker() { // Hidden on purpose } @Override public final void addDuration(final long duration) { // First update the quick stats SUM_UPDATER.addAndGet(this, duration); COUNT_UPDATER.incrementAndGet(this); /* * Now the hairy 'min/max' things. The notion of "now" we cache, * so the first time we use it, we do not call it twice. We populate * it lazily, though. * * The longest/shortest stats both are encapsulated in an object, * so we update them atomically and we minimize the number of volatile * operations. */ DurationWithTime current = shortest; if (current == null || duration < current.getDuration()) { final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis()); while (!SHORTEST_UPDATER.weakCompareAndSet(this, current, newObj)) { current = shortest; if (current != null && duration >= current.getDuration()) { break; } } } current = longest; if (current == null || duration > current.getDuration()) { final DurationWithTime newObj = new DurationWithTime(duration, System.currentTimeMillis()); while (!LONGEST_UPDATER.weakCompareAndSet(this, current, newObj)) { current = longest; if (current != null && duration <= current.getDuration()) { break; } } } } @Override public final long getTotalDurations() { return count; } @Override public final double getAverageDuration() { final long myCount = count; return myCount == 0 ? 0 : UnsignedLong.fromLongBits(sum).doubleValue() / myCount; } @Override public final synchronized void reset() { // Synchronized is just to make sure we do not have concurrent resets :) longest = null; shortest = null; count = 0; sum = 0; } @Override protected final DurationWithTime getLongest() { return longest; } @Override protected final DurationWithTime getShortest() { return shortest; } }