Browse Source

HADOOP-14276. Add a nanosecond API to Time/Timer/FakeTimer. Contributed by Erik Krogen.

(cherry picked from commit 95b7f1d29a5e2dadd70a56fca5faa006c5bd74fc)
(cherry picked from commit c85026038a6ef8d38b7a8563fbb64ac6edd1d4ce)
(cherry picked from commit ef8b30dd4beb8baf59f70e81ee938d7852922a42)
Zhe Zhang 8 years ago
parent
commit
e5d5d0ddb5

+ 3 - 0
hadoop-common-project/hadoop-common/CHANGES.txt

@@ -29,6 +29,9 @@ Release 2.7.4 - UNRELEASED
     HADOOP-13742. Expose "NumOpenConnectionsPerUser" as a metric.
     (Brahma Reddy Battula via kihwal)
 
+    HADOOP-14276. Add a nanosecond API to Time/Timer/FakeTimer.
+    (Erik Krogen via zhz)
+
   OPTIMIZATIONS
 
     HADOOP-14138. Remove S3A ref from META-INF service discovery, rely on

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

@@ -76,14 +76,6 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
       return l > r? 1: l < r? -1: 0;
     }
   };
-
-  /** 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,
       int sizeLimit) {
@@ -102,7 +94,7 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
   private final long creationExpirationPeriod;
   private final long accessExpirationPeriod;
   private final int sizeLimit;
-  private final Clock clock;
+  private final Timer timer;
 
   /**
    * @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 accessExpirationPeriod) {
     this(recommendedLength, sizeLimit,
-        creationExpirationPeriod, accessExpirationPeriod, new Clock());
+        creationExpirationPeriod, accessExpirationPeriod, new Timer());
   }
 
   @VisibleForTesting
@@ -128,7 +120,7 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
       final int sizeLimit,
       final long creationExpirationPeriod,
       final long accessExpirationPeriod,
-      final Clock clock) {
+      final Timer timer) {
     super(updateRecommendedLength(recommendedLength, sizeLimit));
 
     this.sizeLimit = sizeLimit;
@@ -147,11 +139,11 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
 
     this.queue = new PriorityQueue<Entry>(
         sizeLimit > 0? sizeLimit + 1: 1 << 10, expirationTimeComparator);
-    this.clock = clock;
+    this.timer = timer;
   }
 
   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) {
@@ -168,7 +160,7 @@ public class LightWeightCache<K, E extends K> extends LightWeightGSet<K, E> {
 
   /** Evict expired entries. */
   private void evictExpiredEntries() {
-    final long now = clock.currentTime();
+    final long now = timer.monotonicNowNanos();
     for(int i = 0; i < EVICTION_LIMIT; i++) {
       final Entry peeked = queue.peek();
       if (peeked == null || !isExpired(peeked, now)) {

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

@@ -48,7 +48,16 @@ public final class Time {
    */
   public static long monotonicNow() {
     final long NANOSECONDS_PER_MILLISECOND = 1000000;
-
     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();
+  }
 }

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

@@ -48,4 +48,14 @@ public class Timer {
    * @return a monotonic clock that counts in milliseconds.
    */
   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();
+  }
 }

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

@@ -18,6 +18,7 @@
 
 package org.apache.hadoop.util;
 
+import java.util.concurrent.TimeUnit;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 
@@ -28,25 +29,38 @@ import org.apache.hadoop.classification.InterfaceStability;
 @InterfaceAudience.Private
 @InterfaceStability.Unstable
 public class FakeTimer extends Timer {
-  private long nowMillis;
+  private long nowNanos;
 
   /** Constructs a FakeTimer with a non-zero value */
   public FakeTimer() {
-    nowMillis = 1000;  // Initialize with a non-trivial value.
+    nowNanos = 1000;  // Initialize with a non-trivial value.
   }
 
   @Override
   public long now() {
-    return nowMillis;
+    return TimeUnit.NANOSECONDS.toMillis(nowNanos);
   }
 
   @Override
   public long monotonicNow() {
-    return nowMillis;
+    return TimeUnit.NANOSECONDS.toMillis(nowNanos);
+  }
+
+  @Override
+  public long monotonicNowNanos() {
+    return nowNanos;
   }
 
   /** Increases the time by milliseconds */
   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;
   }
 }

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

@@ -212,7 +212,7 @@ public class TestLightWeightCache {
     int iterate_count = 0;
     int contain_count = 0;
 
-    private long currentTestTime = ran.nextInt();
+    private FakeTimer fakeTimer = new FakeTimer();
 
     LightWeightCacheTestCase(int tablelength, int sizeLimit,
         long creationExpirationPeriod, long accessExpirationPeriod,
@@ -229,12 +229,7 @@ public class TestLightWeightCache {
 
       data = new IntData(datasize, modulus);
       cache = new LightWeightCache<IntEntry, IntEntry>(tablelength, sizeLimit,
-          creationExpirationPeriod, 0, new LightWeightCache.Clock() {
-        @Override
-        long currentTime() {
-          return currentTestTime;
-        }
-      });
+          creationExpirationPeriod, 0, fakeTimer);
 
       Assert.assertEquals(0, cache.size());
     }
@@ -246,7 +241,7 @@ public class TestLightWeightCache {
       } else {
         final IntEntry h = hashMap.remove(key);
         if (h != null) {
-          Assert.assertTrue(cache.isExpired(h, currentTestTime));
+          Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
         }
       }
       return c;
@@ -265,7 +260,7 @@ public class TestLightWeightCache {
       } else {
         final IntEntry h = hashMap.remove(key);
         if (h != null) {
-          Assert.assertTrue(cache.isExpired(h, currentTestTime));
+          Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
         }
       }
       return c;
@@ -285,7 +280,7 @@ public class TestLightWeightCache {
         final IntEntry h = hashMap.put(entry);
         if (h != null && h != entry) {
           // if h == entry, its expiration time is already updated
-          Assert.assertTrue(cache.isExpired(h, currentTestTime));
+          Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
         }
       }
       return c;
@@ -304,7 +299,7 @@ public class TestLightWeightCache {
       } else {
         final IntEntry h = hashMap.remove(key);
         if (h != null) {
-          Assert.assertTrue(cache.isExpired(h, currentTestTime));
+          Assert.assertTrue(cache.isExpired(h, fakeTimer.monotonicNowNanos()));
         }
       }
       return c;
@@ -338,7 +333,7 @@ public class TestLightWeightCache {
     }
 
     void check() {
-      currentTestTime += ran.nextInt() & 0x3;
+      fakeTimer.advanceNanos(ran.nextInt() & 0x3);
 
       //test size
       sizeTest();