//******************************************************************************************************
// ProcessQueue.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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php
//
// 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:
// ----------------------------------------------------------------------------------------------------
// 01/07/2006 - J. Ritchie Carroll
// Generated original version of source code.
// 02/12/2006 - J. Ritchie Carroll
// Added multi-item bulk processing functionality.
// 03/21/2007 - J. Ritchie Carroll
// Added "ItemsBeingProcessed" property to return current total number of items being processed.
// Added "Flush" method to allow any remaining items in queue to be processed before shutdown.
// 04/05/2007 - J. Ritchie Carroll
// Added "RequeueMode" properties to allow users to specify how data gets reinserted back into
// the list (prefix or suffix) after processing timeouts or exceptions.
// 07/12/2007 - Pinal C. Patel
// Modified the code for "Flush" method to correctly implement IDisposable interface.
// 08/01/2007 - J. Ritchie Carroll
// Added some minor optimizations where practical.
// 08/17/2007 - J. Ritchie Carroll
// Removed IDisposable implementation because of continued flushing errors.
// 08/17/2007 - Darrell Zuercher
// Edited code comments.
// 11/05/2007 - J. Ritchie Carroll
// Modified flush to complete tasks on calling thread - this avoids errors when timer
// gets disposed before flush call.
// 02/20/2008 - J. Ritchie Carroll
// Implemented standard IDisposable pattern.
// 09/11/2008 - J. Ritchie Carroll
// Converted to C#.
// 11/06/2008 - J. Ritchie Carroll
// Added CurrentStatistics property to return run-time statistics as a group.
// 02/23/2009 - Josh L. Patterson
// Edited Code Comments.
// 08/05/2009 - Josh L. Patterson
// Edited Code Comments.
// 09/14/2009 - Stephen C. Wills
// Added new header and license agreement.
// 01/04/2010 - J. Ritchie Carroll
// Removed hard thread abort from shutdown which will allow current processing items
// to complete before terminating thread.
// 06/21/2010 - Stephen C. Wills
// Modified Dispose to fix potential concurrency issues.
// 11/23/2011 - J. Ritchie Carroll
// Modified to support buffer optimized ISupportBinaryImage.
// 01/24/2012 - Pinal C. Patel
// Modified ProcessTimerThreadProc() method to perform null reference check to avoid an exception
// when the object is being disposed.
// 11/27/2012 - J. Ritchie Carroll
// Modified process queue to be based on ConcurrentQueue<T>. New ProcessList<T> class now assumes
// the List<T> based functionality of original code.
// 12/13/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using GSF.Units;
using Timer = System.Timers.Timer;
namespace GSF.Collections
{
#region [ Enumerations ]
/// <summary>
/// Enumeration of possible <see cref="ProcessQueue{T}"/> threading modes.
/// </summary>
public enum QueueThreadingMode
{
/// <summary>
/// Processes several items in the <see cref="ProcessQueue{T}"/> at once on different threads, where processing order is not important.
/// </summary>
Asynchronous,
/// <summary>
/// Processes items in the <see cref="ProcessQueue{T}"/> one at a time on a single thread, where processing order is important.
/// </summary>
Synchronous
}
/// <summary>
/// Enumeration of possible <see cref="ProcessQueue{T}"/> processing styles.
/// </summary>
public enum QueueProcessingStyle
{
/// <summary>
/// Defines <see cref="ProcessQueue{T}"/> processing delegate to process only one item at a time.
/// </summary>
/// <remarks>
/// This is the typical <see cref="QueueProcessingStyle"/> when the <see cref="QueueThreadingMode"/> is asynchronous.
/// </remarks>
OneAtATime,
/// <summary>
/// Defines <see cref="ProcessQueue{T}"/> processing delegate to process all currently available items in the <see cref="ProcessQueue{T}"/>.
/// Items are passed into delegate as an array.
/// </summary>
/// <remarks>
/// This is the optimal <see cref="QueueProcessingStyle"/> when the <see cref="QueueThreadingMode"/> is synchronous.
/// </remarks>
ManyAtOnce
}
/// <summary>
/// Enumeration of possible requeue reasons.
/// </summary>
public enum RequeueReason
{
/// <summary>
/// Requeuing item since it cannot be processed at this time.
/// </summary>
CannotProcess,
/// <summary>
/// Requeuing item due to an exception.
/// </summary>
Exception,
/// <summary>
/// Requeing item due to timeout.
/// </summary>
Timeout
}
#endregion
/// <summary>
/// Represents a lock-free thread-safe collection of items, based on <see cref="ConcurrentQueue{T}"/>, that get processed on independent threads with a consumer provided function.
/// </summary>
/// <typeparam name="T">Type of object to process</typeparam>
/// <remarks>
/// <para>This class acts as a strongly-typed collection of objects to be processed.</para>
/// <para>Consumers are expected to create new instances of this class through the static construction functions (e.g., CreateAsynchronousQueue, CreateSynchronousQueue, etc.)</para>
/// <para>Note that the <see cref="ProcessQueue{T}"/> will not start processing until the <see cref="ProcessQueue{T}.Start"/> method is called.</para>
/// </remarks>
public class ProcessQueue<T> : IProducerConsumerCollection<T>, IProvideStatus, ISupportLifecycle
{
#region [ Members ]
// Nested Types
// Limits item processing time, if requested.
private sealed class TemporalTask : IDisposable
{
private ProcessQueue<T> m_parent;
private Task m_task;
private readonly T m_item;
private readonly T[] m_items;
private bool m_disposed;
private TemporalTask(ProcessQueue<T> parent, T item)
{
m_parent = parent;
m_item = item;
m_task = Task.Factory.StartNew(ProcessItem);
}
private TemporalTask(ProcessQueue<T> parent, T[] items)
{
m_parent = parent;
m_items = items;
m_task = Task.Factory.StartNew(ProcessItems);
}
~TemporalTask()
{
Dispose(false);
}
private void ProcessItem()
{
m_parent.ProcessItem(m_item);
}
private void ProcessItems()
{
m_parent.ProcessItems(m_items);
}
// Blocks calling thread until specified process timeout has expired.
private bool Wait()
{
return m_task.Wait(m_parent.ProcessTimeout);
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!m_disposed)
{
try
{
if (disposing)
{
if (m_task != null)
m_task.Dispose();
m_task = null;
m_parent = null;
}
}
finally
{
m_disposed = true; // Prevent duplicate dispose.
}
}
}
public static bool Process(ProcessQueue<T> parent, T item)
{
using (TemporalTask temporalProcess = new TemporalTask(parent, item))
{
return temporalProcess.Wait();
}
}
public static bool Process(ProcessQueue<T> parent, T[] items)
{
using (TemporalTask temporalProcess = new TemporalTask(parent, items))
{
return temporalProcess.Wait();
}
}
}
// Constants
/// <summary>Default processing interval (in milliseconds).</summary>
public const int DefaultProcessInterval = 100;
/// <summary>Default maximum number of processing threads.</summary>
public const int DefaultMaximumThreads = 5;
/// <summary>Default processing timeout (in milliseconds).</summary>
public const int DefaultProcessTimeout = Timeout.Infinite;
/// <summary>Default setting for requeuing items on processing timeout.</summary>
public const bool DefaultRequeueOnTimeout = false;
/// <summary>Default setting for requeuing items on processing exceptions.</summary>
public const bool DefaultRequeueOnException = false;
/// <summary>Default real-time processing interval (in milliseconds).</summary>
public const double RealTimeProcessInterval = 0.0;
// Delegates
/// <summary>
/// Function signature that defines a method to process items one at a time.
/// </summary>
/// <param name="item">Item to be processed.</param>
/// <remarks>
/// <para>Required unless <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is implemented.</para>
/// <para>Creates an asynchronous <see cref="ProcessQueue{T}"/> to process individual items - one item at a time - on multiple threads.</para>
/// </remarks>
public delegate void ProcessItemFunctionSignature(T item);
/// <summary>
/// Function signature that defines a method to process multiple items at once.
/// </summary>
/// <param name="items">Items to be processed.</param>
/// <remarks>
/// <para>Required unless <see cref="ProcessQueue{T}.ProcessItemFunction"/> is implemented.</para>
/// <para>Creates an asynchronous <see cref="ProcessQueue{T}"/> to process groups of items simultaneously on multiple threads.</para>
/// </remarks>
public delegate void ProcessItemsFunctionSignature(T[] items);
/// <summary>
/// Function signature that determines if an item can be currently processed.
/// </summary>
/// <param name="item">Item to be checked for processing availablity.</param>
/// <returns>True, if item can be processed. The default is true.</returns>
/// <remarks>
/// <para>Implementation of this function is optional. It is assumed that an item can be processed if this
/// function is not defined</para>
/// <para>Items must eventually get to a state where they can be processed, or they will remain in the <see cref="ProcessQueue{T}"/>
/// indefinitely.</para>
/// <para>
/// Note that when this function is implemented and ProcessingStyle = ManyAtOnce (i.e.,
/// <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is defined), then each item presented for
/// processing must evaluate as "CanProcessItem = True" before any items are processed.
/// </para>
/// </remarks>
public delegate bool CanProcessItemFunctionSignature(T item);
// Events
/// <summary>
/// Event that is raised after an item has been successfully processed.
/// </summary>
/// <remarks>
/// <para>Allows custom handling of successfully processed items.</para>
/// <para>Allows notification when an item has completed processing in the allowed amount of time, if a process
/// timeout is specified.</para>
/// <para>Raised only when ProcessingStyle = OneAtATime (i.e., <see cref="ProcessQueue{T}.ProcessItemFunction"/> is defined).</para>
/// </remarks>
public event EventHandler<EventArgs<T>> ItemProcessed;
/// <summary>
/// Event that is raised after an array of items have been successfully processed.
/// </summary>
/// <remarks>
/// <para>Allows custom handling of successfully processed items.</para>
/// <para>Allows notification when an item has completed processing in the allowed amount of time, if a process
/// timeout is specified.</para>
/// <para>Raised only when when ProcessingStyle = ManyAtOnce (i.e., <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is defined).</para>
/// </remarks>
public event EventHandler<EventArgs<T[]>> ItemsProcessed;
/// <summary>
/// Event that is raised if an item's processing time exceeds the specified process timeout.
/// </summary>
/// <remarks>
/// <para>Allows custom handling of items that took too long to process.</para>
/// <para>Raised only when ProcessingStyle = OneAtATime (i.e., <see cref="ProcessQueue{T}.ProcessItemFunction"/> is defined).</para>
/// </remarks>
public event EventHandler<EventArgs<T>> ItemTimedOut;
/// <summary>
/// Event that is raised if the processing time for an array of items exceeds the specified process timeout.
/// </summary>
/// <remarks>
/// <para>Allows custom handling of items that took too long to process.</para>
/// <para>Raised only when ProcessingStyle = ManyAtOnce (i.e., <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is defined).</para>
/// </remarks>
public event EventHandler<EventArgs<T[]>> ItemsTimedOut;
/// <summary>
/// Event that is raised if an exception is encountered while attempting to processing an item in the <see cref="ProcessQueue{T}"/>.
/// </summary>
/// <remarks>
/// Processing will not stop for any exceptions thrown by the user function, but any captured exceptions will
/// be exposed through this event.
/// </remarks>
public event EventHandler<EventArgs<Exception>> ProcessException;
/// <summary>
/// Occurs when the class has been disposed.
/// </summary>
public event EventHandler Disposed;
// Fields
private ProcessItemFunctionSignature m_processItemFunction;
private ProcessItemsFunctionSignature m_processItemsFunction;
private CanProcessItemFunctionSignature m_canProcessItemFunction;
private IEnumerable<T> m_processQueue;
private int m_maximumThreads;
private int m_processTimeout;
private readonly bool m_processingIsRealTime;
private bool m_requeueOnTimeout;
private bool m_requeueOnException;
private volatile bool m_enabled;
private int m_processing;
private int m_threadCount;
private long m_itemsProcessing;
private long m_itemsProcessed;
private long m_startTime;
private long m_stopTime;
private string m_name;
private bool m_disposed;
#if ThreadTracking
private ManagedThread m_realTimeProcessThread;
#else
private Thread m_realTimeProcessThread;
#endif
private Timer m_processTimer;
private readonly object m_processLock;
#endregion
#region [ Constructors ]
/// <summary>
/// Creates a <see cref="ProcessQueue{T}"/> based on the generic List(Of T) class.
/// </summary>
/// <param name="processItemFunction">Delegate that defines a method to process one item at a time.</param>
/// <param name="canProcessItemFunction">Delegate that determines if an item can currently be processed.</param>
/// <param name="processInterval">a <see cref="double"/> value which represents the process interval in milliseconds.</param>
/// <param name="maximumThreads">The maximum number of threads for the queue to use.</param>
/// <param name="processTimeout">The number of seconds before a process should timeout.</param>
/// <param name="requeueOnTimeout">A <see cref="Boolean"/> value that indicates whether a process should requeue an item on timeout.</param>
/// <param name="requeueOnException">A <see cref="Boolean"/> value that indicates whether a process should requeue after an exception.</param>
protected ProcessQueue(ProcessItemFunctionSignature processItemFunction, CanProcessItemFunctionSignature canProcessItemFunction, double processInterval, int maximumThreads, int processTimeout, bool requeueOnTimeout, bool requeueOnException)
: this(processItemFunction, null, canProcessItemFunction, new ConcurrentQueue<T>(), processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException)
{
}
/// <summary>
/// Creates a bulk item <see cref="ProcessQueue{T}"/> based on the generic List(Of T) class.
/// </summary>
/// <param name="processItemsFunction">Delegate that defines a method to process multiple items at once.</param>
/// <param name="canProcessItemFunction">Delegate that determines if an item can currently be processed.</param>
/// <param name="processInterval">a <see cref="double"/> value which represents the process interval in milliseconds.</param>
/// <param name="maximumThreads">The maximum number of threads for the queue to use.</param>
/// <param name="processTimeout">The number of seconds before a process should timeout.</param>
/// <param name="requeueOnTimeout">A <see cref="Boolean"/> value that indicates whether a process should requeue an item on timeout.</param>
/// <param name="requeueOnException">A <see cref="Boolean"/> value that indicates whether a process should requeue after an exception.</param>
protected ProcessQueue(ProcessItemsFunctionSignature processItemsFunction, CanProcessItemFunctionSignature canProcessItemFunction, double processInterval, int maximumThreads, int processTimeout, bool requeueOnTimeout, bool requeueOnException)
: this(null, processItemsFunction, canProcessItemFunction, new ConcurrentQueue<T>(), processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException)
{
}
/// <summary>
/// Allows derived classes to define their own instance, if desired.
/// </summary>
/// <param name="processItemFunction">Delegate that defines a method to process one item at a time.</param>
/// <param name="processItemsFunction">Delegate that defines a method to process multiple items at once.</param>
/// <param name="canProcessItemFunction">Delegate that determines if an item can currently be processed.</param>
/// <param name="processQueue">A storage list for items to be processed.</param>
/// <param name="processInterval">a <see cref="double"/> value which represents the process interval in milliseconds.</param>
/// <param name="maximumThreads">The maximum number of threads for the queue to use.</param>
/// <param name="processTimeout">The number of seconds before a process should timeout.</param>
/// <param name="requeueOnTimeout">A <see cref="Boolean"/> value that indicates whether a process should requeue an item on timeout.</param>
/// <param name="requeueOnException">A <see cref="Boolean"/> value that indicates whether a process should requeue after an exception.</param>
protected ProcessQueue(ProcessItemFunctionSignature processItemFunction, ProcessItemsFunctionSignature processItemsFunction, CanProcessItemFunctionSignature canProcessItemFunction, IEnumerable<T> processQueue, double processInterval, int maximumThreads, int processTimeout, bool requeueOnTimeout, bool requeueOnException)
{
m_processItemFunction = processItemFunction; // Defining this function creates a ProcessingStyle = OneAtATime process queue
m_processItemsFunction = processItemsFunction; // Defining this function creates a ProcessingStyle = ManyAtOnce process queue
m_canProcessItemFunction = canProcessItemFunction;
m_processQueue = processQueue;
m_maximumThreads = maximumThreads;
m_processTimeout = processTimeout;
m_requeueOnTimeout = requeueOnTimeout;
m_requeueOnException = requeueOnException;
m_processLock = new object();
if (processInterval == RealTimeProcessInterval)
{
// Instantiates process queue for real-time item processing
m_processingIsRealTime = true;
m_maximumThreads = 1;
}
else
{
// Instantiates process queue for intervaled item processing
m_processTimer = new Timer();
m_processTimer.Elapsed += ProcessTimerThreadProc;
m_processTimer.Interval = processInterval;
m_processTimer.AutoReset = true;
m_processTimer.Enabled = false;
}
}
/// <summary>
/// Releases the unmanaged resources before the <see cref="ProcessQueue{T}"/> object is reclaimed by <see cref="GC"/>.
/// </summary>
~ProcessQueue()
{
Dispose(false);
}
#endregion
#region [ Properties ]
/// <summary>
/// Gets or sets the user function for processing individual items in the <see cref="ProcessQueue{T}"/> one at a time.
/// </summary>
/// <remarks>
/// <para>Cannot be defined simultaneously with <see cref="ProcessQueue{T}.ProcessItemsFunction"/>.</para>
/// <para>A <see cref="ProcessQueue{T}"/> must be defined to process a single item at a time or many items at once.</para>
/// <para>Implementation makes ProcessingStyle = OneAtATime.</para>
/// </remarks>
public virtual ProcessItemFunctionSignature ProcessItemFunction
{
get
{
return m_processItemFunction;
}
set
{
if ((object)value != null)
{
Lines 1–500 of 2252. Load more
//****************************************************************************************************** // ProcessQueue.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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php // // 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: // ---------------------------------------------------------------------------------------------------- // 01/07/2006 - J. Ritchie Carroll // Generated original version of source code. // 02/12/2006 - J. Ritchie Carroll // Added multi-item bulk processing functionality. // 03/21/2007 - J. Ritchie Carroll // Added "ItemsBeingProcessed" property to return current total number of items being processed. // Added "Flush" method to allow any remaining items in queue to be processed before shutdown. // 04/05/2007 - J. Ritchie Carroll // Added "RequeueMode" properties to allow users to specify how data gets reinserted back into // the list (prefix or suffix) after processing timeouts or exceptions. // 07/12/2007 - Pinal C. Patel // Modified the code for "Flush" method to correctly implement IDisposable interface. // 08/01/2007 - J. Ritchie Carroll // Added some minor optimizations where practical. // 08/17/2007 - J. Ritchie Carroll // Removed IDisposable implementation because of continued flushing errors. // 08/17/2007 - Darrell Zuercher // Edited code comments. // 11/05/2007 - J. Ritchie Carroll // Modified flush to complete tasks on calling thread - this avoids errors when timer // gets disposed before flush call. // 02/20/2008 - J. Ritchie Carroll // Implemented standard IDisposable pattern. // 09/11/2008 - J. Ritchie Carroll // Converted to C#. // 11/06/2008 - J. Ritchie Carroll // Added CurrentStatistics property to return run-time statistics as a group. // 02/23/2009 - Josh L. Patterson // Edited Code Comments. // 08/05/2009 - Josh L. Patterson // Edited Code Comments. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 01/04/2010 - J. Ritchie Carroll // Removed hard thread abort from shutdown which will allow current processing items // to complete before terminating thread. // 06/21/2010 - Stephen C. Wills // Modified Dispose to fix potential concurrency issues. // 11/23/2011 - J. Ritchie Carroll // Modified to support buffer optimized ISupportBinaryImage. // 01/24/2012 - Pinal C. Patel // Modified ProcessTimerThreadProc() method to perform null reference check to avoid an exception // when the object is being disposed. // 11/27/2012 - J. Ritchie Carroll // Modified process queue to be based on ConcurrentQueue<T>. New ProcessList<T> class now assumes // the List<T> based functionality of original code. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Timers; using GSF.Units; using Timer = System.Timers.Timer; namespace GSF.Collections { #region [ Enumerations ] /// <summary> /// Enumeration of possible <see cref="ProcessQueue{T}"/> threading modes. /// </summary> public enum QueueThreadingMode { /// <summary> /// Processes several items in the <see cref="ProcessQueue{T}"/> at once on different threads, where processing order is not important. /// </summary> Asynchronous, /// <summary> /// Processes items in the <see cref="ProcessQueue{T}"/> one at a time on a single thread, where processing order is important. /// </summary> Synchronous } /// <summary> /// Enumeration of possible <see cref="ProcessQueue{T}"/> processing styles. /// </summary> public enum QueueProcessingStyle { /// <summary> /// Defines <see cref="ProcessQueue{T}"/> processing delegate to process only one item at a time. /// </summary> /// <remarks> /// This is the typical <see cref="QueueProcessingStyle"/> when the <see cref="QueueThreadingMode"/> is asynchronous. /// </remarks> OneAtATime, /// <summary> /// Defines <see cref="ProcessQueue{T}"/> processing delegate to process all currently available items in the <see cref="ProcessQueue{T}"/>. /// Items are passed into delegate as an array. /// </summary> /// <remarks> /// This is the optimal <see cref="QueueProcessingStyle"/> when the <see cref="QueueThreadingMode"/> is synchronous. /// </remarks> ManyAtOnce } /// <summary> /// Enumeration of possible requeue reasons. /// </summary> public enum RequeueReason { /// <summary> /// Requeuing item since it cannot be processed at this time. /// </summary> CannotProcess, /// <summary> /// Requeuing item due to an exception. /// </summary> Exception, /// <summary> /// Requeing item due to timeout. /// </summary> Timeout } #endregion /// <summary> /// Represents a lock-free thread-safe collection of items, based on <see cref="ConcurrentQueue{T}"/>, that get processed on independent threads with a consumer provided function. /// </summary> /// <typeparam name="T">Type of object to process</typeparam> /// <remarks> /// <para>This class acts as a strongly-typed collection of objects to be processed.</para> /// <para>Consumers are expected to create new instances of this class through the static construction functions (e.g., CreateAsynchronousQueue, CreateSynchronousQueue, etc.)</para> /// <para>Note that the <see cref="ProcessQueue{T}"/> will not start processing until the <see cref="ProcessQueue{T}.Start"/> method is called.</para> /// </remarks> public class ProcessQueue<T> : IProducerConsumerCollection<T>, IProvideStatus, ISupportLifecycle { #region [ Members ] // Nested Types // Limits item processing time, if requested. private sealed class TemporalTask : IDisposable { private ProcessQueue<T> m_parent; private Task m_task; private readonly T m_item; private readonly T[] m_items; private bool m_disposed; private TemporalTask(ProcessQueue<T> parent, T item) { m_parent = parent; m_item = item; m_task = Task.Factory.StartNew(ProcessItem); } private TemporalTask(ProcessQueue<T> parent, T[] items) { m_parent = parent; m_items = items; m_task = Task.Factory.StartNew(ProcessItems); } ~TemporalTask() { Dispose(false); } private void ProcessItem() { m_parent.ProcessItem(m_item); } private void ProcessItems() { m_parent.ProcessItems(m_items); } // Blocks calling thread until specified process timeout has expired. private bool Wait() { return m_task.Wait(m_parent.ProcessTimeout); } void IDisposable.Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_task != null) m_task.Dispose(); m_task = null; m_parent = null; } } finally { m_disposed = true; // Prevent duplicate dispose. } } } public static bool Process(ProcessQueue<T> parent, T item) { using (TemporalTask temporalProcess = new TemporalTask(parent, item)) { return temporalProcess.Wait(); } } public static bool Process(ProcessQueue<T> parent, T[] items) { using (TemporalTask temporalProcess = new TemporalTask(parent, items)) { return temporalProcess.Wait(); } } } // Constants /// <summary>Default processing interval (in milliseconds).</summary> public const int DefaultProcessInterval = 100; /// <summary>Default maximum number of processing threads.</summary> public const int DefaultMaximumThreads = 5; /// <summary>Default processing timeout (in milliseconds).</summary> public const int DefaultProcessTimeout = Timeout.Infinite; /// <summary>Default setting for requeuing items on processing timeout.</summary> public const bool DefaultRequeueOnTimeout = false; /// <summary>Default setting for requeuing items on processing exceptions.</summary> public const bool DefaultRequeueOnException = false; /// <summary>Default real-time processing interval (in milliseconds).</summary> public const double RealTimeProcessInterval = 0.0; // Delegates /// <summary> /// Function signature that defines a method to process items one at a time. /// </summary> /// <param name="item">Item to be processed.</param> /// <remarks> /// <para>Required unless <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is implemented.</para> /// <para>Creates an asynchronous <see cref="ProcessQueue{T}"/> to process individual items - one item at a time - on multiple threads.</para> /// </remarks> public delegate void ProcessItemFunctionSignature(T item); /// <summary> /// Function signature that defines a method to process multiple items at once. /// </summary> /// <param name="items">Items to be processed.</param> /// <remarks> /// <para>Required unless <see cref="ProcessQueue{T}.ProcessItemFunction"/> is implemented.</para> /// <para>Creates an asynchronous <see cref="ProcessQueue{T}"/> to process groups of items simultaneously on multiple threads.</para> /// </remarks> public delegate void ProcessItemsFunctionSignature(T[] items); /// <summary> /// Function signature that determines if an item can be currently processed. /// </summary> /// <param name="item">Item to be checked for processing availablity.</param> /// <returns>True, if item can be processed. The default is true.</returns> /// <remarks> /// <para>Implementation of this function is optional. It is assumed that an item can be processed if this /// function is not defined</para> /// <para>Items must eventually get to a state where they can be processed, or they will remain in the <see cref="ProcessQueue{T}"/> /// indefinitely.</para> /// <para> /// Note that when this function is implemented and ProcessingStyle = ManyAtOnce (i.e., /// <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is defined), then each item presented for /// processing must evaluate as "CanProcessItem = True" before any items are processed. /// </para> /// </remarks> public delegate bool CanProcessItemFunctionSignature(T item); // Events /// <summary> /// Event that is raised after an item has been successfully processed. /// </summary> /// <remarks> /// <para>Allows custom handling of successfully processed items.</para> /// <para>Allows notification when an item has completed processing in the allowed amount of time, if a process /// timeout is specified.</para> /// <para>Raised only when ProcessingStyle = OneAtATime (i.e., <see cref="ProcessQueue{T}.ProcessItemFunction"/> is defined).</para> /// </remarks> public event EventHandler<EventArgs<T>> ItemProcessed; /// <summary> /// Event that is raised after an array of items have been successfully processed. /// </summary> /// <remarks> /// <para>Allows custom handling of successfully processed items.</para> /// <para>Allows notification when an item has completed processing in the allowed amount of time, if a process /// timeout is specified.</para> /// <para>Raised only when when ProcessingStyle = ManyAtOnce (i.e., <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is defined).</para> /// </remarks> public event EventHandler<EventArgs<T[]>> ItemsProcessed; /// <summary> /// Event that is raised if an item's processing time exceeds the specified process timeout. /// </summary> /// <remarks> /// <para>Allows custom handling of items that took too long to process.</para> /// <para>Raised only when ProcessingStyle = OneAtATime (i.e., <see cref="ProcessQueue{T}.ProcessItemFunction"/> is defined).</para> /// </remarks> public event EventHandler<EventArgs<T>> ItemTimedOut; /// <summary> /// Event that is raised if the processing time for an array of items exceeds the specified process timeout. /// </summary> /// <remarks> /// <para>Allows custom handling of items that took too long to process.</para> /// <para>Raised only when ProcessingStyle = ManyAtOnce (i.e., <see cref="ProcessQueue{T}.ProcessItemsFunction"/> is defined).</para> /// </remarks> public event EventHandler<EventArgs<T[]>> ItemsTimedOut; /// <summary> /// Event that is raised if an exception is encountered while attempting to processing an item in the <see cref="ProcessQueue{T}"/>. /// </summary> /// <remarks> /// Processing will not stop for any exceptions thrown by the user function, but any captured exceptions will /// be exposed through this event. /// </remarks> public event EventHandler<EventArgs<Exception>> ProcessException; /// <summary> /// Occurs when the class has been disposed. /// </summary> public event EventHandler Disposed; // Fields private ProcessItemFunctionSignature m_processItemFunction; private ProcessItemsFunctionSignature m_processItemsFunction; private CanProcessItemFunctionSignature m_canProcessItemFunction; private IEnumerable<T> m_processQueue; private int m_maximumThreads; private int m_processTimeout; private readonly bool m_processingIsRealTime; private bool m_requeueOnTimeout; private bool m_requeueOnException; private volatile bool m_enabled; private int m_processing; private int m_threadCount; private long m_itemsProcessing; private long m_itemsProcessed; private long m_startTime; private long m_stopTime; private string m_name; private bool m_disposed; #if ThreadTracking private ManagedThread m_realTimeProcessThread; #else private Thread m_realTimeProcessThread; #endif private Timer m_processTimer; private readonly object m_processLock; #endregion #region [ Constructors ] /// <summary> /// Creates a <see cref="ProcessQueue{T}"/> based on the generic List(Of T) class. /// </summary> /// <param name="processItemFunction">Delegate that defines a method to process one item at a time.</param> /// <param name="canProcessItemFunction">Delegate that determines if an item can currently be processed.</param> /// <param name="processInterval">a <see cref="double"/> value which represents the process interval in milliseconds.</param> /// <param name="maximumThreads">The maximum number of threads for the queue to use.</param> /// <param name="processTimeout">The number of seconds before a process should timeout.</param> /// <param name="requeueOnTimeout">A <see cref="Boolean"/> value that indicates whether a process should requeue an item on timeout.</param> /// <param name="requeueOnException">A <see cref="Boolean"/> value that indicates whether a process should requeue after an exception.</param> protected ProcessQueue(ProcessItemFunctionSignature processItemFunction, CanProcessItemFunctionSignature canProcessItemFunction, double processInterval, int maximumThreads, int processTimeout, bool requeueOnTimeout, bool requeueOnException) : this(processItemFunction, null, canProcessItemFunction, new ConcurrentQueue<T>(), processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException) { } /// <summary> /// Creates a bulk item <see cref="ProcessQueue{T}"/> based on the generic List(Of T) class. /// </summary> /// <param name="processItemsFunction">Delegate that defines a method to process multiple items at once.</param> /// <param name="canProcessItemFunction">Delegate that determines if an item can currently be processed.</param> /// <param name="processInterval">a <see cref="double"/> value which represents the process interval in milliseconds.</param> /// <param name="maximumThreads">The maximum number of threads for the queue to use.</param> /// <param name="processTimeout">The number of seconds before a process should timeout.</param> /// <param name="requeueOnTimeout">A <see cref="Boolean"/> value that indicates whether a process should requeue an item on timeout.</param> /// <param name="requeueOnException">A <see cref="Boolean"/> value that indicates whether a process should requeue after an exception.</param> protected ProcessQueue(ProcessItemsFunctionSignature processItemsFunction, CanProcessItemFunctionSignature canProcessItemFunction, double processInterval, int maximumThreads, int processTimeout, bool requeueOnTimeout, bool requeueOnException) : this(null, processItemsFunction, canProcessItemFunction, new ConcurrentQueue<T>(), processInterval, maximumThreads, processTimeout, requeueOnTimeout, requeueOnException) { } /// <summary> /// Allows derived classes to define their own instance, if desired. /// </summary> /// <param name="processItemFunction">Delegate that defines a method to process one item at a time.</param> /// <param name="processItemsFunction">Delegate that defines a method to process multiple items at once.</param> /// <param name="canProcessItemFunction">Delegate that determines if an item can currently be processed.</param> /// <param name="processQueue">A storage list for items to be processed.</param> /// <param name="processInterval">a <see cref="double"/> value which represents the process interval in milliseconds.</param> /// <param name="maximumThreads">The maximum number of threads for the queue to use.</param> /// <param name="processTimeout">The number of seconds before a process should timeout.</param> /// <param name="requeueOnTimeout">A <see cref="Boolean"/> value that indicates whether a process should requeue an item on timeout.</param> /// <param name="requeueOnException">A <see cref="Boolean"/> value that indicates whether a process should requeue after an exception.</param> protected ProcessQueue(ProcessItemFunctionSignature processItemFunction, ProcessItemsFunctionSignature processItemsFunction, CanProcessItemFunctionSignature canProcessItemFunction, IEnumerable<T> processQueue, double processInterval, int maximumThreads, int processTimeout, bool requeueOnTimeout, bool requeueOnException) { m_processItemFunction = processItemFunction; // Defining this function creates a ProcessingStyle = OneAtATime process queue m_processItemsFunction = processItemsFunction; // Defining this function creates a ProcessingStyle = ManyAtOnce process queue m_canProcessItemFunction = canProcessItemFunction; m_processQueue = processQueue; m_maximumThreads = maximumThreads; m_processTimeout = processTimeout; m_requeueOnTimeout = requeueOnTimeout; m_requeueOnException = requeueOnException; m_processLock = new object(); if (processInterval == RealTimeProcessInterval) { // Instantiates process queue for real-time item processing m_processingIsRealTime = true; m_maximumThreads = 1; } else { // Instantiates process queue for intervaled item processing m_processTimer = new Timer(); m_processTimer.Elapsed += ProcessTimerThreadProc; m_processTimer.Interval = processInterval; m_processTimer.AutoReset = true; m_processTimer.Enabled = false; } } /// <summary> /// Releases the unmanaged resources before the <see cref="ProcessQueue{T}"/> object is reclaimed by <see cref="GC"/>. /// </summary> ~ProcessQueue() { Dispose(false); } #endregion #region [ Properties ] /// <summary> /// Gets or sets the user function for processing individual items in the <see cref="ProcessQueue{T}"/> one at a time. /// </summary> /// <remarks> /// <para>Cannot be defined simultaneously with <see cref="ProcessQueue{T}.ProcessItemsFunction"/>.</para> /// <para>A <see cref="ProcessQueue{T}"/> must be defined to process a single item at a time or many items at once.</para> /// <para>Implementation makes ProcessingStyle = OneAtATime.</para> /// </remarks> public virtual ProcessItemFunctionSignature ProcessItemFunction { get { return m_processItemFunction; } set { if ((object)value != null) {