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; } } } }