diff --git a/Rx.NET/Source/src/System.Reactive/Internal/SystemClock.Default.cs b/Rx.NET/Source/src/System.Reactive/Internal/SystemClock.Default.cs
index 4c8c1a3937..a48bc8e1b7 100644
--- a/Rx.NET/Source/src/System.Reactive/Internal/SystemClock.Default.cs
+++ b/Rx.NET/Source/src/System.Reactive/Internal/SystemClock.Default.cs
@@ -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 @@ public class PeriodicTimerSystemClockMonitor : INotifySystemClockChanged
private readonly TimeSpan _period;
private readonly SerialDisposable _timer;
- private DateTimeOffset _lastTime;
+ ///
+ /// Use the Unix milliseconds for the current time
+ /// so it can be atomically read/written without locking.
+ ///
+ private long _lastTimeUnixMillis;
+
private EventHandler _systemClockChanged;
private const int SYNC_MAXRETRIES = 100;
@@ -80,30 +87,47 @@ private void NewTimer()
{
_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());
}
}
}