view Implab/ObjectPool.cs @ 83:397fe8db0806 v2

fixed object pool
author cin
date Mon, 29 Sep 2014 15:49:15 +0400
parents 0363407ee75c
children abe260860bd6
line wrap: on
line source

using System;
using Implab.Parallels;
using System.Threading;

namespace Implab {
    public class ObjectPool<T> : IDisposable {
        readonly Func<T> m_factory;
        readonly Action<T> m_cleanup;
        readonly int m_size;
        readonly MTQueue<T> m_queue = new MTQueue<T>();

        bool m_disposed;

        int m_count;

        public ObjectPool(Func<T> factory, Action<T> cleanup, int size) {
            Safe.ArgumentNotNull(factory, "factory");
            Safe.ArgumentInRange(size, 1, size, "size");

            m_factory = factory;
            m_cleanup = cleanup;
            m_size = size;
        }

        public ObjectPool(Func<T> factory, Action<T> cleanup) : this(factory,cleanup,Environment.ProcessorCount+1) {
        }

        public ObjectPool(Func<T> factory) : this(factory,null,Environment.ProcessorCount+1) {
        }

        public ObjectPoolWrapper<T> AllocateAuto() {

            return new ObjectPoolWrapper<T>(Allocate(), this);
        }

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

            T instance;
            if (m_queue.TryDequeue(out instance)) {
                Interlocked.Decrement(ref m_count);
            } else {
                instance = m_factory();
            }
            return instance;
        }

        public void Release(T instance) {
            Thread.MemoryBarrier();
            if (m_count < m_size && !m_disposed) {
                Interlocked.Increment(ref m_count);

                if (m_cleanup != null)
                    m_cleanup(instance);

                m_queue.Enqueue(instance);

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

            } else {
                if (instance is IDisposable)
                    ((IDisposable)instance).Dispose();
            }
        }

        protected virtual void Dispose(bool disposing) {
            if (disposing) {
                m_disposed = true;
                T instance;
                while (m_queue.TryDequeue(out instance))
                    if (instance is IDisposable)
                        ((IDisposable)instance).Dispose();
            }
        }

        #region IDisposable implementation

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

        #endregion
    }
}