// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System.ComponentModel; using System.Reactive.Concurrency; using System.Reactive.Disposables; namespace System.Reactive.PlatformServices { /// /// (Infrastructure) Provides access to the local system clock. /// [EditorBrowsable(EditorBrowsableState.Never)] public class DefaultSystemClock : ISystemClock { /// /// Gets the current time. /// public DateTimeOffset UtcNow { get { return DateTimeOffset.UtcNow; } } } internal class DefaultSystemClockMonitor : PeriodicTimerSystemClockMonitor { private static readonly TimeSpan DEFAULT_PERIOD = TimeSpan.FromSeconds(1); public DefaultSystemClockMonitor() : base(DEFAULT_PERIOD) { } } /// /// (Infrastructure) Monitors for system clock changes based on a periodic timer. /// [EditorBrowsable(EditorBrowsableState.Never)] public class PeriodicTimerSystemClockMonitor : INotifySystemClockChanged { private readonly TimeSpan _period; private readonly SerialDisposable _timer; private DateTimeOffset _lastTime; private EventHandler _systemClockChanged; private const int SYNC_MAXRETRIES = 100; private const double SYNC_MAXDELTA = 10; private const int MAXERROR = 100; /// /// Creates a new monitor for system clock changes with the specified polling frequency. /// /// Polling frequency for system clock changes. public PeriodicTimerSystemClockMonitor(TimeSpan period) { _period = period; _timer = new SerialDisposable(); } /// /// Event that gets raised when a system clock change is detected. /// public event EventHandler SystemClockChanged { add { NewTimer(); _systemClockChanged += value; } remove { _systemClockChanged -= value; _timer.Disposable = Disposable.Empty; } } private void NewTimer() { _timer.Disposable = Disposable.Empty; var n = 0; do { _lastTime = SystemClock.UtcNow; _timer.Disposable = ConcurrencyAbstractionLayer.Current.StartPeriodicTimer(TimeChanged, _period); } while (Math.Abs((SystemClock.UtcNow - _lastTime).TotalMilliseconds) > SYNC_MAXDELTA && ++n < SYNC_MAXRETRIES); if (n >= SYNC_MAXRETRIES) throw new InvalidOperationException(Strings_Core.FAILED_CLOCK_MONITORING); } private void TimeChanged() { var now = SystemClock.UtcNow; var diff = now - (_lastTime + _period); if (Math.Abs(diff.TotalMilliseconds) >= MAXERROR) { var scc = _systemClockChanged; if (scc != null) scc(this, new SystemClockChangedEventArgs(_lastTime + _period, now)); NewTimer(); } else { _lastTime = SystemClock.UtcNow; } } } }