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 }