|
|
@@ -5,6 +5,8 @@
|
|
|
using System.ComponentModel;
|
|
|
using System.Reactive.Concurrency;
|
|
|
using System.Reactive.Disposables;
|
|
|
+using System.Threading;
|
|
|
+using System.Threading.Tasks;
|
|
|
|
|
|
namespace System.Reactive.PlatformServices
|
|
|
{
|
|
|
@@ -39,7 +41,12 @@ namespace System.Reactive.PlatformServices
|
|
|
private readonly TimeSpan _period;
|
|
|
private readonly SerialDisposable _timer;
|
|
|
|
|
|
- private DateTimeOffset _lastTime;
|
|
|
+ /// <summary>
|
|
|
+ /// Use the Unix milliseconds for the current time
|
|
|
+ /// so it can be atomically read/written without locking.
|
|
|
+ /// </summary>
|
|
|
+ private long _lastTimeUnixMillis;
|
|
|
+
|
|
|
private EventHandler<SystemClockChangedEventArgs> _systemClockChanged;
|
|
|
|
|
|
private const int SYNC_MAXRETRIES = 100;
|
|
|
@@ -80,30 +87,47 @@ namespace System.Reactive.PlatformServices
|
|
|
{
|
|
|
_timer.Disposable = Disposable.Empty;
|
|
|
|
|
|
- var n = 0;
|
|
|
- do
|
|
|
+ var n = 0L;
|
|
|
+ for (; ; )
|
|
|
{
|
|
|
- _lastTime = SystemClock.UtcNow;
|
|
|
+ var now = SystemClock.UtcNow.ToUnixTimeMilliseconds();
|
|
|
+ Interlocked.Exchange(ref _lastTimeUnixMillis, now);
|
|
|
+
|
|
|
_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);
|
|
|
+ if (Math.Abs(SystemClock.UtcNow.ToUnixTimeMilliseconds() - now) <= SYNC_MAXDELTA)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (_timer.Disposable == Disposable.Empty)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (++n >= SYNC_MAXRETRIES)
|
|
|
+ {
|
|
|
+ Task.Delay((int)SYNC_MAXDELTA).Wait();
|
|
|
+ }
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
private void TimeChanged()
|
|
|
{
|
|
|
- var now = SystemClock.UtcNow;
|
|
|
- var diff = now - (_lastTime + _period);
|
|
|
- if (Math.Abs(diff.TotalMilliseconds) >= MAXERROR)
|
|
|
+ var newTime = SystemClock.UtcNow;
|
|
|
+ var now = newTime.ToUnixTimeMilliseconds();
|
|
|
+ var last = Volatile.Read(ref _lastTimeUnixMillis);
|
|
|
+
|
|
|
+ var oldTime = (long)(last + _period.TotalMilliseconds);
|
|
|
+ var diff = now - oldTime;
|
|
|
+ if (Math.Abs(diff) >= MAXERROR)
|
|
|
{
|
|
|
- _systemClockChanged?.Invoke(this, new SystemClockChangedEventArgs(_lastTime + _period, now));
|
|
|
+ _systemClockChanged?.Invoke(this, new SystemClockChangedEventArgs(
|
|
|
+ DateTimeOffset.FromUnixTimeMilliseconds(oldTime), newTime));
|
|
|
|
|
|
NewTimer();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- _lastTime = SystemClock.UtcNow;
|
|
|
+ Interlocked.Exchange(ref _lastTimeUnixMillis, SystemClock.UtcNow.ToUnixTimeMilliseconds());
|
|
|
}
|
|
|
}
|
|
|
}
|