//****************************************************************************************************** // ReusableObjectPool.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/23/2011 - J. Ritchie Carroll // Generated original version of source code. // 12/14/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; namespace GSF { /// /// Represents a reusable object pool that can be used by an application. /// /// /// /// Object pooling can offer a significant performance boost in some scenarios. It is most effective when the cost /// of construction is high and/or the rate of instantiation is high. /// /// /// This object pool is statically created at application startup, one per type, and is available for all components /// and classes within an application domain. Every time you need to use an object, you take one from the pool, use /// it, then return it to the pool when done. This process can be much faster than creating a new object every time /// you need to use one. /// /// /// When objects implement , the will automatically /// attach to the event and return the object to the pool when it's disposed. /// When an object is taken from the pool, the method will be called to /// reinstantiate any need member variables. If class tracks a disposed flag, it should be reset during call to the /// method so that class will be "un-disposed". Also, typical dispose /// implementations call in the method, as such the /// should be called during method to /// make sure class will be disposed by finalizer in case method is never called. /// /// Here is an example class that implements such that it will be automatically returned /// to the pool when the class is disposed and automatically initialized when taken from the pool: /// /// /// <summary> /// /// Class that shows example implementation of reusable <see cref="ISupportLifecycle"/>. /// /// </summary> /// public class ReusableClass : ISupportLifecycle /// { /// #region [ Members ] /// /// // Events /// /// /// <summary> /// /// Occurs when the <see cref="ReusableClass"/> is disposed. /// /// </summary> /// public event EventHandler Disposed; /// /// // Fields /// private System.Timers.Timer m_timer; // Example member variable that needs initialization and disposal /// private bool m_enabled; /// private bool m_disposed; /// /// #endregion /// /// #region [ Constructors ] /// /// /// <summary> /// /// Releases the unmanaged resources before the <see cref="ReusableClass"/> object is reclaimed by <see cref="GC"/>. /// /// </summary> /// ~ReusableClass() /// { /// Dispose(false); /// } /// /// #endregion /// /// #region [ Properties ] /// /// /// <summary> /// /// Gets or sets a boolean value that indicates whether the <see cref="ReusableClass"/> object is currently enabled. /// /// </summary> /// public bool Enabled /// { /// get /// { /// return m_enabled; /// } /// set /// { /// m_enabled = value; /// /// if (m_timer != null) /// m_timer.Enabled = m_enabled; /// } /// } /// /// #endregion /// /// #region [ Methods ] /// /// /// <summary> /// /// Initializes the <see cref="ReusableClass"/> object. /// /// </summary> /// public void Initialize() /// { /// // Undispose class instance if it is being reused /// if (m_disposed) /// { /// m_disposed = false; /// GC.ReRegisterForFinalize(this); /// } /// /// m_enabled = true; /// /// // Initialize member variables /// if (m_timer == null) /// { /// m_timer = new System.Timers.Timer(2000.0D); /// m_timer.Elapsed += m_timer_Elapsed; /// m_timer.Enabled = m_enabled; /// } /// } /// /// /// <summary> /// /// Releases all the resources used by the <see cref="ReusableClass"/> object. /// /// </summary> /// public void Dispose() /// { /// Dispose(true); /// GC.SuppressFinalize(this); /// } /// /// /// <summary> /// /// Releases the unmanaged resources used by the <see cref="ReusableClass"/> object and optionally releases the managed resources. /// /// </summary> /// /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> /// protected virtual void Dispose(bool disposing) /// { /// if (!m_disposed) /// { /// try /// { /// if (disposing) /// { /// // Dispose member variables /// if (m_timer != null) /// { /// m_timer.Elapsed -= m_timer_Elapsed; /// m_timer.Dispose(); /// } /// m_timer = null; /// /// m_enabled = false; /// } /// } /// finally /// { /// m_disposed = true; /// /// if (Disposed != null) /// Disposed(this, EventArgs.Empty); /// } /// } /// } /// /// // Handle timer event /// private void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) /// { /// // Do work here... /// } /// /// #endregion /// } /// /// /// /// /// If the pool objects do not implement you can still use the object pool, but it /// will be very imporant to return the object to the pool when you are finished using it. If you are using an object /// scoped within a method, make sure to use a try/finally so that you can take the object within the try and return /// the object within the finally. If you are using an object as a member scoped class field, make sure you use the /// standard dispose pattern and return the object during the method call. In this /// mode you will also need to manually initialze your object after you get it from the pool to reset its state. /// /// /// Type of object to pool. [Obsolete("It is not recommended to use this class because the need for pooling is rare and implementations of pooling can be dangerous.")] public class ReusableObjectPool where T : class, new() { private readonly ConcurrentQueue m_objectPool = new ConcurrentQueue(); /// /// Gets an object from the pool, or creates a new one if no pool items are available. /// /// An available object from the pool, or a new one if no pool items are available. /// /// If type of implements , the pool will attach /// to the item's event such that the object can be automatically /// restored to the pool upon . It will be up to class implementors to /// make sure makes the class ready for use as this method will /// always be called for an object being taken from the pool. /// public T TakeObject() { T item; bool newItem = false; // Attempt to provide user with a queued item if (!m_objectPool.TryDequeue(out item)) { // No items are available, create a new item for the object pool item = FastObjectFactory.CreateObjectFunction(); newItem = true; } // Automatically handle class life cycle if item implements support for this ISupportLifecycle lifecycleItem = item as ISupportLifecycle; if (lifecycleItem != null) { // Attach to dispose event so item can be automatically returned to the pool if (newItem) lifecycleItem.Disposed += LifecycleItem_Disposed; // Initialize or reinitialize (i.e., un-dispose) the item lifecycleItem.Initialize(); } return item; } /// /// Returns object to the pool. /// /// Reference to the object being returned. /// /// If type of implements , the pool will automatically /// return the item to the pool when the event is raised, usually when /// the object's method is called. /// public void ReturnObject(T item) { if (item != null) m_objectPool.Enqueue(item); } /// /// Releases all the objects currently cached in the pool. /// public void Clear() { T item; while (!m_objectPool.IsEmpty) { m_objectPool.TryDequeue(out item); } } /// /// Allocates the pool to the desired . /// /// Desired pool size. /// Pool must at least be one. public void SetPoolSize(int size) { if (size < 1) throw new ArgumentOutOfRangeException(nameof(size), "pool size must at least be one"); for (int i = 0; i < size - m_objectPool.Count; i++) { T item = FastObjectFactory.CreateObjectFunction(); // Automatically handle life cycle if item implements support for this ISupportLifecycle lifecycleItem = item as ISupportLifecycle; // Attach to dispose event so item can be automatically returned to the pool if (lifecycleItem != null) lifecycleItem.Disposed += LifecycleItem_Disposed; m_objectPool.Enqueue(item); } } /// /// Gets the number of items currently contained in the pool. /// /// The size of the pool. public int GetPoolSize() { return m_objectPool.Count; } // Handler to automatically to return items to the queue private void LifecycleItem_Disposed(object sender, EventArgs e) { ReturnObject(sender as T); } /// /// Default static instance which can be shared throughout the application. /// [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public static readonly ReusableObjectPool Default = new ReusableObjectPool(); } /// /// Represents a reusable object pool that can be used by an application. /// /// /// /// See for more details on using object pooling. /// /// /// should be used when you only have the of an object available (such as when you are /// using reflection), otherwise you should use the generic . /// /// [Obsolete("It is not recommended to use this class because the need for pooling is rare and implementations of pooling can be dangerous.")] public static class ReusableObjectPool { private static readonly ConcurrentDictionary> s_objectPools = new ConcurrentDictionary>(); /// /// Gets an object from the pool, or creates a new one if no pool items are available. /// /// Type of object to get from pool. /// An available object from the pool, or a new one if no pool items are available. /// does not support parameterless public constructor. /// /// If implements , the pool will attach /// to the item's event such that the object can be automatically /// restored to the pool upon . It will be up to class implementors to /// make sure makes the class ready for use as this method will /// always be called for an object being taken from the pool. /// public static object TakeObject(Type type) { return TakeObject(type); } /// /// Gets an object from the pool, or creates a new one if no pool items are available. /// /// Type of object to get from pool. /// Type of returned object to get from pool. /// An available object from the pool, or a new one if no pool items are available. /// /// does not support parameterless public constructor -or- /// is not a subclass or interface implementation of function type definition. /// /// /// /// This function will validate that is related to . /// /// /// If implements , the pool will attach /// to the item's event such that the object can be automatically /// restored to the pool upon . It will be up to class implementors to /// make sure makes the class ready for use as this method will /// always be called for an object being taken from the pool. /// /// public static T TakeObject(Type type) { object item; bool newItem = false; // Get or create object pool associated with specified type ConcurrentQueue objectPool = s_objectPools.GetOrAdd(type, (objType) => new ConcurrentQueue()); // Attempt to provide user with a queued item if (!objectPool.TryDequeue(out item)) { // No items are available, create a new item for the object pool item = FastObjectFactory.GetCreateObjectFunction(type)(); newItem = true; } // Automatically handle class life cycle if item implements support for this ISupportLifecycle lifecycleItem = item as ISupportLifecycle; if (lifecycleItem != null) { // Attach to dispose event so item can be automatically returned to the pool if (newItem) lifecycleItem.Disposed += LifecycleItem_Disposed; // Initialize or reinitialize (i.e., un-dispose) the item lifecycleItem.Initialize(); } return (T)item; } /// /// Returns object to the pool. /// /// Reference to the object being returned. /// /// If type of implements , the pool will automatically /// return the item to the pool when the event is raised, usually when /// the object's method is called. /// public static void ReturnObject(object item) { if (item != null) { ConcurrentQueue objectPool; // Try to get object pool associated with the specified item's type if (s_objectPools.TryGetValue(item.GetType(), out objectPool)) objectPool.Enqueue(item); } } /// /// Releases all the objects currently cached in the specified pool. /// /// Type of pool to clear. public static void Clear(Type type) { ConcurrentQueue objectPool; // Try to get object pool associated with the specified type if (s_objectPools.TryGetValue(type, out objectPool)) { object item; while (!objectPool.IsEmpty) { objectPool.TryDequeue(out item); } } } /// /// Allocates the pool to the desired . /// /// Type of pool to initialize. /// Desired pool size. /// Pool must at least be one. public static void SetPoolSize(Type type, int size) { if (size < 1) throw new ArgumentOutOfRangeException(nameof(size), "pool size must at least be one"); // Get or create object pool associated with specified type ConcurrentQueue objectPool = s_objectPools.GetOrAdd(type, (objType) => new ConcurrentQueue()); for (int i = 0; i < size - objectPool.Count; i++) { object item = FastObjectFactory.GetCreateObjectFunction(type)(); // Automatically handle life cycle if item implements support for this ISupportLifecycle lifecycleItem = item as ISupportLifecycle; // Attach to dispose event so item can be automatically returned to the pool if (lifecycleItem != null) lifecycleItem.Disposed += LifecycleItem_Disposed; objectPool.Enqueue(item); } } // Handler to automatically to return items to the queue private static void LifecycleItem_Disposed(object sender, EventArgs e) { ReturnObject(sender); } } }