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 org.slf4j.Logger;
19  import org.slf4j.LoggerFactory;
20  
21  import java.util.concurrent.atomic.AtomicLong;
22  
23  /***
24   * Generates increasing identifiers (in a single VM only).
25   * Not valid across multiple VMs. Yet, the identifier is based on time, so that the drifting
26   * across a cluster should not ever be large...
27   *
28   * @author Alex Snaps
29   */
30  public final class Timestamper {
31  
32      /***
33       * Value for left shifting System.currentTimeMillis, freeing some space for the counter
34       */
35      public static final int BIN_DIGITS = Integer.getInteger("net.sf.ehcache.util.Timestamper.shift", 12);
36  
37      /***
38       * What is one milliseconds, based on "counter value reserved space", for this Timestamper
39       */
40      public static final int ONE_MS = 1 << BIN_DIGITS;
41  
42      private static final Logger LOG     = LoggerFactory.getLogger(Timestamper.class);
43      private static final int    MAX_LOG = Integer.getInteger("net.sf.ehcache.util.Timestamper.log.max", 1) * 1000;
44  
45      private static final AtomicLong VALUE  = new AtomicLong();
46      private static final AtomicLong LOGGED = new AtomicLong();
47  
48  
49      private Timestamper() {
50          //
51      }
52  
53      /***
54       * Returns an increasing unique value based on the System.currentTimeMillis()
55       * with some additional reserved space for a counter.
56       *
57       * @see net.sf.ehcache.util.Timestamper#BIN_DIGITS
58       * @return uniquely & increasing value
59       */
60      public static long next() {
61          int runs = 0;
62          while (true) {
63              long base = SlewClock.timeMillis() << BIN_DIGITS;
64              long maxValue = base + ONE_MS - 1;
65  
66              for (long current = VALUE.get(), update = Math.max(base, current + 1); update < maxValue;
67                   current = VALUE.get(), update = Math.max(base, current + 1)) {
68                  if (VALUE.compareAndSet(current, update)) {
69                      if (runs > 1) {
70                          log(base, "Thread spin-waits on time to pass. Looped "
71                                    + "{} times, you might want to increase -Dnet.sf.ehcache.util.Timestamper.shift", runs);
72                      }
73                      return update;
74                  }
75              }
76              ++runs;
77          }
78      }
79  
80      private static void log(final long base, final String message, final Object... params) {
81          if (LOG.isInfoEnabled()) {
82              long thisLog = (base >> BIN_DIGITS) / MAX_LOG;
83              long previousLog = LOGGED.get();
84              if (previousLog != thisLog) {
85                  if (LOGGED.compareAndSet(previousLog, thisLog)) {
86                      LOG.info(message, params);
87                  }
88              }
89          }
90      }
91  }