View Javadoc

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          // You shall not instantiate me!
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 }