//******************************************************************************************************
// 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
}
}