Skip to content

Commit ef8b30d

Browse files
committed
HADOOP-14276. Add a nanosecond API to Time/Timer/FakeTimer. Contributed by Erik Krogen.
(cherry picked from commit 95b7f1d) (cherry picked from commit c850260)
1 parent 6367bc1 commit ef8b30d

File tree

5 files changed

+52
-31
lines changed

5 files changed

+52
-31
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/LightWeightCache.java

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,6 @@ public int compare(Entry left, Entry right) {
7676
return l > r? 1: l < r? -1: 0;
7777
}
7878
};
79-
80-
/** A clock for measuring time so that it can be mocked in unit tests. */
81-
static class Clock {
82-
/** @return the current time. */
83-
long currentTime() {
84-
return System.nanoTime();
85-
}
86-
}
8779

8880
private static int updateRecommendedLength(int recommendedLength,
8981
int sizeLimit) {
@@ -102,7 +94,7 @@ private static int updateRecommendedLength(int recommendedLength,
10294
private final long creationExpirationPeriod;
10395
private final long accessExpirationPeriod;
10496
private final int sizeLimit;
105-
private final Clock clock;
97+
private final Timer timer;
10698

10799
/**
108100
* @param recommendedLength Recommended size of the internal array.
@@ -120,15 +112,15 @@ public LightWeightCache(final int recommendedLength,
120112
final long creationExpirationPeriod,
121113
final long accessExpirationPeriod) {
122114
this(recommendedLength, sizeLimit,
123-
creationExpirationPeriod, accessExpirationPeriod, new Clock());
115+
creationExpirationPeriod, accessExpirationPeriod, new Timer());
124116
}
125117

126118
@VisibleForTesting
127119
LightWeightCache(final int recommendedLength,
128120
final int sizeLimit,
129121
final long creationExpirationPeriod,
130122
final long accessExpirationPeriod,
131-
final Clock clock) {
123+
final Timer timer) {
132124
super(updateRecommendedLength(recommendedLength, sizeLimit));
133125

134126
this.sizeLimit = sizeLimit;
@@ -147,11 +139,11 @@ public LightWeightCache(final int recommendedLength,
147139

148140
this.queue = new PriorityQueue<Entry>(
149141
sizeLimit > 0? sizeLimit + 1: 1 << 10, expirationTimeComparator);
150-
this.clock = clock;
142+
this.timer = timer;
151143
}
152144

153145
void setExpirationTime(final Entry e, final long expirationPeriod) {
154-
e.setExpirationTime(clock.currentTime() + expirationPeriod);
146+
e.setExpirationTime(timer.monotonicNowNanos() + expirationPeriod);
155147
}
156148

157149
boolean isExpired(final Entry e, final long now) {
@@ -168,7 +160,7 @@ private E evict() {
168160

169161
/** Evict expired entries. */
170162
private void evictExpiredEntries() {
171-
final long now = clock.currentTime();
163+
final long now = timer.monotonicNowNanos();
172164
for(int i = 0; i < EVICTION_LIMIT; i++) {
173165
final Entry peeked = queue.peek();
174166
if (peeked == null || !isExpired(peeked, now)) {

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Time.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ public static long monotonicNow() {
6565
return System.nanoTime() / NANOSECONDS_PER_MILLISECOND;
6666
}
6767

68+
/**
69+
* Same as {@link #monotonicNow()} but returns its result in nanoseconds.
70+
* Note that this is subject to the same resolution constraints as
71+
* {@link System#nanoTime()}.
72+
* @return a monotonic clock that counts in nanoseconds.
73+
*/
74+
public static long monotonicNowNanos() {
75+
return System.nanoTime();
76+
}
77+
6878
/**
6979
* Convert time in millisecond to human readable format.
7080
* @return a human readable string for the input time

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Timer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,14 @@ public long now() {
4848
* @return a monotonic clock that counts in milliseconds.
4949
*/
5050
public long monotonicNow() { return Time.monotonicNow(); }
51+
52+
/**
53+
* Same as {@link #monotonicNow()} but returns its result in nanoseconds.
54+
* Note that this is subject to the same resolution constraints as
55+
* {@link System#nanoTime()}.
56+
* @return a monotonic clock that counts in nanoseconds.
57+
*/
58+
public long monotonicNowNanos() {
59+
return Time.monotonicNowNanos();
60+
}
5161
}

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/FakeTimer.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
package org.apache.hadoop.util;
2020

21+
import java.util.concurrent.TimeUnit;
2122
import org.apache.hadoop.classification.InterfaceAudience;
2223
import org.apache.hadoop.classification.InterfaceStability;
2324

@@ -28,25 +29,38 @@
2829
@InterfaceAudience.Private
2930
@InterfaceStability.Unstable
3031
public class FakeTimer extends Timer {
31-
private long nowMillis;
32+
private long nowNanos;
3233

3334
/** Constructs a FakeTimer with a non-zero value */
3435
public FakeTimer() {
35-
nowMillis = 1000; // Initialize with a non-trivial value.
36+
nowNanos = 1000; // Initialize with a non-trivial value.
3637
}
3738

3839
@Override
3940
public long now() {
40-
return nowMillis;
41+
return TimeUnit.NANOSECONDS.toMillis(nowNanos);
4142
}
4243

4344
@Override
4445
public long monotonicNow() {
45-
return nowMillis;
46+
return TimeUnit.NANOSECONDS.toMillis(nowNanos);
47+
}
48+
49+
@Override
50+
public long monotonicNowNanos() {
51+
return nowNanos;
4652
}
4753

4854
/** Increases the time by milliseconds */
4955
public void advance(long advMillis) {
50-
nowMillis += advMillis;
56+
nowNanos += TimeUnit.MILLISECONDS.toNanos(advMillis);
57+
}
58+
59+
/**
60+
* Increases the time by nanoseconds.
61+
* @param advNanos Nanoseconds to advance by.
62+
*/
63+
public void advanceNanos(long advNanos) {
64+
nowNanos += advNanos;
5165
}
5266
}

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestLightWeightCache.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ private static class LightWeightCacheTestCase implements GSet<IntEntry, IntEntry
213213
int iterate_count = 0;
214214
int contain_count = 0;
215215

216-
private long currentTestTime = ran.nextInt();
216+
private FakeTimer fakeTimer = new FakeTimer();
217217

218218
LightWeightCacheTestCase(int tablelength, int sizeLimit,
219219
long creationExpirationPeriod, long accessExpirationPeriod,
@@ -230,12 +230,7 @@ private static class LightWeightCacheTestCase implements GSet<IntEntry, IntEntry
230230

231231
data = new IntData(datasize, modulus);
232232
cache = new LightWeightCache<IntEntry, IntEntry>(tablelength, sizeLimit,
233-
creationExpirationPeriod, 0, new LightWeightCache.Clock() {
234-
@Override
235-
long currentTime() {
236-
return currentTestTime;
237-
}
238-
});
233+
creationExpirationPeriod, 0, fakeTimer);
239234

240235
Assert.assertEquals(0, cache.size());
241236
}
@@ -247,7 +242,7 @@ private boolean containsTest(IntEntry key) {
247242
} else {
248243
final IntEntry h = hashMap.remove(key);
249244
if (h != null) {
250-
Assert.assertTrue(cache.isExpired(h, currentTestTime));
245+
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
251246
}
252247
}
253248
return c;
@@ -266,7 +261,7 @@ private IntEntry getTest(IntEntry key) {
266261
} else {
267262
final IntEntry h = hashMap.remove(key);
268263
if (h != null) {
269-
Assert.assertTrue(cache.isExpired(h, currentTestTime));
264+
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
270265
}
271266
}
272267
return c;
@@ -286,7 +281,7 @@ private IntEntry putTest(IntEntry entry) {
286281
final IntEntry h = hashMap.put(entry);
287282
if (h != null && h != entry) {
288283
// if h == entry, its expiration time is already updated
289-
Assert.assertTrue(cache.isExpired(h, currentTestTime));
284+
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
290285
}
291286
}
292287
return c;
@@ -305,7 +300,7 @@ private IntEntry removeTest(IntEntry key) {
305300
} else {
306301
final IntEntry h = hashMap.remove(key);
307302
if (h != null) {
308-
Assert.assertTrue(cache.isExpired(h, currentTestTime));
303+
Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
309304
}
310305
}
311306
return c;
@@ -339,7 +334,7 @@ boolean tossCoin() {
339334
}
340335

341336
void check() {
342-
currentTestTime += ran.nextInt() & 0x3;
337+
fakeTimer.advanceNanos(ran.nextInt() & 0x3);
343338

344339
//test size
345340
sizeTest();

0 commit comments

Comments
 (0)