diff Implab/ObjectPool.cs @ 82:0363407ee75c v2

added object pool
author cin
date Mon, 29 Sep 2014 05:04:32 +0400
parents
children 397fe8db0806
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ObjectPool.cs	Mon Sep 29 05:04:32 2014 +0400
@@ -0,0 +1,85 @@
+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>();
+
+        volatile bool m_disposed;
+
+        volatile 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> Allocate() {
+            if (m_disposed)
+                throw new ObjectDisposedException(this.ToString());
+
+            T instance;
+            if (m_queue.TryDequeue(out instance)) {
+                Interlocked.Decrement(ref m_count);
+                return instance;
+            } else {
+                instance = m_factory();
+            }
+            return new ObjectPoolWrapper<T>(instance, this);
+        }
+
+        public void Release(T instance) {
+            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))
+                    Safe.Dispose(instance);
+
+            } else {
+                Safe.Dispose(instance);
+            }
+        }
+
+        protected virtual void Dispose(bool disposing) {
+            if (disposing) {
+                m_disposed = true;
+                T instance;
+                while (m_queue.TryDequeue(out instance))
+                    Safe.Dispose(instance);
+            }
+        }
+
+        #region IDisposable implementation
+
+        public void Dispose() {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        #endregion
+    }
+}
+