146 lines
5.0 KiB
C#
146 lines
5.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Boxfriend.Utils
|
|
{
|
|
/// <summary>
|
|
/// Pools a specific number of objects, will reuse oldest active objects when all objects are in use.
|
|
/// </summary>
|
|
public class ObjectPoolCircular<T> where T : class
|
|
{
|
|
|
|
private Queue<T> _activeQueue, _inactiveQueue;
|
|
private readonly Func<T> _objectCreator;
|
|
private readonly Action<T> _returnObjectToPool, _getObjectFromPool, _destroyObject;
|
|
private readonly int _size;
|
|
|
|
/// <summary>
|
|
/// Total number of objects in the pool.
|
|
/// </summary>
|
|
public int Count => _size;
|
|
|
|
/// <summary>
|
|
/// Total number of currently active pooled objects
|
|
/// </summary>
|
|
public int ActiveCount => _activeQueue.Count;
|
|
|
|
/// <summary>
|
|
/// Total number of currently inactive pooled objects
|
|
/// </summary>
|
|
public int InactiveCount => _inactiveQueue.Count;
|
|
|
|
/// <param name="createObject">Creates and returns an object of the specified type.</param>
|
|
/// <param name="getObjectFromPool">Action called on object when pulled from the pool or created.</param>
|
|
/// <param name="returnObjectToPool">Action called on object when returned to pool.</param>
|
|
/// <param name="onDestroyObject">Action called on object when it is to be destroyed. Can be null</param>
|
|
/// <param name="size">Total number of objects in the pool</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">Size must be greater than zero</exception>
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
public ObjectPoolCircular (Func<T> createObject, Action<T> getObjectFromPool, Action<T> returnObjectToPool, Action<T> onDestroyObject = null, int size = 100)
|
|
{
|
|
if (size <= 0)
|
|
throw new ArgumentOutOfRangeException(nameof(size), "size must be greater than zero");
|
|
|
|
|
|
_returnObjectToPool = returnObjectToPool ?? throw new ArgumentNullException(nameof(returnObjectToPool));
|
|
_getObjectFromPool = getObjectFromPool ?? throw new ArgumentNullException(nameof(getObjectFromPool));
|
|
_objectCreator = createObject ?? throw new ArgumentNullException(nameof(createObject));
|
|
_destroyObject = onDestroyObject;
|
|
|
|
_size = size;
|
|
_inactiveQueue = new(size);
|
|
_activeQueue = new(size);
|
|
for (var i = 0; i < size; i++)
|
|
{
|
|
var obj = _objectCreator();
|
|
_returnObjectToPool(obj);
|
|
_inactiveQueue.Enqueue(obj);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an object from the pool or reuses the oldest active object if all pooled objects are in use. Calls <see langword="Action"/> <see cref="_getObjectFromPool"/> on the object
|
|
/// Will call <see langword="Action"/> <see cref="_returnObjectToPool"/> if reusing an active object.
|
|
/// </summary>
|
|
public T FromPool ()
|
|
{
|
|
if(_inactiveQueue.Count + _activeQueue.Count == 0)
|
|
throw new InvalidOperationException("Object pool has been cleared, there is nothing left to get");
|
|
|
|
|
|
T poolObject;
|
|
if(_inactiveQueue.Count == 0)
|
|
{
|
|
poolObject = _activeQueue.Dequeue();
|
|
_returnObjectToPool(poolObject);
|
|
}
|
|
else
|
|
{
|
|
poolObject = _inactiveQueue.Dequeue();
|
|
}
|
|
|
|
_getObjectFromPool(poolObject);
|
|
_activeQueue.Enqueue(poolObject);
|
|
return poolObject;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an item to the pool and calls <see langword="Action"/> <see cref="_returnObjectToPool"/> on it
|
|
/// Generates garbage if item is not the oldest object pulled from the pool
|
|
/// </summary>
|
|
/// <param name="item">Item to be added</param>
|
|
public void ToPool (T item)
|
|
{
|
|
if (item == null) throw new ArgumentNullException(nameof(item));
|
|
|
|
if(_activeQueue.Peek() == item)
|
|
_activeQueue.Dequeue();
|
|
else
|
|
_activeQueue = new Queue<T>(_activeQueue.Where(x => x != item));
|
|
|
|
_returnObjectToPool(item);
|
|
_inactiveQueue.Enqueue(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all active items to the inactive queue
|
|
/// </summary>
|
|
public void ReturnAllToPool()
|
|
{
|
|
while(ActiveCount > 0)
|
|
{
|
|
var obj = _activeQueue.Dequeue();
|
|
_returnObjectToPool(obj);
|
|
_inactiveQueue.Enqueue(obj);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all items from the pool, calling <see langword="Action"/> <see cref="_destroyObject"/> on it if not null.
|
|
/// Does not call <see langword="Action"/> <see cref="_returnObjectToPool"/>.
|
|
/// </summary>
|
|
public void EmptyPool()
|
|
{
|
|
if(_destroyObject is null)
|
|
{
|
|
_activeQueue.Clear();
|
|
_inactiveQueue.Clear();
|
|
return;
|
|
}
|
|
|
|
while(_activeQueue.Count > 0)
|
|
{
|
|
var obj = _activeQueue.Dequeue();
|
|
_destroyObject(obj);
|
|
}
|
|
|
|
while(_inactiveQueue.Count > 0)
|
|
{
|
|
var obj = _inactiveQueue.Dequeue();
|
|
_destroyObject(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|