This is an automated email from the ASF dual-hosted git repository.

reschke pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 06d46de56a OAK-11622: Clock improvements (#2204)
06d46de56a is described below

commit 06d46de56a4e28aadeff375bf3b95ac39869311b
Author: Julian Reschke <[email protected]>
AuthorDate: Fri Apr 4 10:30:40 2025 +0200

    OAK-11622: Clock improvements (#2204)
---
 .../org/apache/jackrabbit/oak/stats/Clock.java     | 75 +++++++++++++++++-----
 .../apache/jackrabbit/oak/stats/package-info.java  |  2 +-
 .../org/apache/jackrabbit/oak/stats/ClockTest.java | 68 +++++++++++++++++++-
 .../jackrabbit/oak/stats/NonTickingTestClock.java} | 20 +++++-
 4 files changed, 145 insertions(+), 20 deletions(-)

diff --git 
a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java 
b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
index a19c60e595..87e3a20c3c 100644
--- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
+++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/Clock.java
@@ -16,7 +16,10 @@
  */
 package org.apache.jackrabbit.oak.stats;
 
+import org.apache.jackrabbit.oak.commons.properties.SystemPropertySupplier;
+
 import java.io.Closeable;
+import java.time.Duration;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.util.Date;
@@ -29,7 +32,12 @@ import java.util.concurrent.atomic.AtomicLong;
 /**
  * Mechanism for keeping track of time at millisecond accuracy.
  * <p>
- * As of Oak 1.20, this extends from {@link java.time.Clock}.
+ * This extends from {@link java.time.Clock}. Consumers of clocks
+ * are advised to use that interface, unless the additional features
+ * of this class are needed.
+ * <p>
+ * Note that implementations of this class in general do not support
+ * the timezone related features of {@linkplain java.time.Clock}.
  */
 public abstract class Clock extends java.time.Clock {
 
@@ -40,7 +48,7 @@ public abstract class Clock extends java.time.Clock {
      * the effect of an inaccurate system clock.
      */
     private static final int SIMPLE_CLOCK_NOISE =
-            Integer.getInteger("simple.clock.noise", 0);
+        SystemPropertySupplier.create("simple.clock.noise", 0).get();
 
     /**
      * Millisecond granularity of the {@link #ACCURATE} clock.
@@ -49,15 +57,15 @@ public abstract class Clock extends java.time.Clock {
      * code that relies on millisecond timestamps.
      */
     private static final long ACCURATE_CLOCK_GRANULARITY =
-            Long.getLong("accurate.clock.granularity", 1);
+            SystemPropertySupplier.create("accurate.clock.granularity", 
1L).get();
 
     /**
      * Millisecond update interval of the {@link Fast} clock. Configurable
-     * by the "fast.clock.interval" system property to to make it easier
+     * by the "fast.clock.interval" system property to make it easier
      * to test the effect of different update frequencies.
      */
     static final long FAST_CLOCK_INTERVAL =
-            Long.getLong("fast.clock.interval", 10);
+            SystemPropertySupplier.create("fast.clock.interval", 10L).get();
 
     private long monotonic = 0;
 
@@ -65,6 +73,10 @@ public abstract class Clock extends java.time.Clock {
 
     /**
      * Returns the current time in milliseconds since the epoch.
+     * <p>
+     * Users of this class should use {@link java.time.Clock#millis()} instead.
+     * <em>This</em> abstract method remains here for cases where this
+     * class is extended.
      *
      * @see System#currentTimeMillis()
      * @see java.time.Clock#millis()
@@ -72,6 +84,17 @@ public abstract class Clock extends java.time.Clock {
      */
     public abstract long getTime();
 
+    /**
+     * Returns the current time in milliseconds since the epoch.
+     *
+     * @see System#currentTimeMillis()
+     * @return current time in milliseconds since the epoch
+     */
+    @Override
+    public long millis() {
+        return getTime();
+    }
+
     /**
      * Returns a monotonically increasing timestamp based on the current time.
      * A call to this method will always return a value that is greater than
@@ -96,7 +119,7 @@ public abstract class Clock extends java.time.Clock {
      * Returns a strictly increasing timestamp based on the current time.
      * This method is like {@link #getTimeMonotonic()}, with the exception
      * that two calls of this method will never return the same timestamp.
-     * Instead this method will explicitly wait until the current time
+     * Instead, this method will explicitly wait until the current time
      * increases beyond any previously returned value. Note that the wait
      * may last long if this method is called frequently from many concurrent
      * thread or if the system time is adjusted backwards. The caller should
@@ -161,6 +184,32 @@ public abstract class Clock extends java.time.Clock {
         }
     }
 
+    /**
+     * Waits for the given delta in ms. The current thread
+     * is suspended until the {@link #getTimeIncreasing()} method returns
+     * a time that's equal or greater than the start time plus the given
+     * delta.
+     *
+     * @param delta time in milliseconds to wait for
+     * @throws InterruptedException if the wait was interrupted
+     */
+    public void waitFor(long delta) throws InterruptedException {
+        waitUntil(getTime() + delta);
+    }
+
+    /**
+     * Waits for the given duration. The current thread
+     * is suspended until the {@link #getTimeIncreasing()} method returns
+     * a time that's equal or greater than the start time plus the given
+     * delta.
+     *
+     * @param delta Duration to wait for
+     * @throws InterruptedException if the wait was interrupted
+     */
+    public void waitFor(Duration delta) throws InterruptedException {
+        waitUntil(getTime() + delta.toMillis());
+    }
+
     @Override
     public ZoneId getZone() {
         return ZoneId.of("Z");
@@ -273,7 +322,7 @@ public abstract class Clock extends java.time.Clock {
 
             // Last clock sync was over 10s ago or the nanosecond timer has
             // drifted more than 100ms from the wall clock, so it's best to
-            // to a hard sync with no smoothing.
+            // do a hard sync with no smoothing.
             if (nowms >= ms + 1000) {
                 ms = nowms;
                 ns = nowns;
@@ -307,12 +356,8 @@ public abstract class Clock extends java.time.Clock {
         private final ScheduledFuture<?> future;
 
         public Fast(ScheduledExecutorService executor) {
-            future = executor.scheduleAtFixedRate(new Runnable() {
-                @Override
-                public void run() {
-                    time = ACCURATE.getTime();
-                }
-            }, FAST_CLOCK_INTERVAL, FAST_CLOCK_INTERVAL, 
TimeUnit.MILLISECONDS);
+            future = executor.scheduleAtFixedRate(() ->
+                    time = ACCURATE.getTime(), FAST_CLOCK_INTERVAL, 
FAST_CLOCK_INTERVAL, TimeUnit.MILLISECONDS);
         }
 
         @Override
@@ -332,7 +377,7 @@ public abstract class Clock extends java.time.Clock {
 
     /**
      * A virtual clock that has no connection to the actual system time.
-     * Instead the clock maintains an internal counter that's incremented
+     * Instead, the clock maintains an internal counter that's incremented
      * atomically whenever the current time is requested. This guarantees
      * that the reported time signal is always strictly increasing.
      */
@@ -357,5 +402,5 @@ public abstract class Clock extends java.time.Clock {
         public String toString() {
             return "Clock.Virtual";
         }
-    };
+    }
 }
diff --git 
a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/package-info.java 
b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/package-info.java
index b04ab9e46d..b9c303d14a 100644
--- 
a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/package-info.java
+++ 
b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.2.0")
+@Version("1.3.0")
 package org.apache.jackrabbit.oak.stats;
 
 import org.osgi.annotation.versioning.Version;
diff --git 
a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java 
b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java
index 16acf31062..0b6dce9d6a 100644
--- a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java
+++ b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/ClockTest.java
@@ -19,6 +19,8 @@ package org.apache.jackrabbit.oak.stats;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.time.Duration;
+import java.time.Instant;
 import java.time.ZoneId;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -68,19 +70,83 @@ public class ClockTest {
     }
 
     @Test
-    public void testClockJavaTime() throws InterruptedException {
+    public void testGetDate() {
+        Clock c = Clock.SIMPLE;
+
+        long t1 = c.millis();
+        long t2 = c.getDate().getTime();
+        long t3 = c.millis();
+
+        assertTrue(t1 <= t2);
+        assertTrue(t2 <= t3);
+    }
+
+    @Test
+    public void testWaitUntilSimple() throws InterruptedException {
+        testClockWaitUntil(Clock.SIMPLE);
+        testClockWaitFor(Clock.SIMPLE);
+    }
+
+    @Test
+    public void testWaitUntilAccurate() throws InterruptedException {
+        testClockWaitUntil(Clock.ACCURATE);
+        testClockWaitFor(Clock.ACCURATE);
+    }
+
+    @Test
+    public void testWaitUntilVirtual() throws InterruptedException {
+        testClockWaitUntil(new Clock.Virtual());
+        testClockWaitFor(new Clock.Virtual());
+    }
+
+    private void testClockWaitUntil(Clock c) throws InterruptedException {
+        long start = c.millis();
+        long delta = 100;
+        long until = start + delta;
+        c.waitUntil(until);
+
+        assertTrue(c.millis() - start >= delta);
+    }
+
+    private void testClockWaitFor(Clock c) throws InterruptedException {
+        long start = c.millis();
+        long delta = 100;
+        c.waitFor(delta);
+
+        assertTrue(c.millis() - start >= delta);
+
+        start = c.millis();
+        c.waitFor(Duration.ofMillis(100));
+
+        assertTrue(c.millis() - start >= delta);
+    }
+
+    @Test
+    public void testClockJavaTime() {
         Clock c = Clock.SIMPLE;
 
         long t1 = c.millis();
         long t2 = c.getTime();
         long t3 = c.millis();
+        Instant i4 = c.instant();
         assertTrue(t1 <= t2);
         assertTrue(t2 <= t3);
+        assertTrue(t3 <= i4.toEpochMilli());
 
         java.time.Clock c2 = c.withZone(ZoneId.of("Z"));
         assertEquals(c2.getZone(), c.getZone());
     }
 
+    @Test
+    public void testNonTicking() {
+        NonTickingTestClock ntc = new NonTickingTestClock();
+        assertEquals(0, ntc.millis());
+        ntc.setTime(1000);
+        assertEquals(1000, ntc.millis());
+        ntc.setTime(500);
+        assertEquals(500, ntc.millis());
+    }
+
     private void testClockDrift(Clock clock) throws InterruptedException {
 
         long drift = clock.getTime() - System.currentTimeMillis();
diff --git 
a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/package-info.java 
b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/NonTickingTestClock.java
similarity index 69%
copy from 
oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/package-info.java
copy to 
oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/NonTickingTestClock.java
index b04ab9e46d..a5379be4df 100644
--- 
a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/package-info.java
+++ 
b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/NonTickingTestClock.java
@@ -6,7 +6,7 @@
  * (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
+ *      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,
@@ -14,7 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("1.2.0")
 package org.apache.jackrabbit.oak.stats;
 
-import org.osgi.annotation.versioning.Version;
+/**
+ * Simple non-ticking clock that can be set (for use in test cases).
+ */
+public class NonTickingTestClock extends Clock {
+
+    long time = 0;
+
+    @Override
+    public long getTime() {
+        return this.time;
+    }
+
+    public void setTime(long time) {
+        this.time = time;
+    }
+}

Reply via email to