HADOOP-14276. Add a nanosecond API to Time/Timer/FakeTimer. Contributed by Erik Krogen.
(cherry picked from commit 95b7f1d29a
)
This commit is contained in:
parent
a467e5412e
commit
c85026038a
|
@ -77,14 +77,6 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A clock for measuring time so that it can be mocked in unit tests. */
|
|
||||||
static class Clock {
|
|
||||||
/** @return the current time. */
|
|
||||||
long currentTime() {
|
|
||||||
return System.nanoTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int updateRecommendedLength(int recommendedLength,
|
private static int updateRecommendedLength(int recommendedLength,
|
||||||
int sizeLimit) {
|
int sizeLimit) {
|
||||||
return sizeLimit > 0 && sizeLimit < recommendedLength?
|
return sizeLimit > 0 && sizeLimit < recommendedLength?
|
||||||
|
@ -102,7 +94,7 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
|
||||||
private final long creationExpirationPeriod;
|
private final long creationExpirationPeriod;
|
||||||
private final long accessExpirationPeriod;
|
private final long accessExpirationPeriod;
|
||||||
private final int sizeLimit;
|
private final int sizeLimit;
|
||||||
private final Clock clock;
|
private final Timer timer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param recommendedLength Recommended size of the internal array.
|
* @param recommendedLength Recommended size of the internal array.
|
||||||
|
@ -120,7 +112,7 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
|
||||||
final long creationExpirationPeriod,
|
final long creationExpirationPeriod,
|
||||||
final long accessExpirationPeriod) {
|
final long accessExpirationPeriod) {
|
||||||
this(recommendedLength, sizeLimit,
|
this(recommendedLength, sizeLimit,
|
||||||
creationExpirationPeriod, accessExpirationPeriod, new Clock());
|
creationExpirationPeriod, accessExpirationPeriod, new Timer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -128,7 +120,7 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
|
||||||
final int sizeLimit,
|
final int sizeLimit,
|
||||||
final long creationExpirationPeriod,
|
final long creationExpirationPeriod,
|
||||||
final long accessExpirationPeriod,
|
final long accessExpirationPeriod,
|
||||||
final Clock clock) {
|
final Timer timer) {
|
||||||
super(updateRecommendedLength(recommendedLength, sizeLimit));
|
super(updateRecommendedLength(recommendedLength, sizeLimit));
|
||||||
|
|
||||||
this.sizeLimit = sizeLimit;
|
this.sizeLimit = sizeLimit;
|
||||||
|
@ -147,11 +139,11 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
|
||||||
|
|
||||||
this.queue = new PriorityQueue<Entry>(
|
this.queue = new PriorityQueue<Entry>(
|
||||||
sizeLimit > 0? sizeLimit + 1: 1 << 10, expirationTimeComparator);
|
sizeLimit > 0? sizeLimit + 1: 1 << 10, expirationTimeComparator);
|
||||||
this.clock = clock;
|
this.timer = timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setExpirationTime(final Entry e, final long expirationPeriod) {
|
void setExpirationTime(final Entry e, final long expirationPeriod) {
|
||||||
e.setExpirationTime(clock.currentTime() + expirationPeriod);
|
e.setExpirationTime(timer.monotonicNowNanos() + expirationPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isExpired(final Entry e, final long now) {
|
boolean isExpired(final Entry e, final long now) {
|
||||||
|
@ -168,7 +160,7 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
|
||||||
|
|
||||||
/** Evict expired entries. */
|
/** Evict expired entries. */
|
||||||
private void evictExpiredEntries() {
|
private void evictExpiredEntries() {
|
||||||
final long now = clock.currentTime();
|
final long now = timer.monotonicNowNanos();
|
||||||
for(int i = 0; i < EVICTION_LIMIT; i++) {
|
for(int i = 0; i < EVICTION_LIMIT; i++) {
|
||||||
final Entry peeked = queue.peek();
|
final Entry peeked = queue.peek();
|
||||||
if (peeked == null || !isExpired(peeked, now)) {
|
if (peeked == null || !isExpired(peeked, now)) {
|
||||||
|
|
|
@ -65,6 +65,16 @@ public final class Time {
|
||||||
return System.nanoTime() / NANOSECONDS_PER_MILLISECOND;
|
return System.nanoTime() / NANOSECONDS_PER_MILLISECOND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #monotonicNow()} but returns its result in nanoseconds.
|
||||||
|
* Note that this is subject to the same resolution constraints as
|
||||||
|
* {@link System#nanoTime()}.
|
||||||
|
* @return a monotonic clock that counts in nanoseconds.
|
||||||
|
*/
|
||||||
|
public static long monotonicNowNanos() {
|
||||||
|
return System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert time in millisecond to human readable format.
|
* Convert time in millisecond to human readable format.
|
||||||
* @return a human readable string for the input time
|
* @return a human readable string for the input time
|
||||||
|
|
|
@ -48,4 +48,14 @@ public class Timer {
|
||||||
* @return a monotonic clock that counts in milliseconds.
|
* @return a monotonic clock that counts in milliseconds.
|
||||||
*/
|
*/
|
||||||
public long monotonicNow() { return Time.monotonicNow(); }
|
public long monotonicNow() { return Time.monotonicNow(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #monotonicNow()} but returns its result in nanoseconds.
|
||||||
|
* Note that this is subject to the same resolution constraints as
|
||||||
|
* {@link System#nanoTime()}.
|
||||||
|
* @return a monotonic clock that counts in nanoseconds.
|
||||||
|
*/
|
||||||
|
public long monotonicNowNanos() {
|
||||||
|
return Time.monotonicNowNanos();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.apache.hadoop.util;
|
package org.apache.hadoop.util;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
|
||||||
|
@ -28,25 +29,38 @@ import org.apache.hadoop.classification.InterfaceStability;
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
@InterfaceStability.Unstable
|
@InterfaceStability.Unstable
|
||||||
public class FakeTimer extends Timer {
|
public class FakeTimer extends Timer {
|
||||||
private long nowMillis;
|
private long nowNanos;
|
||||||
|
|
||||||
/** Constructs a FakeTimer with a non-zero value */
|
/** Constructs a FakeTimer with a non-zero value */
|
||||||
public FakeTimer() {
|
public FakeTimer() {
|
||||||
nowMillis = 1000; // Initialize with a non-trivial value.
|
nowNanos = 1000; // Initialize with a non-trivial value.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long now() {
|
public long now() {
|
||||||
return nowMillis;
|
return TimeUnit.NANOSECONDS.toMillis(nowNanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long monotonicNow() {
|
public long monotonicNow() {
|
||||||
return nowMillis;
|
return TimeUnit.NANOSECONDS.toMillis(nowNanos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long monotonicNowNanos() {
|
||||||
|
return nowNanos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increases the time by milliseconds */
|
/** Increases the time by milliseconds */
|
||||||
public void advance(long advMillis) {
|
public void advance(long advMillis) {
|
||||||
nowMillis += advMillis;
|
nowNanos += TimeUnit.MILLISECONDS.toNanos(advMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increases the time by nanoseconds.
|
||||||
|
* @param advNanos Nanoseconds to advance by.
|
||||||
|
*/
|
||||||
|
public void advanceNanos(long advNanos) {
|
||||||
|
nowNanos += advNanos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,7 +213,7 @@ public class TestLightWeightCache {
|
||||||
int iterate_count = 0;
|
int iterate_count = 0;
|
||||||
int contain_count = 0;
|
int contain_count = 0;
|
||||||
|
|
||||||
private long currentTestTime = ran.nextInt();
|
private FakeTimer fakeTimer = new FakeTimer();
|
||||||
|
|
||||||
LightWeightCacheTestCase(int tablelength, int sizeLimit,
|
LightWeightCacheTestCase(int tablelength, int sizeLimit,
|
||||||
long creationExpirationPeriod, long accessExpirationPeriod,
|
long creationExpirationPeriod, long accessExpirationPeriod,
|
||||||
|
@ -230,12 +230,7 @@ public class TestLightWeightCache {
|
||||||
|
|
||||||
data = new IntData(datasize, modulus);
|
data = new IntData(datasize, modulus);
|
||||||
cache = new LightWeightCache<IntEntry, IntEntry>(tablelength, sizeLimit,
|
cache = new LightWeightCache<IntEntry, IntEntry>(tablelength, sizeLimit,
|
||||||
creationExpirationPeriod, 0, new LightWeightCache.Clock() {
|
creationExpirationPeriod, 0, fakeTimer);
|
||||||
@Override
|
|
||||||
long currentTime() {
|
|
||||||
return currentTestTime;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Assert.assertEquals(0, cache.size());
|
Assert.assertEquals(0, cache.size());
|
||||||
}
|
}
|
||||||
|
@ -247,7 +242,7 @@ public class TestLightWeightCache {
|
||||||
} else {
|
} else {
|
||||||
final IntEntry h = hashMap.remove(key);
|
final IntEntry h = hashMap.remove(key);
|
||||||
if (h != null) {
|
if (h != null) {
|
||||||
Assert.assertTrue(cache.isExpired(h, currentTestTime));
|
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
|
@ -266,7 +261,7 @@ public class TestLightWeightCache {
|
||||||
} else {
|
} else {
|
||||||
final IntEntry h = hashMap.remove(key);
|
final IntEntry h = hashMap.remove(key);
|
||||||
if (h != null) {
|
if (h != null) {
|
||||||
Assert.assertTrue(cache.isExpired(h, currentTestTime));
|
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
|
@ -286,7 +281,7 @@ public class TestLightWeightCache {
|
||||||
final IntEntry h = hashMap.put(entry);
|
final IntEntry h = hashMap.put(entry);
|
||||||
if (h != null && h != entry) {
|
if (h != null && h != entry) {
|
||||||
// if h == entry, its expiration time is already updated
|
// if h == entry, its expiration time is already updated
|
||||||
Assert.assertTrue(cache.isExpired(h, currentTestTime));
|
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
|
@ -305,7 +300,7 @@ public class TestLightWeightCache {
|
||||||
} else {
|
} else {
|
||||||
final IntEntry h = hashMap.remove(key);
|
final IntEntry h = hashMap.remove(key);
|
||||||
if (h != null) {
|
if (h != null) {
|
||||||
Assert.assertTrue(cache.isExpired(h, currentTestTime));
|
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
|
@ -339,7 +334,7 @@ public class TestLightWeightCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
void check() {
|
void check() {
|
||||||
currentTestTime += ran.nextInt() & 0x3;
|
fakeTimer.advanceNanos(ran.nextInt() & 0x3);
|
||||||
|
|
||||||
//test size
|
//test size
|
||||||
sizeTest();
|
sizeTest();
|
||||||
|
|
Loading…
Reference in New Issue