view Implab/Components/DisposablePool.cs @ 186:75103928da09 ref20160224

working on cancelation and error handling
author cin
date Tue, 19 Apr 2016 00:50:14 +0300
parents b933ec88446e
children c52691faaf21
line wrap: on
line source

using System;
using Implab.Parallels;
using System.Threading;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Implab.Components {
    /// <summary>
    /// The base class for implementing pools of disposable objects.
    /// </summary>
    /// <remarks>
    /// <para>This class maintains a set of pre-created objects and which are frequently allocated and released
    /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed,
    /// if the pool is empty it will create new objects on demand.</para>
    /// <para>Instances of this class are thread-safe.</para>
    /// </remarks>
    public abstract class DisposablePool<T> : IDisposable {
        readonly int m_size;
        readonly AsyncQueue<T> m_queue = new AsyncQueue<T>();

        [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
        static readonly bool _isValueType = typeof(T).IsValueType;

        bool m_disposed;

        int m_count;

        protected DisposablePool(int size) {
            m_size = size;
        }

        protected DisposablePool() : this(Environment.ProcessorCount+1) {
        }

        public T Allocate() {
            if (m_disposed)
                throw new ObjectDisposedException(ToString());

            T instance;
            if (m_queue.TryDequeue(out instance)) {
                Interlocked.Decrement(ref m_count);
            } else {
                instance = CreateInstance();
                Debug.Assert(!Object.Equals(instance, default(T)) || _isValueType);
            }
            return instance;
        }

        protected abstract T CreateInstance();

        protected virtual void CleanupInstance(T instance) {
        }

        public void Release(T instance) {
            if ( Object.Equals(instance,default(T)) && !_isValueType)
                return;

            Thread.MemoryBarrier();
            if (m_count < m_size && !m_disposed) {
                Interlocked.Increment(ref m_count);

                CleanupInstance(instance);

                m_queue.Enqueue(instance);

                // пока элемент возвращался в кеш, была начата операция освобождения всего кеша
                // и возможно уже законцена, в таком случае следует извлечь элемент обратно и
                // освободить его. Если операция освобождения кеша еще не заврешилась, то будет
                // изъят и освобожден произвольный элемен, что не повлияет на ход всего процесса.
                if (m_disposed && m_queue.TryDequeue(out instance) && instance is IDisposable)
                    ((IDisposable)instance).Dispose() ;

            } else {
                var disposable = instance as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }
        }

        protected virtual void Dispose(bool disposing) {
            if (disposing) {
                m_disposed = true;
                T instance;
                while (m_queue.TryDequeue(out instance)) {
                    var disposable = instance as IDisposable;
                    if (disposable != null)
                        disposable.Dispose();
                }
            }
        }

        #region IDisposable implementation

        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}