Skip to content

Instantly share code, notes, and snippets.

@benaadams
Created December 6, 2015 17:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save benaadams/5f5ea438d733de6f762a to your computer and use it in GitHub Desktop.
Save benaadams/5f5ea438d733de6f762a to your computer and use it in GitHub Desktop.
Object Pools
public interface IObjectPool<T>
{
T Rent();
void Return(T obj);
}
public interface IResetable
{
void Reset();
}
public class PooledObject<T>
{
public T Object { get; }
public IObjectPool<PooledObject<T>> OwningPool { get; }
public bool IsRented { get; set; }
public PooledObject(T obj, IObjectPool<PooledObject<T>> owningPool)
{
Object = obj;
OwningPool = owningPool;
}
}
public class CheckedPooledObject<T> : PooledObject<T>, IDisposable
{
public CheckedPooledObject(T obj, IObjectPool<PooledObject<T>> owningPool)
: base(obj, owningPool)
{
}
~CheckedPooledObject()
{
throw new InvalidOperationException("Pooled object was not disposed");
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
public class ObjectPoolObjectInUseException : Exception { }
public class ObjectPoolObjectReturnedTwiceException : Exception { }
public class ObjectPoolWrongPoolException : Exception { }
// No wrapping, no checking, no resetting
public class UnsafeObjectPool<T> : IObjectPool<T>, IDisposable
where T : class, new()
{
private ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
public T Rent()
{
var queue = _queue;
if (queue == null) throw new ObjectDisposedException(nameof(UnsafeObjectPool<T>));
T obj;
if (queue.TryDequeue(out obj))
{
return obj;
}
else
{
return new T();
}
}
public void Return(T obj)
{
var queue = _queue;
if (queue != null) queue.Enqueue(obj);
}
public void Dispose()
{
_queue = null;
}
}
// Common or garden object pool
public class ObjectPool<T> : IObjectPool<PooledObject<T>>, IDisposable
where T : class, IResetable, new()
{
private ConcurrentQueue<PooledObject<T>> _queue = new ConcurrentQueue<PooledObject<T>>();
public PooledObject<T> Rent()
{
var queue = _queue;
if (queue == null) throw new ObjectDisposedException(nameof(ObjectPool<T>));
PooledObject<T> pooledObject;
if (queue.TryDequeue(out pooledObject))
{
if (pooledObject.IsRented) throw new ObjectPoolObjectInUseException();
pooledObject.IsRented = true;
return pooledObject;
}
else
{
return new PooledObject<T>(new T(), this) { IsRented = true };
}
}
public void Return(PooledObject<T> pooledObject)
{
if (!pooledObject.IsRented) throw new ObjectPoolObjectReturnedTwiceException();
if (pooledObject.OwningPool != this) throw new ObjectPoolWrongPoolException();
pooledObject.Object.Reset();
pooledObject.IsRented = false;
var queue = _queue;
if (queue != null) queue.Enqueue(pooledObject);
}
public void Dispose()
{
var queue = _queue;
_queue = null;
PooledObject<T> pooledObject;
while (queue.TryDequeue(out pooledObject))
{
// Empty pool
}
}
}
// Dispose pooled objects
public class DisposingObjectPool<T> : IObjectPool<PooledObject<T>>, IDisposable
where T : class, IResetable, IDisposable, new()
{
private ConcurrentQueue<PooledObject<T>> _queue = new ConcurrentQueue<PooledObject<T>>();
public PooledObject<T> Rent()
{
var queue = _queue;
if (queue == null) throw new ObjectDisposedException(nameof(ObjectPool<T>));
PooledObject<T> pooledObject;
if (queue.TryDequeue(out pooledObject))
{
if (pooledObject.IsRented) throw new ObjectPoolObjectInUseException();
pooledObject.IsRented = true;
return pooledObject;
}
else
{
return new PooledObject<T>(new T(), this) { IsRented = true };
}
}
public void Return(PooledObject<T> pooledObject)
{
if (!pooledObject.IsRented) throw new ObjectPoolObjectReturnedTwiceException();
if (pooledObject.OwningPool != this) throw new ObjectPoolWrongPoolException();
pooledObject.Object.Reset();
pooledObject.IsRented = false;
Interlocked.MemoryBarrier();
var queue = _queue;
if (queue != null)
{
queue.Enqueue(pooledObject);
if (_queue != null)
{
pooledObject.Object.Dispose();
}
}
else
{
pooledObject.Object.Dispose();
}
}
public void Dispose()
{
var queue = _queue;
_queue = null;
Interlocked.MemoryBarrier();
PooledObject<T> pooledObject;
while (queue.TryDequeue(out pooledObject))
{
pooledObject.Object.Dispose();
}
}
}
// Max pooled objects
public class BoundedObjectPool<T> : IObjectPool<PooledObject<T>>, IDisposable
where T : class, IResetable, IDisposable, new()
{
private ConcurrentQueue<PooledObject<T>> _queue = new ConcurrentQueue<PooledObject<T>>();
private int _maxPooled;
public BoundedObjectPool(int maxPooled)
{
_maxPooled = maxPooled;
}
public PooledObject<T> Rent()
{
var queue = _queue;
if (queue == null) throw new ObjectDisposedException(nameof(ObjectPool<T>));
PooledObject<T> pooledObject;
if (queue.TryDequeue(out pooledObject))
{
if (pooledObject.IsRented) throw new ObjectPoolObjectInUseException();
pooledObject.IsRented = true;
return pooledObject;
}
else
{
return new PooledObject<T>(new T(), this) { IsRented = true };
}
}
public void Return(PooledObject<T> pooledObject)
{
if (!pooledObject.IsRented) throw new ObjectPoolObjectReturnedTwiceException();
if (pooledObject.OwningPool != this) throw new ObjectPoolWrongPoolException();
pooledObject.Object.Reset();
pooledObject.IsRented = false;
Interlocked.MemoryBarrier();
var queue = _queue;
if (queue != null && queue.Count < _maxPooled)
{
queue.Enqueue(pooledObject);
if (_queue != null)
{
pooledObject.Object.Dispose();
}
}
else
{
pooledObject.Object.Dispose();
}
}
public void Dispose()
{
var queue = _queue;
_queue = null;
Interlocked.MemoryBarrier();
PooledObject<T> pooledObject;
while (queue.TryDequeue(out pooledObject))
{
pooledObject.Object.Dispose();
}
}
}
// Factory constructor
public class FactoryObjectPool<T> : IObjectPool<PooledObject<T>>, IDisposable
where T : class, IResetable, IDisposable
{
private ConcurrentQueue<PooledObject<T>> _queue = new ConcurrentQueue<PooledObject<T>>();
private int _maxPooled;
private Func<T> _factory;
public FactoryObjectPool(int maxPooled, Func<T> factory)
{
_maxPooled = maxPooled;
_factory = factory;
}
public PooledObject<T> Rent()
{
var queue = _queue;
if (queue == null) throw new ObjectDisposedException(nameof(FactoryObjectPool<T>));
PooledObject<T> pooledObject;
if (queue.TryDequeue(out pooledObject))
{
if (pooledObject.IsRented) throw new ObjectPoolObjectInUseException();
pooledObject.IsRented = true;
return pooledObject;
}
else
{
return new PooledObject<T>(_factory(), this) { IsRented = true };
}
}
public void Return(PooledObject<T> pooledObject)
{
if (!pooledObject.IsRented) throw new ObjectPoolObjectReturnedTwiceException();
if (pooledObject.OwningPool != this) throw new ObjectPoolWrongPoolException();
pooledObject.Object.Reset();
pooledObject.IsRented = false;
Interlocked.MemoryBarrier();
var queue = _queue;
if (queue != null && queue.Count < _maxPooled)
{
queue.Enqueue(pooledObject);
if (_queue != null)
{
pooledObject.Object.Dispose();
}
}
else
{
pooledObject.Object.Dispose();
}
}
public void Dispose()
{
var queue = _queue;
_queue = null;
Interlocked.MemoryBarrier();
PooledObject<T> pooledObject;
while (queue.TryDequeue(out pooledObject))
{
pooledObject.Object.Dispose();
}
}
}
// Test pools
public class CheckedObjectPool<T> : IObjectPool<PooledObject<T>>, IDisposable
where T : class, IResetable, IDisposable, new()
{
private ConcurrentQueue<CheckedPooledObject<T>> _queue = new ConcurrentQueue<CheckedPooledObject<T>>();
public PooledObject<T> Rent()
{
var queue = _queue;
if (queue == null) throw new ObjectDisposedException(nameof(ObjectPool<T>));
CheckedPooledObject<T> pooledObject;
if (queue.TryDequeue(out pooledObject))
{
if (pooledObject.IsRented) throw new ObjectPoolObjectInUseException();
pooledObject.IsRented = true;
return pooledObject;
}
else
{
return new CheckedPooledObject<T>(new T(), this) { IsRented = true };
}
}
public void Return(PooledObject<T> pooledObject)
{
if (!pooledObject.IsRented) throw new ObjectPoolObjectReturnedTwiceException();
if (pooledObject.OwningPool != this) throw new ObjectPoolWrongPoolException();
CheckedPooledObject<T> checkedPooledObject = pooledObject as CheckedPooledObject<T>;
if (checkedPooledObject == null)
{
throw new ArgumentException("Pooled object was not created by this pool", nameof(pooledObject));
}
checkedPooledObject.Object.Reset();
checkedPooledObject.IsRented = false;
Interlocked.MemoryBarrier();
var queue = _queue;
if (queue != null)
{
queue.Enqueue(checkedPooledObject);
if (_queue != null)
{
checkedPooledObject.Object.Dispose();
}
}
else
{
checkedPooledObject.Object.Dispose();
}
}
public void Dispose()
{
var queue = _queue;
_queue = null;
Interlocked.MemoryBarrier();
CheckedPooledObject<T> checkedPooledObject;
while (queue.TryDequeue(out checkedPooledObject))
{
checkedPooledObject.Object.Dispose();
}
}
}
// No actual pooling
public class NoPoolFactoryObjectPool<T> : IObjectPool<T>, IDisposable
where T : class, IResetable, new()
{
private Func<T> _factory;
private bool _isDisposed;
public NoPoolFactoryObjectPool(Func<T> factory)
{
_factory = factory;
}
public T Rent()
{
if (_isDisposed) throw new ObjectDisposedException(nameof(ObjectPool<T>));
return _factory();
}
public void Return(T obj)
{
obj.Reset();
}
public void Dispose()
{
_isDisposed = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment