//****************************************************************************************************** // AsyncQueue.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 11/08/2012 - J. Ritchie Carroll / Stephen C. Wills // Generated original version of source code. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Concurrent; using System.Threading; using GSF.Threading; namespace GSF.Collections { /// /// Creates a fast, light-weight asynchronous processing queue with very low contention. /// /// Type of items to process. public class AsyncQueue { #region [ Members ] // Events /// /// Event that is raised if an exception is encountered while attempting to processing an item in the . /// /// /// Processing will not stop for any exceptions thrown by user processing function, but exceptions will be exposed through this event. /// public event EventHandler> ProcessException; // Fields private readonly ConcurrentQueue m_asyncQueue; private Action m_processItemFunction; private ISynchronizedOperation m_processItemOperation; private int m_enabled; #endregion #region [ Constructors ] /// /// Creates a new . /// public AsyncQueue() { m_asyncQueue = new ConcurrentQueue(); m_processItemOperation = new ShortSynchronizedOperation(TryProcessItem, OnProcessException); m_enabled = 1; } /// /// Creates a new . /// /// The type of synchronized operation to use to process items in the queue. public AsyncQueue(SynchronizedOperationType synchronizedOperationType) { m_asyncQueue = new ConcurrentQueue(); m_enabled = 1; switch (synchronizedOperationType) { case SynchronizedOperationType.Short: m_processItemOperation = new ShortSynchronizedOperation(TryProcessItem, OnProcessException); break; case SynchronizedOperationType.Long: m_processItemOperation = new LongSynchronizedOperation(TryProcessItem, OnProcessException); break; case SynchronizedOperationType.LongBackground: LongSynchronizedOperation processItemOperation = new LongSynchronizedOperation(TryProcessItem, OnProcessException); processItemOperation.IsBackground = true; m_processItemOperation = processItemOperation; break; case SynchronizedOperationType.Mixed: m_processItemOperation = new MixedSynchronizedOperation(TryProcessItem, OnProcessException); break; case SynchronizedOperationType.DedicatedBackground: m_processItemOperation = new DedicatedSynchronizedOperation(TryProcessItem, OnProcessException, true); break; case SynchronizedOperationType.DedicatedForeground: m_processItemOperation = new DedicatedSynchronizedOperation(TryProcessItem, OnProcessException, false); break; default: throw new ArgumentOutOfRangeException(nameof(synchronizedOperationType)); } } #endregion #region [ Properties ] /// /// Gets the total number of items currently in the queue. /// public int Count { get { return m_asyncQueue.Count; } } /// /// Gets or sets item processing function. /// public Action ProcessItemFunction { get { return Interlocked.CompareExchange(ref m_processItemFunction, null, null); } set { Interlocked.Exchange(ref m_processItemFunction, value); } } /// /// Gets or sets flag that enables or disables processing. /// public bool Enabled { get { return Interlocked.CompareExchange(ref m_enabled, 0, 0) != 0; } set { Interlocked.Exchange(ref m_enabled, value ? 1 : 0); } } #endregion #region [ Methods ] /// /// Enqueues an item for processing. /// /// Item to be queued for processing. public void Enqueue(T item) { if ((object)m_processItemFunction == null) throw new NullReferenceException("No item processing function has been assigned - cannot enqueue item for processing."); // Queue item for processing if (Enabled) { m_asyncQueue.Enqueue(item); m_processItemOperation.RunOnceAsync(); } } // Attempts to dequeue and process an item from the queue. private void TryProcessItem() { T item; if (m_asyncQueue.TryDequeue(out item)) { if (Enabled) m_processItemFunction(item); if (!m_asyncQueue.IsEmpty) m_processItemOperation.RunOnceAsync(); } } // Raises the ProcessException event. private void OnProcessException(Exception ex) { if ((object)ProcessException != null) ProcessException(this, new EventArgs(ex)); } #endregion } }