//******************************************************************************************************
// DynamicObjectPool`1.cs - Gbtc
//
// Copyright © 2016, 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/17/2016 - Steven E. Chisholm
// Generated original version of source code.
//
//
//******************************************************************************************************
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using GSF.Diagnostics;
using GSF.Threading;
namespace GSF.Collections
{
///
/// Provides a thread safe queue that acts as a buffer pool.
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
public class DynamicObjectPool
where T : class
{
private static readonly LogPublisher Log = Logger.CreatePublisher(typeof(DynamicObjectPool), MessageClass.Component);
private readonly ScheduledTask m_collection;
private readonly ConcurrentQueue m_queue;
private readonly Func m_instanceObject;
private readonly Queue m_countHistory;
private readonly int m_targetCount;
private int m_objectsCreated;
///
/// Creates a new Resource Queue.
///
/// A delegate that will return the necessary queue.
/// the ideal number of objects that are always pending on the queue.
public DynamicObjectPool(Func instance, int targetCount)
{
if (instance == null)
throw new ArgumentNullException(nameof(instance));
m_targetCount = targetCount;
m_countHistory = new Queue(100);
m_instanceObject = instance;
m_queue = new ConcurrentQueue();
m_collection = new ScheduledTask();
m_collection.Running += CollectionRunning;
m_collection.Start(1000);
}
private void CollectionRunning(object sender, EventArgs e)
{
m_collection.Start(1000);
m_countHistory.Enqueue(m_queue.Count);
if (m_countHistory.Count >= 60)
{
int objectsCreated = Interlocked.Exchange(ref m_objectsCreated, 0);
//if there were ever more than the target items in the queue over the past 60 seconds
//remove some items.
//However, don't remove items if the pool ever got to 0 and had objects that had to be created.
int min = m_countHistory.Min();
m_countHistory.Clear();
if (objectsCreated > 250)
Log.Publish(MessageLevel.Error, MessageFlags.PerformanceIssue | MessageFlags.UsageIssue, "Items Created since last collection cycle.", (objectsCreated).ToString());
else if (objectsCreated > 50)
Log.Publish(MessageLevel.Warning, "Items Created since last collection cycle.", (objectsCreated).ToString());
else if (objectsCreated > 0)
Log.Publish(MessageLevel.Info, "Items Created since last collection cycle.", (objectsCreated).ToString());
if (min > m_targetCount && objectsCreated == 0)
{
int itemsRemoved = (min - m_targetCount);
if (itemsRemoved > 250)
Log.Publish(MessageLevel.Error, MessageFlags.PerformanceIssue | MessageFlags.UsageIssue, "Removing items", itemsRemoved.ToString());
else if (itemsRemoved > 50)
Log.Publish(MessageLevel.Warning, "Removing items", itemsRemoved.ToString());
else if (itemsRemoved > 0)
Log.Publish(MessageLevel.Info, "Removing items", itemsRemoved.ToString());
}
while (min > m_targetCount && objectsCreated == 0)
{
T item;
if (m_queue.TryDequeue(out item))
{
(item as IDisposable)?.Dispose();
}
else
{
return;
}
min--;
}
}
}
///
/// Removes an item from the queue. If one does not exist, one is created.
///
///
public T Dequeue()
{
T item;
if (m_queue.TryDequeue(out item))
{
return item;
}
Interlocked.Increment(ref m_objectsCreated);
return m_instanceObject();
}
///
/// Adds an item back to the queue.
///
/// The resource to queue.
public void Enqueue(T resource)
{
m_queue.Enqueue(resource);
}
}
}