diff --git a/cosid-core/src/main/java/me/ahoo/cosid/util/Clock.java b/cosid-core/src/main/java/me/ahoo/cosid/util/Clock.java index 080099e0cf..8ab2d3de37 100644 --- a/cosid-core/src/main/java/me/ahoo/cosid/util/Clock.java +++ b/cosid-core/src/main/java/me/ahoo/cosid/util/Clock.java @@ -23,10 +23,11 @@ * @author ahoo wang */ @ThreadSafe +@FunctionalInterface public interface Clock { - Clock CACHE = new CacheClock(); Clock SYSTEM = new SystemClock(); + Clock CACHE = new CacheClock(SYSTEM); long secondTime(); @@ -58,11 +59,13 @@ class CacheClock implements Clock, Runnable { * Tolerate a one-second time limit. */ public static final long ONE_SECOND_PERIOD = Duration.ofSeconds(1).toNanos(); + private final Clock clock; private final Thread thread; private volatile long lastTime; - public CacheClock() { - this.lastTime = getSystemSecondTime(); + public CacheClock(Clock clock) { + this.clock = clock; + this.lastTime = clock.secondTime(); this.thread = new Thread(this); this.thread.setName("CosId-CacheClock"); this.thread.setDaemon(true); @@ -77,7 +80,11 @@ public long secondTime() { @Override public void run() { while (!thread.isInterrupted()) { - this.lastTime = getSystemSecondTime(); + long currentTime = clock.secondTime(); + // Avoid time going backwards + if (currentTime > lastTime) { + this.lastTime = currentTime; + } LockSupport.parkNanos(this, ONE_SECOND_PERIOD); } } diff --git a/cosid-core/src/test/java/me/ahoo/cosid/util/CacheClockTest.java b/cosid-core/src/test/java/me/ahoo/cosid/util/CacheClockTest.java index aa3c0ce552..2cf2d0415a 100644 --- a/cosid-core/src/test/java/me/ahoo/cosid/util/CacheClockTest.java +++ b/cosid-core/src/test/java/me/ahoo/cosid/util/CacheClockTest.java @@ -13,12 +13,14 @@ package me.ahoo.cosid.util; +import static me.ahoo.cosid.util.Clock.CacheClock.ONE_SECOND_PERIOD; import static org.junit.jupiter.api.Assertions.assertTrue; import lombok.SneakyThrows; import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.LockSupport; /** @@ -45,4 +47,28 @@ void secondTimeWhenSleep() { long tolerance = 1L; assertTrue(diff <= tolerance); } + + @Test + void secondTimeIfBackwards() { + Clock backwardsClock = new BackwardsClock(); + Clock cacheClock = new Clock.CacheClock(backwardsClock); + long lastTime = cacheClock.secondTime(); + for (int i = 0; i < 6; i++) { + LockSupport.parkNanos(this, ONE_SECOND_PERIOD); + long currentTime = cacheClock.secondTime(); + assertTrue(currentTime >= lastTime); + lastTime = currentTime; + } + } + + static class BackwardsClock implements Clock { + private final long[] timeline = new long[]{1, 2, 3, 4, 5}; + private int index = 0; + + @Override + public long secondTime() { + int idx = Math.floorMod(index++, timeline.length); + return timeline[idx]; + } + } }