mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-01-28 20:04:59 +00:00
183 lines
3.2 KiB
C#
183 lines
3.2 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
|
|
/// </summary>
|
|
public sealed class RateLimitedEventQueue<T> : IEnumerable<T>, ICollection, IDisposable
|
|
{
|
|
/// <summary>
|
|
/// Raised to handle to the next item in the queue.
|
|
/// </summary>
|
|
public event EventHandler<GenericEventArgs<T>> OnItemDequeued;
|
|
|
|
private readonly SafeTimer m_DequeueTimer;
|
|
private readonly Queue<T> m_Queue;
|
|
private readonly SafeCriticalSection m_QueueSection;
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Gets/sets the time between dequeues in milliseconds.
|
|
/// </summary>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
public RateLimitedEventQueue()
|
|
{
|
|
m_Queue = new Queue<T>();
|
|
m_QueueSection = new SafeCriticalSection();
|
|
|
|
m_DequeueTimer = SafeTimer.Stopped(DequeueTimerCallback);
|
|
}
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// Release resources.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
OnItemDequeued = null;
|
|
|
|
m_DequeueTimer.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enqueues the given item.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
public void Enqueue(T item)
|
|
{
|
|
m_QueueSection.Enter();
|
|
|
|
try
|
|
{
|
|
m_Queue.Enqueue(item);
|
|
|
|
if (m_Queue.Count == 1)
|
|
SendNext();
|
|
}
|
|
finally
|
|
{
|
|
m_QueueSection.Leave();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the queued items.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
m_QueueSection.Enter();
|
|
|
|
try
|
|
{
|
|
m_DequeueTimer.Stop();
|
|
m_Queue.Clear();
|
|
}
|
|
finally
|
|
{
|
|
m_QueueSection.Leave();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// Sends the next pulse in the queue.
|
|
/// </summary>
|
|
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<T>(item));
|
|
|
|
m_DequeueTimer.Reset(BetweenMilliseconds);
|
|
}
|
|
finally
|
|
{
|
|
m_QueueSection.Leave();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the dequeue timer elapses.
|
|
/// </summary>
|
|
private void DequeueTimerCallback()
|
|
{
|
|
m_QueueSection.Enter();
|
|
|
|
try
|
|
{
|
|
m_Queue.Dequeue();
|
|
SendNext();
|
|
}
|
|
finally
|
|
{
|
|
m_QueueSection.Leave();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable/ICollection
|
|
|
|
public IEnumerator<T> 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
|
|
}
|
|
}
|