Home > Uncategorized > A Resource Pool for use once objects

A Resource Pool for use once objects

At work today I had a meeting where we went over a scenario where it would be useful to have a bunch of objects ready to use, but we don’t trust other people to not misuse shared resources. So what they wanted was a pool that would have a minimum number of objects ready to go, but that nothing would be placed back into the pool. The idea being that they might grab a number of object from the pool, and then the pool should fill up again while the main code was doing work.

using System;
using System.Collections.Generic;
using System.Threading;

namespace PreFetchPool
{
    /// <summary>
    /// Class to precreate objects to ensure that there is a minimum of use once resources
    /// </summary>
    /// <author>jader3rd</author>
    public class PreFectchPool<T> : IDisposable where T : class
    {
        private ReaderWriterLockSlim poolLock;
        private Queue<T> pool;
        private Func<T> createFunc;
        private bool disposing;
        private readonly TimeSpan timeout = TimeSpan.FromSeconds(15);
        private ManualResetEvent poolHasItems;

        /// <summary>
        /// The amount of items which should exist in the pool. Default is 10.
        /// </summary>
        public uint Quota { get; set; }

        public PreFectchPool(Func<T> create)
        {
            createFunc = create;
            poolLock = new ReaderWriterLockSlim();
            pool = new Queue<T>();
            disposing = false;
            Quota = 10;
            pool.Enqueue(createFunc.Invoke());
            poolHasItems = new ManualResetEvent(true);
            ThreadPool.QueueUserWorkItem(checkPool);
        }

        /// <summary>
        /// Unblock any threads waiting on the pool. Dispose of any objects left in the pool.
        /// </summary>
        public void Dispose()
        {
            disposing = true;
            poolHasItems.Set();
            poolLock.EnterWriteLock();
            poolLock.ExitWriteLock();
            if (null != poolLock)
            {
                poolLock.Dispose();
                poolLock = null;
            }

            while (0 < pool.Count)
            {
                T item = pool.Dequeue();
                if (item is IDisposable)
                {
                    ((IDisposable)item).Dispose();
                }
            }
        }

        /// <summary>
        /// If the pool needs more objects, create one and add it to the pool.
        /// </summary>
        /// <param name="state">Not used, just there to satisfy QueueUserWorkItem</param>
        private void checkPool(Object state)
        {
            if (disposing) return;
            try
            {
                poolLock.EnterUpgradeableReadLock();
                if (pool.Count < Quota)
                {
                    T item = createFunc.Invoke();
                    try
                    {
                        poolLock.EnterWriteLock();
                        pool.Enqueue(item);
                        poolHasItems.Set();
                    }
                    finally
                    {
                        if (poolLock.IsWriteLockHeld) poolLock.ExitWriteLock();
                    }
                }
            }
            finally
            {
                if (poolLock.IsUpgradeableReadLockHeld) poolLock.ExitUpgradeableReadLock();
            }
            ThreadPool.QueueUserWorkItem(checkPool);
        }

        /// <summary>
        /// Get an item from the pool
        /// </summary>
        /// <returns>The item</returns>
        public T Get()
        {
            if (disposing) throw new ObjectDisposedException(GetType().Name);
            T item = null;
            do
            {
                // If the pool doesn't have any items for the timeout period try to create an object
                if (poolHasItems.WaitOne(timeout) && !disposing)
                {
                    try
                    {
                        poolLock.EnterWriteLock();
                        if (0 < pool.Count)
                        {
                            item = pool.Dequeue();
                        }
                        if (0 == pool.Count)
                        {
                            poolHasItems.Reset();
                        }
                    }
                    finally
                    {
                        if (poolLock.IsWriteLockHeld) poolLock.ExitWriteLock();
                    }
                }
                if (null == item)
                {
                    if (disposing) throw new ObjectDisposedException(GetType().Name);
                    ThreadPool.QueueUserWorkItem(checkPool);
                }
            } while (null == item);

            ThreadPool.QueueUserWorkItem(checkPool);
            return item;
        }
    }
}
Categories: Uncategorized
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: