1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.ehcache.util;
17
18 import net.sf.ehcache.util.lang.VicariousThreadLocal;
19
20 import java.util.concurrent.atomic.AtomicLong;
21
22 /***
23 * SlewClock will return the current time in millis, but will never go back in time.
24 * If it detects that a back movement in time, it will slew the results while keeping them incrementing until time has caught up
25 *
26 * @author Chris Denis
27 * @author Alex Snaps
28 */
29 final class SlewClock {
30
31 private static final TimeProvider PROVIDER = TimeProviderLoader.getTimeProvider();
32
33 private static final long DRIFT_MAXIMAL = Integer.getInteger("net.sf.ehcache.util.Timestamper.drift.max", 50);
34
35 private static final long SLEEP_MAXIMAL = Integer.getInteger("net.sf.ehcache.util.Timestamper.sleep.max", 50);
36
37 private static final int SLEEP_BASE = Integer.getInteger("net.sf.ehcache.util.Timestamper.sleep.min", 25);
38
39 private static final AtomicLong CURRENT = new AtomicLong(getCurrentTime());
40
41 private static final VicariousThreadLocal<Long> OFFSET = new VicariousThreadLocal<Long>();
42
43 private SlewClock() {
44
45 }
46
47 /***
48 * Will return the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
49 * But without ever going back. If a movement back in time is detected, the method will slew until time caught up
50 * @return The difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
51 */
52 static long timeMillis() {
53 boolean interrupted = false;
54 try {
55 while (true) {
56 long mono = CURRENT.get();
57 long wall = getCurrentTime();
58 if (wall == mono) {
59 OFFSET.remove();
60 return wall;
61 } else if (wall >= mono) {
62 if (CURRENT.compareAndSet(mono, wall)) {
63 OFFSET.remove();
64 return wall;
65 }
66 } else {
67 long delta = mono - wall;
68 if (delta < DRIFT_MAXIMAL) {
69 OFFSET.remove();
70 return mono;
71 } else {
72 Long lastDelta = OFFSET.get();
73 if (lastDelta == null || delta < lastDelta) {
74 long update = wall - delta;
75 update = update < mono ? mono + 1 : update;
76 if (CURRENT.compareAndSet(mono, update)) {
77 OFFSET.set(Long.valueOf(delta));
78 return update;
79 }
80 } else {
81 try {
82 Thread.sleep(sleepTime(delta, lastDelta));
83 } catch (InterruptedException e) {
84 interrupted = true;
85 }
86 }
87 }
88 }
89 }
90 } finally {
91 if (interrupted) {
92 Thread.currentThread().interrupt();
93 }
94 }
95 }
96
97 /***
98 * Verifies whether the current thread is currently catching up on time.
99 * To be meaning full, this method has to be called after the thread has called {@link #timeMillis()} at least once
100 * @return true if the thread is being marked as catching up on time
101 */
102 static boolean isThreadCatchingUp() {
103 return OFFSET.get() != null;
104 }
105
106 /***
107 * The method will check how much behind is the current thread compared to the wall clock since the last {@link #timeMillis()} call.
108 * To be meaning full, this method has to be called after the thread has called {@link #timeMillis()} at least once
109 * @return the amount of milliseconds the thread is behind the wall clock, 0 if none.
110 */
111 static long behind() {
112 Long offset = OFFSET.get();
113 return offset == null ? 0 : offset;
114 }
115
116 private static long sleepTime(final long current, final long previous) {
117 long target = SLEEP_BASE + (current - previous) * 2;
118 return Math.min(target > 0 ? target : SLEEP_BASE, SLEEP_MAXIMAL);
119 }
120
121 private static long getCurrentTime() {
122 return PROVIDER.currentTimeMillis();
123 }
124
125 /***
126 * Defines how the {@link SlewClock} utility class will get to the wall clock
127 */
128 interface TimeProvider {
129
130 /***
131 * @return the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
132 */
133 long currentTimeMillis();
134
135 }
136 }