using System; using System.Collections; using System.Collections.Generic; using ICD.Common.Utils.EventArguments; using ICD.Common.Utils.Extensions; using ICD.Common.Utils.Timers; namespace ICD.Common.Utils.Collections { /// /// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval. /// public sealed class RateLimitedEventQueue : IEnumerable, ICollection, IDisposable { /// /// Raised to handle to the next item in the queue. /// public event EventHandler> OnItemDequeued; private readonly SafeTimer m_DequeueTimer; private readonly Queue m_Queue; private readonly SafeCriticalSection m_QueueSection; #region Properties /// /// Gets/sets the time between dequeues in milliseconds. /// public long BetweenMilliseconds { get; set; } public int Count { get { return m_QueueSection.Execute(() => m_Queue.Count); } } bool ICollection.IsSynchronized { get { return true; } } object ICollection.SyncRoot { get { return this; } } #endregion /// /// Constructor. /// public RateLimitedEventQueue() { m_Queue = new Queue(); m_QueueSection = new SafeCriticalSection(); m_DequeueTimer = SafeTimer.Stopped(DequeueTimerCallback); } #region Methods /// /// Release resources. /// public void Dispose() { OnItemDequeued = null; m_QueueSection.Enter(); try { m_DequeueTimer.Dispose(); } finally { m_QueueSection.Leave(); } } /// /// Enqueues the given item. /// /// public void Enqueue(T item) { m_QueueSection.Enter(); try { m_Queue.Enqueue(item); if (m_Queue.Count == 1) SendNext(); } finally { m_QueueSection.Leave(); } } /// /// Clears the queued items. /// public void Clear() { m_QueueSection.Enter(); try { m_DequeueTimer.Stop(); m_Queue.Clear(); } finally { m_QueueSection.Leave(); } } #endregion #region Private Methods /// /// Sends the next pulse in the queue. /// private void SendNext() { if (!m_QueueSection.TryEnter()) return; try { if (m_Queue.Count == 0) return; T item = m_Queue.Peek(); OnItemDequeued.Raise(this, new GenericEventArgs(item)); m_DequeueTimer.Reset(BetweenMilliseconds); } finally { m_QueueSection.Leave(); } } /// /// Called when the dequeue timer elapses. /// private void DequeueTimerCallback() { m_QueueSection.Enter(); try { m_Queue.Dequeue(); SendNext(); } finally { m_QueueSection.Leave(); } } #endregion #region IEnumerable/ICollection public IEnumerator GetEnumerator() { return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { m_QueueSection.Enter(); try { foreach (T item in this) { array.SetValue(item, index); index++; } } finally { m_QueueSection.Leave(); } } #endregion } }