// 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;
}
}
}
}