net.sf.ehcache.util.SlewClock Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/**
* Copyright Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.ehcache.util;
import net.sf.ehcache.util.lang.VicariousThreadLocal;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* SlewClock will return the current time in millis, but will never go back in time.
* If it detects that a back movement in time, it will slew the results while keeping them incrementing until time has caught up
*
* @author Chris Denis
* @author Alex Snaps
*/
final class SlewClock {
private static final Logger LOG = LoggerFactory.getLogger(SlewClock.class);
private static final TimeProvider PROVIDER = TimeProviderLoader.getTimeProvider();
private static final long DRIFT_MAXIMAL = Integer.getInteger("net.sf.ehcache.util.Timestamper.drift.max", 50);
private static final long SLEEP_MAXIMAL = Integer.getInteger("net.sf.ehcache.util.Timestamper.sleep.max", 50);
private static final int SLEEP_BASE = Integer.getInteger("net.sf.ehcache.util.Timestamper.sleep.min", 25);
private static final AtomicLong CURRENT = new AtomicLong(Long.MIN_VALUE);
private static final VicariousThreadLocal OFFSET = new VicariousThreadLocal();
private SlewClock() {
// You shall not instantiate me!
}
/**
* For testing purposes, you probably never want to use this.
* This method will resync, basically resetting any on going slewing, its internal clock tracking with whatever value
* the provider seeds it with.
*
* @deprecated
*/
@Deprecated
static void realignWithTimeProvider() {
CURRENT.set(getCurrentTime());
}
/**
* Will return the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
* But without ever going back. If a movement back in time is detected, the method will slew until time caught up
* @return The difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
*/
static long timeMillis() {
boolean interrupted = false;
try {
while (true) {
long mono = CURRENT.get();
long wall = getCurrentTime();
if (wall == mono) {
OFFSET.remove();
return wall;
} else if (wall > mono) {
if (CURRENT.compareAndSet(mono, wall)) {
OFFSET.remove();
return wall;
}
} else {
long delta = mono - wall;
if (delta < DRIFT_MAXIMAL) {
OFFSET.remove();
return mono;
} else {
Long lastDelta = OFFSET.get();
if (lastDelta == null || delta < lastDelta) {
if (CURRENT.compareAndSet(mono, mono + 1)) {
OFFSET.set(Long.valueOf(delta));
return mono + 1;
}
} else {
OFFSET.set(Long.valueOf(Math.max(delta, lastDelta)));
try {
long sleep = sleepTime(delta, lastDelta);
LOG.trace("{} sleeping for {}ms to adjust for wall-clock drift.", Thread.currentThread(), sleep);
Thread.sleep(sleep);
} catch (InterruptedException e) {
interrupted = true;
}
}
}
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
/**
* Verifies whether the current thread is currently catching up on time.
* To be meaning full, this method has to be called after the thread has called {@link #timeMillis()} at least once
* @return true if the thread is being marked as catching up on time
*/
static boolean isThreadCatchingUp() {
return OFFSET.get() != null;
}
/**
* The method will check how much behind is the current thread compared to the wall clock since the last {@link #timeMillis()} call.
* To be meaning full, this method has to be called after the thread has called {@link #timeMillis()} at least once
* @return the amount of milliseconds the thread is behind the wall clock, 0 if none.
*/
static long behind() {
Long offset = OFFSET.get();
return offset == null ? 0 : offset;
}
private static long sleepTime(final long current, final long previous) {
long target = SLEEP_BASE + (current - previous) * 2;
return Math.min(target > 0 ? target : SLEEP_BASE, SLEEP_MAXIMAL);
}
private static long getCurrentTime() {
return PROVIDER.currentTimeMillis();
}
/**
* Defines how the {@link SlewClock} utility class will get to the wall clock
*/
interface TimeProvider {
/**
* @return the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
*/
long currentTimeMillis();
}
}