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