package se.l4.vibe.internal.timer; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import se.l4.vibe.percentile.PercentileCounter; import se.l4.vibe.timer.Stopwatch; import se.l4.vibe.timer.Timer; import se.l4.vibe.timer.TimerListener; import se.l4.vibe.timer.TimerSnapshot; /** * Implementation of {@link Timer}. * * @author Andreas Holstenson * */ public class TimerImpl implements Timer { private static final TimerListener[] EMPTY = new TimerListener[0]; private final Lock listenerLock; protected volatile TimerListener[] listeners; private final PercentileCounter counter; private volatile TimerSnapshot lastSample; private final AtomicLong min; private final AtomicLong max; public TimerImpl(PercentileCounter counter) { this.counter = counter; listenerLock = new ReentrantLock(); listeners = EMPTY; min = new AtomicLong(); max = new AtomicLong(); } @Override public void addListener(TimerListener listener) { listenerLock.lock(); try { TimerListener[] listeners = this.listeners; TimerListener[] newListeners = new TimerListener[listeners.length + 1]; System.arraycopy(listeners, 0, newListeners, 0, listeners.length); newListeners[listeners.length] = listener; this.listeners = newListeners; } finally { listenerLock.unlock(); } } @Override public void removeListener(TimerListener listener) { listenerLock.lock(); try { TimerListener[] listeners = this.listeners; int index = -1; for(int i=0, n=listeners.length; i<n; i++) { if(listeners[i] == listener) { index = i; break; } } if(index == -1) { // No such listener, just return return; } TimerListener[] newListeners = new TimerListener[listeners.length - 1]; System.arraycopy(listeners, 0, newListeners, 0, index); if(index < listeners.length - 1) { System.arraycopy(listeners, index + 1, newListeners, index, listeners.length- index - 1); } this.listeners = newListeners; } finally { listenerLock.unlock(); } } private long time() { return System.nanoTime(); } @Override public Stopwatch start() { final long time = time(); return new Stopwatch() { @Override public void stop() { long now = time(); long nowInMs = System.currentTimeMillis(); long total = now - time; counter.add(total); min.updateAndGet(c -> c > total ? total : c); max.updateAndGet(c -> c < total ? total : c); TimerListener[] listeners = TimerImpl.this.listeners; if(listeners.length > 0) { for(TimerListener l : listeners) { l.timerEvent(nowInMs, total); } } } }; } private TimerSnapshot createSample() { return new TimerSnapshotImpl(counter.get(), min.get(), max.get()); } @Override public TimerSnapshot peek() { return createSample(); } @Override public TimerSnapshot read() { return lastSample; } @Override public TimerSnapshot sample() { lastSample = createSample(); counter.reset(); min.set(Long.MAX_VALUE); max.set(0); return lastSample; } }