/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.test.jitter;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReferenceArray;
import static com.hazelcast.test.jitter.JitterRule.AGGREGATION_INTERVAL_MILLIS;
import static com.hazelcast.test.jitter.JitterRule.CAPACITY;
import static com.hazelcast.util.QuickMath.modPowerOfTwo;
public class JitterRecorder {
private final AtomicReferenceArray<Slot> slots = new AtomicReferenceArray<Slot>(CAPACITY);
public void recordPause(long startTimeMillis, long hiccupNanos) {
Slot slot = getSlotForTimestamp(startTimeMillis);
slot.recordHiccup(hiccupNanos);
}
public Iterable<Slot> getSlotsBetween(long from, long to) {
long firstBucket = getBucket(from);
int slotIndex = toSlotIndex(firstBucket);
ArrayList<Slot> result = new ArrayList<Slot>();
long minStartTime = firstBucket * AGGREGATION_INTERVAL_MILLIS;
for (int i = 0; i < CAPACITY; i++) {
Slot slot = slots.get(slotIndex);
if (slot != null && slot.getStartIntervalMillis() >= minStartTime && slot.getStartIntervalMillis() <= to) {
result.add(slot);
}
slotIndex = advanceSlotIndex(slotIndex);
}
return result;
}
private int advanceSlotIndex(int slotIndex) {
slotIndex++;
return modPowerOfTwo(slotIndex, CAPACITY);
}
private Slot getSlotForTimestamp(long startTime) {
//bucket on a linear time-line
long bucket = getBucket(startTime);
//slot in a circular buffer
int slotIndex = toSlotIndex(bucket);
Slot slot = slots.get(slotIndex);
if (isNullOrStale(slot, bucket)) {
slot = newSlot(bucket);
slots.set(slotIndex, slot);
}
return slot;
}
private int toSlotIndex(long bucket) {
return (int) modPowerOfTwo(bucket, CAPACITY);
}
private boolean isNullOrStale(Slot slot, long bucket) {
return slot == null || isStaleSlot(slot, bucket);
}
private long getBucket(long startTime) {
return startTime / AGGREGATION_INTERVAL_MILLIS;
}
private Slot newSlot(long bucket) {
return new Slot(bucket * AGGREGATION_INTERVAL_MILLIS);
}
private boolean isStaleSlot(Slot slot, long currentBucket) {
long slotBucket = getBucket(slot.getStartIntervalMillis());
return slotBucket != currentBucket;
}
}