using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using PepperDash.Core;
namespace PepperDash_Essentials_Core.Queues
{
///
/// Threadsafe processing of queued items with pacing if required
///
public class GenericQueue : IQueue
{
private readonly string _key;
protected readonly CrestronQueue _queue;
protected readonly Thread _worker;
protected readonly CEvent _waitHandle = new CEvent();
private readonly bool _delayEnabled;
private readonly int _delayTime;
///
/// If the instance has been disposed.
///
public bool Disposed { get; private set; }
///
/// Constructor for generic queue with no pacing
///
/// Key
public GenericQueue(string key)
{
_key = key;
_queue = new CrestronQueue();
_worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running);
CrestronEnvironment.ProgramStatusEventHandler += programEvent =>
{
if (programEvent != eProgramStatusEventType.Stopping)
return;
Dispose();
};
}
///
/// Constructor for generic queue with no pacing
///
/// Key
/// Pacing in ms between actions
public GenericQueue(string key, int pacing)
: this(key)
{
_delayEnabled = pacing > 0;
_delayTime = pacing;
}
///
/// Thread callback
///
/// The action used to process dequeued items
/// Null when the thread is exited
private object ProcessQueue(object obj)
{
while (true)
{
IQueueMessage item = null;
if (_queue.Count > 0)
{
item = _queue.Dequeue();
if (item == null)
break;
}
if (item != null)
{
try
{
Debug.Console(2, this, "Processing queue item: '{0}'", item.ToString());
item.Dispatch();
if (_delayEnabled)
Thread.Sleep(_delayTime);
}
catch (Exception ex)
{
Debug.ConsoleWithLog(0, this, "Caught an exception in the Queue {0}\r{1}\r{2}", ex.Message, ex.InnerException, ex.StackTrace);
}
}
else _waitHandle.Wait();
}
return null;
}
public void Enqueue(IQueueMessage item)
{
_queue.Enqueue(item);
_waitHandle.Set();
}
///
/// Disposes the thread and cleans up resources. Thread cannot be restarted once
/// disposed.
///
public void Dispose()
{
Dispose(true);
CrestronEnvironment.GC.SuppressFinalize(this);
}
///
/// Actually does the disposing. If you override this method, be sure to either call the base implementation
/// or clean up all the resources yourself.
///
/// set to true unless called from finalizer
protected void Dispose(bool disposing)
{
if (Disposed)
return;
if (disposing)
{
Enqueue(null);
_worker.Join();
_waitHandle.Close();
}
Disposed = true;
}
~GenericQueue()
{
Dispose(false);
}
///
/// Key
///
public string Key
{
get { return _key; }
}
}
}