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