Animatroller.Framework.Controller.HighPrecisionTimer.HighPrecisionTimer C# (CSharp) Method

HighPrecisionTimer() public method

public HighPrecisionTimer ( int intervalMs, bool startRunning = true ) : System
intervalMs int
startRunning bool
return System
        public HighPrecisionTimer(int intervalMs, bool startRunning = true)
        {
            log.Info("Starting HighPrecisionTimer with {0} ms interval", intervalMs);

            if (intervalMs < 1)
                throw new ArgumentOutOfRangeException();
            System.Diagnostics.Trace.Assert(intervalMs >= 10, "Not reliable/tested, may use too much CPU");

            this.IntervalMs = intervalMs;
            this.cancelSource = new CancellationTokenSource();
            this.taskComplete = new ManualResetEvent(false);

            #if PROFILE
            // Used to report timing accuracy for 1 sec, running total
            tickTiming = new CircularBuffer.CircularBuffer<int>(1000 / intervalMs, true);
            execTiming = new CircularBuffer.CircularBuffer<long>(1000 / intervalMs, true);
            #endif

            var watch = System.Diagnostics.Stopwatch.StartNew();
            long durationMs = 0;
            long totalTicks = 0;
            long nextStop = intervalMs;
            #if PROFILE
            long lastReport = 0;
            #endif

            this.task = new Task(() =>
                {
                    var eventArgs = new TickEventArgs();

                    while (!this.cancelSource.IsCancellationRequested)
                    {
                        long msLeft = nextStop - watch.ElapsedMilliseconds;
                        if (msLeft <= 0)
                        {
                            durationMs = watch.ElapsedMilliseconds;
                            totalTicks = durationMs / intervalMs;

            #if PROFILE
                            var execWatch = System.Diagnostics.Stopwatch.StartNew();
            #endif
                            var handler = Tick;
                            if (handler != null)
                            {
                                eventArgs.Duration = TimeSpan.FromMilliseconds(durationMs);
                                eventArgs.TotalTicks = totalTicks;
                                handler(this, eventArgs);
                                if (eventArgs.Cancel)
                                    break;
                            }
            #if PROFILE
                            execWatch.Stop();
                            execTiming.Put(execWatch.ElapsedTicks);
                            tickTiming.Put((int)(durationMs - nextStop));

                            if (durationMs - lastReport >= 1000)
                            {
                                // Report
                                log.Debug("HighPTimer  avg: {0:F1}  best: {1}  worst: {2}   MaxExec: {3:N1}ms",
                                    tickTiming.Average(), tickTiming.Min(), tickTiming.Max(),
                                    TimeSpan.FromTicks(execTiming.Max()).TotalMilliseconds);

                                lastReport = durationMs;
                            }
            #endif

                            // Calculate when the next stop is. If we're too slow on the trigger then we'll skip ticks
                            nextStop = intervalMs * (watch.ElapsedMilliseconds / intervalMs + 1);
                            continue;
                        }
                        else if (msLeft < 16)
                        {
                            System.Threading.SpinWait.SpinUntil(() => watch.ElapsedMilliseconds >= nextStop);
                            continue;
                        }

                        System.Threading.Thread.Sleep(1);
                    }

                    this.taskComplete.Set();
                }, cancelSource.Token, TaskCreationOptions.LongRunning);

            if (startRunning)
                this.task.Start();
        }