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-core Show documentation
Show all versions of ehcache-core Show documentation
This is the ehcache core module. Pair it with other modules for added functionality.
/**
* Copyright 2003-2010 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;
/**
* 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 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(getCurrentTime());
private static final VicariousThreadLocal OFFSET = new VicariousThreadLocal();
private SlewClock() {
// You shall not instantiate me!
}
/**
* 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) {
long update = wall - delta;
update = update < mono ? mono + 1 : update;
if (CURRENT.compareAndSet(mono, update)) {
OFFSET.set(Long.valueOf(delta));
return update;
}
} else {
try {
Thread.sleep(sleepTime(delta, lastDelta));
} 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();
}
}