1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Threading;
  6.  
  7. namespace Pooling
  8. {
  9.     public enum LoadingMode { Eager, Lazy, LazyExpanding };
  10.  
  11.     public enum AccessMode { FIFO, LIFO, Circular };
  12.  
  13.     public class Pool<T> : IDisposable
  14.     {
  15.         private bool isDisposed;
  16.         private Func<Pool<T>, T> factory;
  17.         private LoadingMode loadingMode;
  18.         private IItemStore itemStore;
  19.         private int size;
  20.         private int count;
  21.         private Semaphore sync;
  22.  
  23.         public Pool(int size, Func<Pool<T>, T> factory)
  24.             : this(size, factory, LoadingMode.Lazy, AccessMode.FIFO)
  25.         {
  26.         }
  27.  
  28.         public Pool(int size, Func<Pool<T>, T> factory,
  29.             LoadingMode loadingMode, AccessMode accessMode)
  30.         {
  31.             if (size <= 0)
  32.                 throw new ArgumentOutOfRangeException("size", size,
  33.                     "Argument 'size' must be greater than zero.");
  34.             if (factory == null)
  35.                 throw new ArgumentNullException("factory");
  36.  
  37.             this.size = size;
  38.             this.factory = factory;
  39.             sync = new Semaphore(size, size);
  40.             this.loadingMode = loadingMode;
  41.             this.itemStore = CreateItemStore(accessMode, size);
  42.             if (loadingMode == LoadingMode.Eager)
  43.             {
  44.                 PreloadItems();
  45.             }
  46.         }
  47.  
  48.         public T Acquire()
  49.         {
  50.             sync.WaitOne();
  51.             switch (loadingMode)
  52.             {
  53.                 case LoadingMode.Eager:
  54.                     return AcquireEager();
  55.                 case LoadingMode.Lazy:
  56.                     return AcquireLazy();
  57.                 default:
  58.                     Debug.Assert(loadingMode == LoadingMode.LazyExpanding,
  59.                         "Unknown LoadingMode encountered in Acquire method.");
  60.                     return AcquireLazyExpanding();
  61.             }
  62.         }
  63.  
  64.         public void Release(T item)
  65.         {
  66.             lock (itemStore)
  67.             {
  68.                 itemStore.Store(item);
  69.             }
  70.             sync.Release();
  71.         }
  72.  
  73.         public void Dispose()
  74.         {
  75.             if (isDisposed)
  76.             {
  77.                 return;
  78.             }
  79.             isDisposed = true;
  80.             if (typeof(IDisposable).IsAssignableFrom(typeof(T)))
  81.             {
  82.                 lock (itemStore)
  83.                 {
  84.                     while (itemStore.Count > 0)
  85.                     {
  86.                         IDisposable disposable = (IDisposable)itemStore.Fetch();
  87.                         disposable.Dispose();
  88.                     }
  89.                 }
  90.             }
  91.             sync.Close();
  92.         }
  93.  
  94.         #region Acquisition
  95.  
  96.         private T AcquireEager()
  97.         {
  98.             lock (itemStore)
  99.             {
  100.                 return itemStore.Fetch();
  101.             }
  102.         }
  103.  
  104.         private T AcquireLazy()
  105.         {
  106.             lock (itemStore)
  107.             {
  108.                 if (itemStore.Count > 0)
  109.                 {
  110.                     return itemStore.Fetch();
  111.                 }
  112.             }
  113.             Interlocked.Increment(ref count);
  114.             return factory(this);
  115.         }
  116.  
  117.         private T AcquireLazyExpanding()
  118.         {
  119.             bool shouldExpand = false;
  120.             if (count < size)
  121.             {
  122.                 int newCount = Interlocked.Increment(ref count);
  123.                 if (newCount <= size)
  124.                 {
  125.                     shouldExpand = true;
  126.                 }
  127.                 else
  128.                 {
  129.                     // Another thread took the last spot - use the store instead
  130.                     Interlocked.Decrement(ref count);
  131.                 }
  132.             }
  133.             if (shouldExpand)
  134.             {
  135.                 return factory(this);
  136.             }
  137.             else
  138.             {
  139.                 lock (itemStore)
  140.                 {
  141.                     return itemStore.Fetch();
  142.                 }
  143.             }
  144.         }
  145.  
  146.         private void PreloadItems()
  147.         {
  148.             for (int i = 0; i < size; i++)
  149.             {
  150.                 T item = factory(this);
  151.                 itemStore.Store(item);
  152.             }
  153.             count = size;
  154.         }
  155.  
  156.         #endregion
  157.  
  158.         #region Collection Wrappers
  159.  
  160.         interface IItemStore
  161.         {
  162.             T Fetch();
  163.             void Store(T item);
  164.             int Count { get; }
  165.         }
  166.  
  167.         private IItemStore CreateItemStore(AccessMode mode, int capacity)
  168.         {
  169.             switch (mode)
  170.             {
  171.                 case AccessMode.FIFO:
  172.                     return new QueueStore(capacity);
  173.                 case AccessMode.LIFO:
  174.                     return new StackStore(capacity);
  175.                 default:
  176.                     Debug.Assert(mode == AccessMode.Circular,
  177.                         "Invalid AccessMode in CreateItemStore");
  178.                     return new CircularStore(capacity);
  179.             }
  180.         }
  181.  
  182.         class QueueStore : Queue<T>, IItemStore
  183.         {
  184.             public QueueStore(int capacity) : base(capacity)
  185.             {
  186.             }
  187.  
  188.             public T Fetch()
  189.             {
  190.                 return Dequeue();
  191.             }
  192.  
  193.             public void Store(T item)
  194.             {
  195.                 Enqueue(item);
  196.             }
  197.         }
  198.  
  199.         class StackStore : Stack<T>, IItemStore
  200.         {
  201.             public StackStore(int capacity) : base(capacity)
  202.             {
  203.             }
  204.  
  205.             public T Fetch()
  206.             {
  207.                 return Pop();
  208.             }
  209.  
  210.             public void Store(T item)
  211.             {
  212.                 Push(item);
  213.             }
  214.         }
  215.  
  216.         class CircularStore : IItemStore
  217.         {
  218.             private List<Slot> slots;
  219.             private int freeSlotCount;
  220.             private int position = -1;
  221.  
  222.             public CircularStore(int capacity)
  223.             {
  224.                 slots = new List<Slot>(capacity);
  225.             }
  226.  
  227.             public T Fetch()
  228.             {
  229.                 if (Count == 0)
  230.                     throw new InvalidOperationException("The buffer is empty.");
  231.  
  232.                 int startPosition = position;
  233.                 do
  234.                 {
  235.                     Advance();
  236.                     Slot slot = slots[position];
  237.                     if (!slot.IsInUse)
  238.                     {
  239.                         slot.IsInUse = true;
  240.                         --freeSlotCount;
  241.                         return slot.Item;
  242.                     }
  243.                 } while (startPosition != position);
  244.                 throw new InvalidOperationException("No free slots.");
  245.             }
  246.  
  247.             public void Store(T item)
  248.             {
  249.                 Slot slot = slots.Find(s => object.Equals(s.Item, item));
  250.                 if (slot == null)
  251.                 {
  252.                     slot = new Slot(item);
  253.                     slots.Add(slot);
  254.                 }
  255.                 slot.IsInUse = false;
  256.                 ++freeSlotCount;
  257.             }
  258.  
  259.             public int Count
  260.             {
  261.                 get { return freeSlotCount; }
  262.             }
  263.  
  264.             private void Advance()
  265.             {
  266.                 position = (position + 1) % slots.Count;
  267.             }
  268.  
  269.             class Slot
  270.             {
  271.                 public Slot(T item)
  272.                 {
  273.                     this.Item = item;
  274.                 }
  275.  
  276.                 public T Item { get; private set; }
  277.                 public bool IsInUse { get; set; }
  278.             }
  279.         }
  280.  
  281.         #endregion
  282.  
  283.         public bool IsDisposed
  284.         {
  285.             get { return isDisposed; }
  286.         }
  287.     }
  288.  
  289.     public interface IFoo : IDisposable
  290.     {
  291.         void Test();
  292.     }
  293.  
  294.     public class Foo : IFoo
  295.     {
  296.         private static int count = 0;
  297.  
  298.         private int num;
  299.  
  300.         public Foo()
  301.         {
  302.             num = Interlocked.Increment(ref count);
  303.         }
  304.  
  305.         public void Dispose()
  306.         {
  307.             Console.WriteLine("Goodbye from Foo #{0}", num);
  308.         }
  309.  
  310.         public void Test()
  311.         {
  312.             Console.WriteLine("Hello from Foo #{0}", num);
  313.         }
  314.     }
  315.  
  316.     public class PooledFoo : IFoo
  317.     {
  318.         private Foo internalFoo;
  319.         private Pool<IFoo> pool;
  320.  
  321.         public PooledFoo(Pool<IFoo> pool)
  322.         {
  323.             if (pool == null)
  324.                 throw new ArgumentNullException("pool");
  325.  
  326.             this.pool = pool;
  327.             this.internalFoo = new Foo();
  328.         }
  329.  
  330.         public void Dispose()
  331.         {
  332.             if (pool.IsDisposed)
  333.             {
  334.                 internalFoo.Dispose();
  335.             }
  336.             else
  337.             {
  338.                 pool.Release(this);
  339.             }
  340.         }
  341.  
  342.         public void Test()
  343.         {
  344.             internalFoo.Test();
  345.         }
  346.     }
  347. }