SystemClock.Default.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System.ComponentModel;
  3. using System.Reactive.Concurrency;
  4. using System.Reactive.Disposables;
  5. namespace System.Reactive.PlatformServices
  6. {
  7. /// <summary>
  8. /// (Infrastructure) Provides access to the local system clock.
  9. /// </summary>
  10. [EditorBrowsable(EditorBrowsableState.Never)]
  11. public class DefaultSystemClock : ISystemClock
  12. {
  13. /// <summary>
  14. /// Gets the current time.
  15. /// </summary>
  16. public DateTimeOffset UtcNow
  17. {
  18. get { return DateTimeOffset.UtcNow; }
  19. }
  20. }
  21. internal class DefaultSystemClockMonitor : PeriodicTimerSystemClockMonitor
  22. {
  23. private static readonly TimeSpan DEFAULT_PERIOD = TimeSpan.FromSeconds(1);
  24. public DefaultSystemClockMonitor()
  25. : base(DEFAULT_PERIOD)
  26. {
  27. }
  28. }
  29. /// <summary>
  30. /// (Infrastructure) Monitors for system clock changes based on a periodic timer.
  31. /// </summary>
  32. [EditorBrowsable(EditorBrowsableState.Never)]
  33. public class PeriodicTimerSystemClockMonitor : INotifySystemClockChanged
  34. {
  35. private readonly TimeSpan _period;
  36. private readonly SerialDisposable _timer;
  37. private DateTimeOffset _lastTime;
  38. private EventHandler<SystemClockChangedEventArgs> _systemClockChanged;
  39. private const int SYNC_MAXRETRIES = 100;
  40. private const double SYNC_MAXDELTA = 10;
  41. private const int MAXERROR = 100;
  42. /// <summary>
  43. /// Creates a new monitor for system clock changes with the specified polling frequency.
  44. /// </summary>
  45. /// <param name="period">Polling frequency for system clock changes.</param>
  46. public PeriodicTimerSystemClockMonitor(TimeSpan period)
  47. {
  48. _period = period;
  49. _timer = new SerialDisposable();
  50. }
  51. /// <summary>
  52. /// Event that gets raised when a system clock change is detected.
  53. /// </summary>
  54. public event EventHandler<SystemClockChangedEventArgs> SystemClockChanged
  55. {
  56. add
  57. {
  58. NewTimer();
  59. _systemClockChanged += value;
  60. }
  61. remove
  62. {
  63. _systemClockChanged -= value;
  64. _timer.Disposable = Disposable.Empty;
  65. }
  66. }
  67. private void NewTimer()
  68. {
  69. _timer.Disposable = Disposable.Empty;
  70. var n = 0;
  71. do
  72. {
  73. _lastTime = SystemClock.UtcNow;
  74. _timer.Disposable = ConcurrencyAbstractionLayer.Current.StartPeriodicTimer(TimeChanged, _period);
  75. } while (Math.Abs((SystemClock.UtcNow - _lastTime).TotalMilliseconds) > SYNC_MAXDELTA && ++n < SYNC_MAXRETRIES);
  76. if (n >= SYNC_MAXRETRIES)
  77. throw new InvalidOperationException(Strings_Core.FAILED_CLOCK_MONITORING);
  78. }
  79. private void TimeChanged()
  80. {
  81. var now = SystemClock.UtcNow;
  82. var diff = now - (_lastTime + _period);
  83. if (Math.Abs(diff.TotalMilliseconds) >= MAXERROR)
  84. {
  85. var scc = _systemClockChanged;
  86. if (scc != null)
  87. scc(this, new SystemClockChangedEventArgs(_lastTime + _period, now));
  88. NewTimer();
  89. }
  90. else
  91. {
  92. _lastTime = SystemClock.UtcNow;
  93. }
  94. }
  95. }
  96. }