view Implab/Components/DisposablePool.cs @ 187:dd4a3590f9c6 ref20160224

Reworked cancelation handling, if the cancel handler isn't specified the OperationCanceledException will be handled by the error handler Any unhandled OperationCanceledException will cause the promise cancelation
author cin
date Tue, 19 Apr 2016 17:35:20 +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
    }
}