namespace Unosquare.Swan.Components { using System; using System.Diagnostics; using Abstractions; /// /// A time measurement artifact. /// internal sealed class RealTimeClock : IDisposable { private readonly Stopwatch _chrono = new Stopwatch(); private ISyncLocker _locker = SyncLockerFactory.Create(useSlim: true); private long _offsetTicks; private double _speedRatio = 1.0d; private bool _isDisposed; /// /// Initializes a new instance of the class. /// The clock starts paused and at the 0 position. /// public RealTimeClock() { Reset(); } /// /// Gets or sets the clock position. /// public TimeSpan Position { get { using (_locker.AcquireReaderLock()) { return TimeSpan.FromTicks( _offsetTicks + Convert.ToInt64(_chrono.Elapsed.Ticks * SpeedRatio)); } } } /// /// Gets a value indicating whether the clock is running. /// public bool IsRunning { get { using (_locker.AcquireReaderLock()) { return _chrono.IsRunning; } } } /// /// Gets or sets the speed ratio at which the clock runs. /// public double SpeedRatio { get { using (_locker.AcquireReaderLock()) { return _speedRatio; } } set { using (_locker.AcquireWriterLock()) { if (value < 0d) value = 0d; // Capture the initial position se we set it even after the speedratio has changed // this ensures a smooth position transition var initialPosition = Position; _speedRatio = value; Update(initialPosition); } } } /// /// Sets a new position value atomically. /// /// The new value that the position porperty will hold. public void Update(TimeSpan value) { using (_locker.AcquireWriterLock()) { var resume = _chrono.IsRunning; _chrono.Reset(); _offsetTicks = value.Ticks; if (resume) _chrono.Start(); } } /// /// Starts or resumes the clock. /// public void Play() { using (_locker.AcquireWriterLock()) { if (_chrono.IsRunning) return; _chrono.Start(); } } /// /// Pauses the clock. /// public void Pause() { using (_locker.AcquireWriterLock()) { _chrono.Stop(); } } /// /// Sets the clock position to 0 and stops it. /// The speed ratio is not modified. /// public void Reset() { using (_locker.AcquireWriterLock()) { _offsetTicks = 0; _chrono.Reset(); } } /// public void Dispose() { if (_isDisposed) return; _isDisposed = true; _locker?.Dispose(); _locker = null; } } }