{
internal abstract class ObjectPool<T> where T : class
{
+ // This is the number of objects we are going to cache
const int capacity = 20;
+ /* We use this bit in addIndex to synchronize the array and the index itself
+ * Namely when we update addIndex we also set that bit for the time the value
+ * in the array hasn't still been updated to the object we are returning to the cache
+ */
const int bit = 0x8000000;
readonly T[] buffer;
addIndex = capacity - 1;
}
+ /* Code that want to use a pool subclass it and
+ * implement that method. In most case 'new T ()'.
+ */
protected abstract T Creator ();
public T Take ()
{
+ // If no element in the cache, we return a new object
if ((addIndex & ~bit) - 1 == removeIndex)
return Creator ();
do {
i = removeIndex;
+ // We return a new element when looping becomes too costly
if ((addIndex & ~bit) - 1 == i || tries == 0)
return Creator ();
result = buffer[i % capacity];
int i;
int tries = 3;
do {
+ // While an array update is ongoing (i.e. an extra write op) we loop
do {
i = addIndex;
} while ((i & bit) > 0);
+ // If no more room just forget about the object altogether
if (i - removeIndex >= capacity - 1)
return;
+ // We update addIndex and notify that we are going to set buffer correctly
} while (Interlocked.CompareExchange (ref addIndex, i + 1 + bit, i) != i && --tries > 0);
buffer[i % capacity] = obj;
+ Thread.MemoryBarrier ();
+ // Since bit essentialy acts like a lock, we simply use an atomic read/write combo
addIndex = addIndex - bit;
}
}