1 /*
2  * Created on Apr 16, 2004 Created by Alon Rohter
3  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation; either version 2 of the License, or (at your option) any later
8  * version. This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11  * details. You should have received a copy of the GNU General Public License
12  * along with this program; if not, write to the Free Software Foundation, Inc.,
13  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14  *
15  */
16 package org.gudy.azureus2.core3.util;
17 
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.concurrent.atomic.AtomicLong;
22 
23 /**
24  * Utility class to retrieve current system time, and catch clock backward time
25  * changes.
26  */
27 public class SystemTime {
28 	public static final long	TIME_GRANULARITY_MILLIS	= 25;	//internal update time ms
29 
30 	private static final int	STEPS_PER_SECOND	= (int) (1000 / TIME_GRANULARITY_MILLIS);
31 
32 	private static SystemTimeProvider	instance;
33 
34 	// can't do that without some safeguarding code.
35 	// monotime does guarantee that time neither goes backwards nor performs leaps into the future.
36 	// the HPC doesn't jump backward but can jump forward in time
37 	private static final boolean		SOD_IT_LETS_USE_HPC = false;//	= Constants.isCVSVersion();
38 
39 	private static volatile List<TickConsumer>			systemTimeConsumers		= new ArrayList<TickConsumer>();
40 	private static volatile List<TickConsumer>			monotoneTimeConsumers	= new ArrayList<TickConsumer>();
41 	private static volatile List<ChangeListener>		clock_change_list		= new ArrayList<ChangeListener>();
42 	//private static long					hpc_base_time;
43 	//private static long					hpc_last_time;
44 	//private static boolean				no_hcp_logged;
45 
46 	static
47 	{
48 		try
49 		{
50 			if (System.getProperty("azureus.time.use.raw.provider", "0").equals("1"))
51 			{
52 				System.out.println("Warning: Using Raw Provider");
53 				instance = new RawProvider();
54 			} else
55 			{
56 				instance = new SteppedProvider();
57 			}
58 		} catch (Throwable e)
59 		{
60 			// might be in applet...
61 			instance = new SteppedProvider();
62 		}
63 	}
64 
useRawProvider()65 	public static void useRawProvider() {
66 		if (!(instance instanceof RawProvider))
67 		{
68 			Debug.out( "Whoa, someone already created a non-raw provider!" );
69 
70 			instance = new RawProvider();
71 		}
72 	}
73 
74 	protected interface SystemTimeProvider {
getTime()75 		public long getTime();
76 
getMonoTime()77 		public long getMonoTime();
78 
79 		public long
getSteppedMonoTime()80 		getSteppedMonoTime();
81 	}
82 
83 	private static class SteppedProvider implements SystemTimeProvider {
84 		private static final long	HPC_START = getHighPrecisionCounter()/1000000L;
85 
86 		private final Thread		updater;
87 		private volatile long		stepped_time;
88 		private volatile long		currentTimeOffset = System.currentTimeMillis();
89 		private AtomicLong 			last_approximate_time = new AtomicLong();
90 		//private volatile long		last_approximate_time;
91 		private volatile int		access_count;
92 		private volatile int		slice_access_count;
93 		private volatile int		access_average_per_slice;
94 		private volatile int		drift_adjusted_granularity;
95 
96 		private volatile long		stepped_mono_time;
97 
SteppedProvider()98 		private SteppedProvider()
99 		{
100 			// System.out.println("SystemTime: using stepped time provider");
101 
102 			stepped_time = 0;
103 
104 			updater = new Thread("SystemTime")
105 			{
106 				public void run() {
107 					long adjustedTimeOffset = currentTimeOffset;
108 					// these averages rely on monotone time, thus won't be affected by system time changes
109 					final Average access_average = Average.getInstance(1000, 10);
110 					final Average drift_average = Average.getInstance(1000, 10);
111 					long lastOffset = adjustedTimeOffset;
112 					long lastSecond = -1000;
113 					int tick_count = 0;
114 					while (true)
115 					{
116 						final long rawTime = System.currentTimeMillis();
117 						/*
118 						 * keep the monotone time in sync with the raw system
119 						 * time, for this we need to know the offset of the
120 						 * current time to the system time
121 						 */
122 						long newMonotoneTime = rawTime - adjustedTimeOffset;
123 						long delta = newMonotoneTime - stepped_time;
124 						/*
125 						 * unless the system time jumps, then we just guess the
126 						 * time that has passed and adjust the update, so that
127 						 * the next round can be in sync with the system time
128 						 * again
129 						 */
130 						if (delta < 0 || delta > 1000)
131 						{
132 							/*
133 							 * jump occured, update monotone time offset, but
134 							 * not the current time one, that only happens every
135 							 * second
136 							 */
137 							stepped_time += TIME_GRANULARITY_MILLIS;
138 							adjustedTimeOffset = rawTime - stepped_time;
139 						} else
140 						{ // time is good, keep it
141 							stepped_time = newMonotoneTime;
142 						}
143 						tick_count++;
144 
145 						long change;
146 
147 						if ( tick_count == STEPS_PER_SECOND ){
148 
149 							change = adjustedTimeOffset - lastOffset;
150 
151 							if ( change != 0 ){
152 
153 								Iterator<ChangeListener> it = clock_change_list.iterator();
154 								//Debug.outNoStack("Clock change of " + change + " ms detected, raw=" + rawTime );
155 								while (it.hasNext()){
156 
157 									try{
158 										it.next().clockChangeDetected( rawTime, change );
159 
160 									}catch( Throwable e ){
161 
162 										Debug.out( e );
163 									}
164 								}
165 								lastOffset = adjustedTimeOffset;
166 
167 								currentTimeOffset = adjustedTimeOffset;
168 							}
169 							// averaging magic to estimate the amount of time that passes between each getTime invocation
170 							long drift = stepped_time - lastSecond - 1000;
171 							lastSecond = stepped_time;
172 							drift_average.addValue(drift);
173 							drift_adjusted_granularity = (int) (TIME_GRANULARITY_MILLIS + (drift_average.getAverage() / STEPS_PER_SECOND));
174 							access_average.addValue(access_count);
175 							access_average_per_slice = (int) (access_average.getAverage() / STEPS_PER_SECOND);
176 							//System.out.println( "access count = " + access_count + ", average = " + access_average.getAverage() + ", per slice = " + access_average_per_slice + ", drift = " + drift +", average = " + drift_average.getAverage() + ", dag =" + drift_adjusted_granularity );
177 							access_count = 0;
178 							tick_count = 0;
179 						}else{
180 							change = 0;
181 						}
182 
183 						slice_access_count = 0;
184 
185 						stepped_mono_time = stepped_time;
186 
187 						long adjustedTime = stepped_time + currentTimeOffset;
188 
189 						if ( change != 0 ){
190 							Iterator<ChangeListener> it = clock_change_list.iterator();
191 							//Debug.outNoStack("Clock change of " + change + " ms completed, curr=" + adjustedTime );
192 							while (it.hasNext()){
193 
194 								try{
195 									it.next().clockChangeCompleted( adjustedTime, change );
196 
197 								}catch( Throwable e ){
198 
199 									Debug.out( e );
200 								}
201 							}
202 						}
203 
204 						// copy reference since we use unsynced COW semantics
205 						List<TickConsumer> consumersRef = monotoneTimeConsumers;
206 						for (int i = 0; i < consumersRef.size(); i++)
207 						{
208 							TickConsumer cons = consumersRef.get(i);
209 							try
210 							{
211 								cons.consume(stepped_time);
212 							} catch (Throwable e)
213 							{
214 								Debug.printStackTrace(e);
215 							}
216 						}
217 
218 						/*
219 						 * notify consumers with the external offset, internal
220 						 * offset is only meant for updates
221 						 */
222 						consumersRef = systemTimeConsumers;
223 
224 						for (int i = 0; i < consumersRef.size(); i++)
225 						{
226 							TickConsumer cons = consumersRef.get(i);
227 							try
228 							{
229 								cons.consume(adjustedTime);
230 							} catch (Throwable e)
231 							{
232 								Debug.printStackTrace(e);
233 							}
234 						}
235 
236 						try
237 						{
238 							Thread.sleep(TIME_GRANULARITY_MILLIS);
239 						} catch (Exception e)
240 						{
241 							Debug.printStackTrace(e);
242 						}
243 					}
244 				}
245 			};
246 			updater.setDaemon(true);
247 			// we don't want this thread to lag much as it'll stuff up the upload/download rate mechanisms (for example)
248 			updater.setPriority(Thread.MAX_PRIORITY);
249 			updater.start();
250 		}
251 
getTime()252 		public long getTime() {
253 			return getMonoTime() + currentTimeOffset;
254 		}
255 
getMonoTime()256 		public long getMonoTime() {
257 			if ( SOD_IT_LETS_USE_HPC ){
258 
259 				return( ( getHighPrecisionCounter()/1000000) - HPC_START );
260 
261 			}else{
262 				long adjusted_time;
263 				long averageSliceStep = access_average_per_slice;
264 				if (averageSliceStep > 0)
265 				{
266 					long sliceStep = (drift_adjusted_granularity * slice_access_count) / averageSliceStep;
267 					if (sliceStep >= drift_adjusted_granularity)
268 					{
269 						sliceStep = drift_adjusted_granularity - 1;
270 					}
271 					adjusted_time = sliceStep + stepped_time;
272 				} else
273 					adjusted_time = stepped_time;
274 				access_count++;
275 				slice_access_count++;
276 
277 				// make sure we don't go backwards and our reference value for going backwards doesn't go backwards either
278 				long approxBuffered = last_approximate_time.get();
279 				if (adjusted_time < approxBuffered)
280 					adjusted_time = approxBuffered;
281 				else
282 					last_approximate_time.compareAndSet(approxBuffered, adjusted_time);
283 
284 				return adjusted_time;
285 			}
286 		}
287 
getSteppedMonoTime()288 		public long getSteppedMonoTime() {
289 
290 			if ( SOD_IT_LETS_USE_HPC ){
291 
292 				return( getHighPrecisionCounter()/1000000 );
293 
294 			}else{
295 
296 				return( stepped_mono_time );
297 			}
298 		}
299 	}
300 
301 	private static class RawProvider implements SystemTimeProvider {
302 		//private static final int	STEPS_PER_SECOND	= (int) (1000 / TIME_GRANULARITY_MILLIS);
303 		private final Thread		updater;
304 
RawProvider()305 		private RawProvider()
306 		{
307 			System.out.println("SystemTime: using raw time provider");
308 
309 			updater = new Thread("SystemTime")
310 			{
311 				long	last_time;
312 
313 				public void run() {
314 					while (true)
315 					{
316 						long current_time = getTime();
317 						long change;
318 
319 						if ( last_time != 0 ){
320 
321 							long offset = current_time - last_time;
322 
323 							if (offset < 0 || offset > 5000){
324 
325 								change = offset;
326 
327 									// clock's changed
328 
329 								Iterator<ChangeListener> it = clock_change_list.iterator();
330 
331 								while (it.hasNext()){
332 
333 									try{
334 										it.next().clockChangeDetected(current_time, change);
335 									}catch( Throwable e ){
336 
337 										Debug.out( e );
338 									}
339 								}
340 							}else{
341 								change = 0;
342 							}
343 						}else{
344 							change = 0;
345 						}
346 
347 						last_time = current_time;
348 
349 						if ( change != 0 ){
350 							Iterator<ChangeListener> it = clock_change_list.iterator();
351 							while (it.hasNext()){
352 
353 								try{
354 									it.next().clockChangeCompleted(current_time, change);
355 
356 								}catch( Throwable e ){
357 
358 									Debug.out( e );
359 								}
360 							}
361 						}
362 
363 						List consumer_list_ref = systemTimeConsumers;
364 
365 						for (int i = 0; i < consumer_list_ref.size(); i++)
366 						{
367 							TickConsumer cons = (TickConsumer) consumer_list_ref.get(i);
368 							try
369 							{
370 								cons.consume(current_time);
371 							} catch (Throwable e)
372 							{
373 								Debug.printStackTrace(e);
374 							}
375 						}
376 						consumer_list_ref = monotoneTimeConsumers;
377 
378 						long	mono_time = getMonoTime();
379 
380 						for (int i = 0; i < consumer_list_ref.size(); i++)
381 						{
382 							TickConsumer cons = (TickConsumer) consumer_list_ref.get(i);
383 							try
384 							{
385 								cons.consume(mono_time);
386 							} catch (Throwable e)
387 							{
388 								Debug.printStackTrace(e);
389 							}
390 						}
391 
392 						try
393 						{
394 							Thread.sleep(TIME_GRANULARITY_MILLIS);
395 						} catch (Exception e)
396 						{
397 							Debug.printStackTrace(e);
398 						}
399 					}
400 				}
401 			};
402 			updater.setDaemon(true);
403 			// we don't want this thread to lag much as it'll stuff up the upload/download rate mechanisms (for example)
404 			updater.setPriority(Thread.MAX_PRIORITY);
405 			updater.start();
406 		}
407 
getTime()408 		public long getTime() {
409 			return System.currentTimeMillis();
410 		}
411 
412 		/**
413 		 * This implementation does not guarantee monotonous time increases with
414 		 * 100% accuracy as the adjustedTimeOffset is only adjusted every
415 		 * TIME_GRANULARITY_MILLIS
416 		 */
getMonoTime()417 		public long getMonoTime() {
418 			return getHighPrecisionCounter()/1000000;
419 		}
420 
getSteppedMonoTime()421 		public long getSteppedMonoTime() {
422 			return getMonoTime();
423 		}
424 	}
425 
426 	/**
427 	 * Note that this can this time can jump into the future or past due to
428 	 * clock adjustments use getMonotonousTime() if you need steady increases
429 	 *
430 	 * @return current system time in millisecond since epoch
431 	 */
getCurrentTime()432 	public static long getCurrentTime() {
433 		return (instance.getTime());
434 	}
435 
436 	/**
437 	 * Time that is guaranteed to grow monotonously and also ignores larger
438 	 * jumps into the future which might be caused by adjusting the system clock<br>
439 	 * <br>
440 	 *
441 	 * <b>Do not mix times retrieved by this method with normal time!</b>
442 	 *
443 	 * @return the amount of real time passed since the program start in
444 	 *         milliseconds
445 	 */
getMonotonousTime()446 	public static long getMonotonousTime() {
447 		return instance.getMonoTime();
448 	}
449 
450 		/**
451 		 * Like getMonotonousTime but only updated at TIME_GRANULARITY_MILLIS intervals (not interpolated)
452 		 * As such it is likely to be cheaper to obtain
453 		 * @return
454 		 */
455 
getSteppedMonotonousTime()456 	public static long getSteppedMonotonousTime() {
457 		return instance.getSteppedMonoTime();
458 	}
459 
460 
getOffsetTime(long offsetMS)461 	public static long getOffsetTime(long offsetMS) {
462 		return instance.getTime() + offsetMS;
463 	}
464 
registerConsumer(TickConsumer c)465 	public static void registerConsumer(TickConsumer c) {
466 		synchronized (instance)
467 		{
468 			List new_list = new ArrayList(systemTimeConsumers);
469 			new_list.add(c);
470 			systemTimeConsumers = new_list;
471 		}
472 	}
473 
unregisterConsumer(TickConsumer c)474 	public static void unregisterConsumer(TickConsumer c) {
475 		synchronized (instance)
476 		{
477 			List new_list = new ArrayList(systemTimeConsumers);
478 			new_list.remove(c);
479 			systemTimeConsumers = new_list;
480 		}
481 	}
482 
registerMonotonousConsumer(TickConsumer c)483 	public static void registerMonotonousConsumer(TickConsumer c) {
484 		synchronized (instance)
485 		{
486 			List new_list = new ArrayList(monotoneTimeConsumers);
487 			new_list.add(c);
488 			monotoneTimeConsumers = new_list;
489 		}
490 	}
491 
unregisterMonotonousConsumer(TickConsumer c)492 	public static void unregisterMonotonousConsumer(TickConsumer c) {
493 		synchronized (instance)
494 		{
495 			List new_list = new ArrayList(monotoneTimeConsumers);
496 			new_list.remove(c);
497 			monotoneTimeConsumers = new_list;
498 		}
499 	}
500 
registerClockChangeListener(ChangeListener c)501 	public static void registerClockChangeListener(ChangeListener c) {
502 		synchronized (instance)
503 		{
504 			List new_list = new ArrayList(clock_change_list);
505 			new_list.add(c);
506 			clock_change_list = new_list;
507 		}
508 	}
509 
unregisterClockChangeListener(ChangeListener c)510 	public static void unregisterClockChangeListener(ChangeListener c) {
511 		synchronized (instance)
512 		{
513 			List new_list = new ArrayList(clock_change_list);
514 			new_list.remove(c);
515 			clock_change_list = new_list;
516 		}
517 	}
518 
519 	public interface TickConsumer {
consume(long current_time)520 		public void consume(long current_time);
521 	}
522 
523 	public interface ChangeListener {
524 		/**
525 		 * Called before the change becomes visible to getCurrentTime callers
526 		 * @param current_time
527 		 * @param change_millis
528 		 */
clockChangeDetected(long current_time, long change_millis)529 		public void clockChangeDetected(long current_time, long change_millis);
530 		/**
531 		 * Called after the change is visible to getCurrentTime callers
532 		 * @param current_time
533 		 * @param change_millis
534 		 */
clockChangeCompleted(long current_time, long change_millis)535 		public void clockChangeCompleted(long current_time, long change_millis);
536 	}
537 
538 	public static long
getHighPrecisionCounter()539 	getHighPrecisionCounter()
540 	{
541 		return( System.nanoTime());
542 	}
543 
main(String[] args)544 	public static void main(String[] args) {
545 		for (int i = 0; i < 1; i++)
546 		{
547 			//final int f_i = i;
548 			new Thread()
549 			{
550 				public void run() {
551 					/*
552 					 * Average access_average = Average.getInstance( 1000, 10 );
553 					 *
554 					 * long last = SystemTime.getCurrentTime();
555 					 *
556 					 * int count = 0;
557 					 *
558 					 * while( true ){
559 					 *
560 					 * long now = SystemTime.getCurrentTime();
561 					 *
562 					 * long diff = now - last;
563 					 *
564 					 * System.out.println( "diff=" + diff );
565 					 *
566 					 * last = now;
567 					 *
568 					 * access_average.addValue( diff );
569 					 *
570 					 * count++;
571 					 *
572 					 * if ( count == 33 ){
573 					 *
574 					 * System.out.println( "AVERAGE " + f_i + " = " +
575 					 * access_average.getAverage());
576 					 *
577 					 * count = 0; }
578 					 *
579 					 * try{ Thread.sleep( 3 );
580 					 *
581 					 * }catch( Throwable e ){ } }
582 					 */
583 					long cstart = SystemTime.getCurrentTime();
584 					long mstart = SystemTime.getMonotonousTime();
585 					System.out.println("alter system clock to see differences between monotonous and current time");
586 					long cLastRound = cstart;
587 					long mLastRound = mstart;
588 					while (true)
589 					{
590 						long mnow = SystemTime.getMonotonousTime();
591 						long cnow = SystemTime.getCurrentTime();
592 						//if(mLastRound > mnow)
593 						System.out.println("current: " + (cnow - cstart) + " monotonous:" + (mnow - mstart) + " delta current:" + (cnow - cLastRound) + " delta monotonous:" + (mnow - mLastRound));
594 						cLastRound = cnow;
595 						mLastRound = mnow;
596 						try
597 						{
598 							Thread.sleep(15);
599 						} catch (Throwable e)
600 						{}
601 					}
602 				}
603 			}.start();
604 		}
605 	}
606 }
607