package org.sef4j.callstack.stats; import java.lang.reflect.Field; import java.util.concurrent.Callable; import org.sef4j.callstack.CallStackElt; import org.sef4j.core.util.ICopySupport; /** * perf counter for pending thread in a section * * this class is multi-thread safe, and lock-FREE! */ @SuppressWarnings("restriction") public class PendingPerfCount implements ICopySupport<PendingPerfCount> { /** * count of currently pending threads */ private int pendingCount; /** * sum of startTime in nanos for all currently pending threads. * notice that will probably overflow long (2^64 bits), so the value is "correct modulo 2^64" * * to compute average until given timeNow, use <code>(pendingCount * timeNow - pendingSumStartTime) / pendingCount</code> * see getPendingAverageTimeNanosUntilTime(timeNow) */ private long pendingSumStartTime; // ------------------------------------------------------------------------ public PendingPerfCount() { } public PendingPerfCount(PendingPerfCount src) { set(src); } public static final Callable<PendingPerfCount> FACTORY = new Callable<PendingPerfCount>() { @Override public PendingPerfCount call() throws Exception { return new PendingPerfCount(); } }; // ------------------------------------------------------------------------ public int getPendingCount() { return UNSAFE.getIntVolatile(this, pendingCountFieldOffset); } public long getPendingSumStartTime() { return UNSAFE.getLongVolatile(this, pendingSumStartTimeFieldOffset); } public long getPendingAverageTimeNanosUntilTime(long timeNanos) { int count = getPendingCount(); if (count == 0) return 0; long sumStart = getPendingSumStartTime(); long avg = (count * timeNanos - sumStart) / count; return avg; } public long getPendingAverageTimeMillisUntilTime(long timeNanos) { long avgNanos = getPendingAverageTimeNanosUntilTime(timeNanos); return ThreadTimeUtils.nanosToMillis(avgNanos); } @Override /* java.lang.Object */ public PendingPerfCount clone() { return copy(); } @Override /* ICopySupport<> */ public PendingPerfCount copy() { return new PendingPerfCount(this); } public void set(PendingPerfCount src) { this.pendingCount = src.getPendingCount(); this.pendingSumStartTime = src.getPendingSumStartTime(); } public void clear() { UNSAFE.getAndSetInt(this, pendingCountFieldOffset, 0); UNSAFE.getAndSetLong(this, pendingSumStartTimeFieldOffset, 0L); } public void incr(PendingPerfCount src) { int incrCount = src.getPendingCount(); long incrPendingSum = src.getPendingSumStartTime(); UNSAFE.getAndAddInt(this, pendingCountFieldOffset, incrCount); UNSAFE.getAndAddLong(this, pendingSumStartTimeFieldOffset, incrPendingSum); } // ------------------------------------------------------------------------ public void addPending(long startTimeMillis) { UNSAFE.getAndAddInt(this, pendingCountFieldOffset, 1); UNSAFE.getAndAddLong(this, pendingSumStartTimeFieldOffset, startTimeMillis); } public void removePending(long startTimeMillis) { UNSAFE.getAndAddInt(this, pendingCountFieldOffset, -1); UNSAFE.getAndAddLong(this, pendingSumStartTimeFieldOffset, -startTimeMillis); } // Helper method using StackElt start/end times // ------------------------------------------------------------------------ public void addPending(CallStackElt stackElt) { addPending(stackElt.getStartTime()); } public void removePending(CallStackElt stackElt) { removePending(stackElt.getStartTime()); } // ------------------------------------------------------------------------ @Override public String toString() { final int count = pendingCount; if (count == 0) return "PendingPerfCounts[]"; final long sum = this.pendingSumStartTime; long timeNow = ThreadTimeUtils.getTime(); long avgMillisUntilNow = getPendingAverageTimeMillisUntilTime(timeNow); return "PendingPerfCounts[" + "count:" + count + "sum:" + sum + ", avgMillis:" + avgMillisUntilNow + " ms until now:" + timeNow + "]"; } // internal for UNSAFE // ------------------------------------------------------------------------ private static final sun.misc.Unsafe UNSAFE; private static final long pendingCountFieldOffset; private static final long pendingSumStartTimeFieldOffset; static { UNSAFE = UnsafeUtils.getUnsafe(); Class<PendingPerfCount> thisClass = PendingPerfCount.class; Field pendingCountField = getField(thisClass, "pendingCount"); pendingCountFieldOffset = UNSAFE.objectFieldOffset(pendingCountField); Field pendingSumStartTimeField = getField(thisClass, "pendingSumStartTime"); pendingSumStartTimeFieldOffset = UNSAFE.objectFieldOffset(pendingSumStartTimeField); } private static Field getField(Class<?> clss, String name) { Field[] fields = clss.getDeclaredFields(); for(Field f : fields) { if (f.getName().equals(name)) { return f; } } return null; } }