1 package net.sf.ehcache.util;
2
3 import org.junit.Test;
4
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.Date;
8 import java.util.List;
9 import java.util.concurrent.TimeUnit;
10 import java.util.concurrent.atomic.AtomicBoolean;
11 import java.util.concurrent.atomic.AtomicInteger;
12 import java.util.concurrent.atomic.AtomicLong;
13
14 import static org.hamcrest.CoreMatchers.is;
15 import static org.junit.Assert.assertThat;
16
17 /***
18 * @author Alex Snaps
19 */
20 public class SlewClockTest {
21
22 private static final boolean SLOWRUN = Boolean.getBoolean("TimestamperTest.slowRun");
23 private static final long BACK_IN_TIME = TimeUnit.SECONDS.toMillis(30);
24 private static final int THREADS = 10;
25 private static final long DURATION = 15;
26
27
28 @Test
29 public void testTimeMillis() throws Throwable {
30 final AtomicLong slewStart = new AtomicLong();
31 final AtomicLong offset = new AtomicLong(0);
32 final List<Throwable> errors = Collections.synchronizedList(new ArrayList<Throwable>());
33 TimeProviderLoader.setTimeProvider(new SlewClock.TimeProvider() {
34 public long currentTimeMillis() {
35 return System.currentTimeMillis() - offset.get();
36 }
37 });
38 final AtomicBoolean stopped = new AtomicBoolean(false);
39 final AtomicInteger catchingUp = new AtomicInteger();
40 SlewClockVerifierThread[] threads = new SlewClockVerifierThread[THREADS];
41 for(int i =0; i < THREADS; i++) {
42 threads[i] = new SlewClockVerifierThread(stopped, catchingUp, errors, slewStart, i == 0);
43 }
44 for (Thread thread : threads) {
45 thread.start();
46 }
47 Thread.sleep(TimeUnit.SECONDS.toMillis(DURATION));
48 System.out.println("Going back in time by " + BACK_IN_TIME);
49 slewStart.set(System.currentTimeMillis());
50 offset.set(BACK_IN_TIME);
51 Thread.sleep(SLOWRUN ? BACK_IN_TIME * 3 : BACK_IN_TIME * 2);
52 stopped.set(true);
53 for (SlewClockVerifierThread thread : threads) {
54 thread.join();
55 }
56 if(!errors.isEmpty()) {
57 System.err.println("We have " + errors.size() + " error(s) here!");
58 throw errors.get(0);
59 }
60 assertThat(catchingUp.get(), is(THREADS));
61 }
62
63
64 private static class SlewClockVerifierThread extends Thread {
65 private final AtomicBoolean stopped;
66 private final AtomicInteger catchingUp;
67 private final AtomicLong slewTime;
68 private final boolean log;
69 private List<Throwable> errors;
70 private boolean wasSlewing;
71 private int previousSecond;
72 private long previous;
73
74 public SlewClockVerifierThread(final AtomicBoolean stopped, final AtomicInteger catchingUp, final List<Throwable> errors,
75 final AtomicLong slewTime, final boolean log) {
76 this.stopped = stopped;
77 this.catchingUp = catchingUp;
78 this.errors = errors;
79 this.slewTime = slewTime;
80 this.log = log;
81 }
82
83 @Override
84 public void run() {
85 long run =0;
86 while (!stopped.get()) {
87 long timeMillis = SlewClock.timeMillis();
88 if(SlewClock.isThreadCatchingUp()) {
89 if (!wasSlewing) {
90 catchingUp.incrementAndGet();
91 }
92 wasSlewing = true;
93 if(log) {
94 int x = (int) TimeUnit.MILLISECONDS.toSeconds(timeMillis) % 10;
95 if (SLOWRUN || x != previousSecond) {
96 previousSecond = x;
97 System.out.println(new Date(timeMillis) + " (" + timeMillis + "): " +
98 new Date(TimeProviderLoader.getTimeProvider().currentTimeMillis()) + " " +
99 SlewClock.behind());
100 }
101 }
102 if (SLOWRUN) {
103 try {
104 Thread.sleep(15);
105 } catch (InterruptedException e) {
106 interrupt();
107 }
108 }
109 } else if(wasSlewing) {
110 System.out.println("Caught up in " + (System.currentTimeMillis() - slewTime.get()) + "ms");
111 wasSlewing = false;
112 }
113 assertThat(timeMillis >= previous, is(true));
114 previous = timeMillis;
115 }
116 try {
117 assertThat(SlewClock.isThreadCatchingUp(), is(false));
118 } catch (AssertionError e) {
119 errors.add(e);
120 }
121 }
122 }
123 }