// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; namespace Tools.CrashReporter.CrashReportCommon { /// /// Thread-safe event counter for measuring approximate rate over a fixed period between now and a fixed time ago /// public class EventCounter { private class Bin { public int Count { get; set; } } /// /// Create an event counter /// /// The period over which events are counter. The time we keep them and calculate the average rate. /// The number of bins in which events are kept in the given period. public EventCounter(TimeSpan InAvePeriod, int InResolution) { Period = InAvePeriod; Resolution = InResolution; Bins = new Queue(Resolution); BinPeriod = TimeSpan.FromTicks(Period.Ticks/Resolution); ActiveBin = new Bin(); Bins.Enqueue(ActiveBin); Count = 0; Timestamp = DateTime.UtcNow; } /// /// Adds events to the counter. Keeps track of internal bins and updates them keeping only those in our Period. /// /// The number of events to add public void AddEvent(int InCount = 1) { lock (ThisLock) { TotalCount += InCount; DateTime EventTime = DateTime.UtcNow; while (EventTime - Timestamp > BinPeriod) { ActiveBin = new Bin(); Bins.Enqueue(ActiveBin); Timestamp += BinPeriod; } Count += InCount; ActiveBin.Count += InCount; while (Bins.Count > Resolution) { Count -= Bins.Dequeue().Count; } } } /// /// Count of all events since the counter was created. /// public int TotalEvents { get { return TotalCount; } } /// /// Count of all events currently in the time period we're interested in (Period) preceding now. /// public int EventsInPeriod { get { return Count; } } /// /// Current rate of events average over the period. /// public double EventsPerSecond { get { lock (ThisLock) { return Count/(BinPeriod.TotalSeconds*Bins.Count); } } } private DateTime Timestamp; private readonly TimeSpan Period; private readonly int Resolution; private readonly TimeSpan BinPeriod; private readonly Queue Bins; private Bin ActiveBin; private int TotalCount; private int Count; private readonly Object ThisLock = new Object(); } }