package com.hwlcn.ldap.util;
import com.hwlcn.core.annotation.ThreadSafety;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import static com.hwlcn.ldap.util.Debug.*;
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class FixedRateBarrier
implements Serializable
{
private static final long serialVersionUID = -3490156685189909611L;
private static final long minSleepMillis;
static
{
final List<Long> minSleepMillisMeasurements = new ArrayList<Long>();
for (int i = 0; i < 11; i++)
{
final long timeBefore = System.currentTimeMillis();
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
debugException(e);
}
final long sleepMillis = System.currentTimeMillis() - timeBefore;
minSleepMillisMeasurements.add(sleepMillis);
}
Collections.sort(minSleepMillisMeasurements);
final long medianSleepMillis = minSleepMillisMeasurements.get(
minSleepMillisMeasurements.size()/2);
minSleepMillis = Math.max(medianSleepMillis, 1);
final String message = "Calibrated FixedRateBarrier to use " +
"minSleepMillis=" + minSleepMillis + ". " +
"Minimum sleep measurements = " + minSleepMillisMeasurements;
debug(Level.INFO, DebugType.OTHER, message);
}
private final long intervalDurationNanos;
private final double millisBetweenIterations;
private final int perInterval;
private volatile boolean shutdownRequested = false;
private long countInThisInterval = 0;
private long intervalStartNanos = 0;
private long intervalEndNanos = 0;
public FixedRateBarrier(final long intervalDurationMs, final int perInterval)
{
Validator.ensureTrue(intervalDurationMs > 0,
"FixedRateBarrier.intervalDurationMs must be at least 1.");
Validator.ensureTrue(perInterval > 0,
"FixedRateBarrier.perInterval must be at least 1.");
this.perInterval = perInterval;
intervalDurationNanos = 1000L * 1000L * intervalDurationMs;
millisBetweenIterations = (double)intervalDurationMs/(double)perInterval;
}
public synchronized boolean await()
{
while (!shutdownRequested)
{
final long now = System.nanoTime();
if ((intervalStartNanos == 0) ||
(now < intervalStartNanos))
{
intervalStartNanos = now;
intervalEndNanos = intervalStartNanos + intervalDurationNanos;
}
else if (now >= intervalEndNanos)
{
countInThisInterval = 0;
if (now < (intervalEndNanos + intervalDurationNanos))
{
intervalStartNanos = now;
}
else
{
intervalStartNanos = intervalEndNanos;
}
intervalEndNanos = intervalStartNanos + intervalDurationNanos;
}
final long intervalRemaining = intervalEndNanos - now;
if (intervalRemaining <= 0)
{
continue;
}
final double intervalFractionRemaining =
(double) intervalRemaining / intervalDurationNanos;
final double expectedRemaining = intervalFractionRemaining * perInterval;
final long actualRemaining = perInterval - countInThisInterval;
if (actualRemaining >= expectedRemaining)
{
countInThisInterval++;
break;
}
else
{
final double gapIterations = expectedRemaining - actualRemaining;
final double remainingMillis =
millisBetweenIterations * gapIterations;
if (remainingMillis >= minSleepMillis)
{
try
{
Thread.sleep((long)remainingMillis);
}
catch (InterruptedException e)
{
debugException(e);
}
}
else
{
Thread.yield();
}
}
}
return shutdownRequested;
}
public void shutdownRequested()
{
shutdownRequested = true;
}
public boolean isShutdownRequested()
{
return shutdownRequested;
}
}