SystemClock.Default.cs 3.6 KB

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