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