Files
dungeons/Assets/Plugins/Zenject/Source/Factories/Pooling/MemoryPoolBase.cs

291 lines
7.3 KiB
C#

using System;
using System.Collections.Generic;
using ModestTree;
namespace Zenject
{
[NoReflectionBaking]
public class PoolExceededFixedSizeException : Exception
{
public PoolExceededFixedSizeException(string errorMessage)
: base(errorMessage)
{
}
}
[Serializable]
public class MemoryPoolSettings
{
public int InitialSize;
public int MaxSize;
public PoolExpandMethods ExpandMethod;
public MemoryPoolSettings()
{
InitialSize = 0;
MaxSize = int.MaxValue;
ExpandMethod = PoolExpandMethods.OneAtATime;
}
public MemoryPoolSettings(int initialSize, int maxSize, PoolExpandMethods expandMethod)
{
InitialSize = initialSize;
MaxSize = maxSize;
ExpandMethod = expandMethod;
}
public static readonly MemoryPoolSettings Default = new MemoryPoolSettings();
}
[ZenjectAllowDuringValidation]
public class MemoryPoolBase<TContract> : IValidatable, IMemoryPool, IDisposable
{
Stack<TContract> _inactiveItems;
IFactory<TContract> _factory;
MemoryPoolSettings _settings;
DiContainer _container;
int _activeCount;
[Inject]
void Construct(
IFactory<TContract> factory,
DiContainer container,
[InjectOptional]
MemoryPoolSettings settings)
{
_settings = settings ?? MemoryPoolSettings.Default;
_factory = factory;
_container = container;
_inactiveItems = new Stack<TContract>(_settings.InitialSize);
if (!container.IsValidating)
{
for (int i = 0; i < _settings.InitialSize; i++)
{
_inactiveItems.Push(AllocNew());
}
}
#if UNITY_EDITOR
StaticMemoryPoolRegistry.Add(this);
#endif
}
protected DiContainer Container
{
get { return _container; }
}
public IEnumerable<TContract> InactiveItems
{
get { return _inactiveItems; }
}
public int NumTotal
{
get { return NumInactive + NumActive; }
}
public int NumInactive
{
get { return _inactiveItems.Count; }
}
public int NumActive
{
get { return _activeCount; }
}
public Type ItemType
{
get { return typeof(TContract); }
}
public void Dispose()
{
#if UNITY_EDITOR
StaticMemoryPoolRegistry.Remove(this);
#endif
}
void IMemoryPool.Despawn(object item)
{
Despawn((TContract)item);
}
public void Despawn(TContract item)
{
Assert.That(!_inactiveItems.Contains(item),
"Tried to return an item to pool {0} twice", GetType());
_activeCount--;
_inactiveItems.Push(item);
#if ZEN_INTERNAL_PROFILING
using (ProfileTimers.CreateTimedBlock("User Code"))
#endif
#if UNITY_EDITOR
using (ProfileBlock.Start("{0}.OnDespawned", GetType()))
#endif
{
OnDespawned(item);
}
if (_inactiveItems.Count > _settings.MaxSize)
{
Resize(_settings.MaxSize);
}
}
TContract AllocNew()
{
try
{
var item = _factory.Create();
if (!_container.IsValidating)
{
Assert.IsNotNull(item, "Factory '{0}' returned null value when creating via {1}!", _factory.GetType(), GetType());
OnCreated(item);
}
return item;
}
catch (Exception e)
{
throw new ZenjectException(
"Error during construction of type '{0}' via {1}.Create method!".Fmt(
typeof(TContract), GetType()), e);
}
}
void IValidatable.Validate()
{
try
{
_factory.Create();
}
catch (Exception e)
{
throw new ZenjectException(
"Validation for factory '{0}' failed".Fmt(GetType()), e);
}
}
public void Clear()
{
Resize(0);
}
public void ShrinkBy(int numToRemove)
{
Resize(_inactiveItems.Count - numToRemove);
}
public void ExpandBy(int numToAdd)
{
Resize(_inactiveItems.Count + numToAdd);
}
protected TContract GetInternal()
{
if (_inactiveItems.Count == 0)
{
ExpandPool();
Assert.That(!_inactiveItems.IsEmpty());
}
var item = _inactiveItems.Pop();
_activeCount++;
OnSpawned(item);
return item;
}
public void Resize(int desiredPoolSize)
{
if (_inactiveItems.Count == desiredPoolSize)
{
return;
}
if (_settings.ExpandMethod == PoolExpandMethods.Disabled)
{
throw new PoolExceededFixedSizeException(
"Pool factory '{0}' attempted resize but pool set to fixed size of '{1}'!"
.Fmt(GetType(), _inactiveItems.Count));
}
Assert.That(desiredPoolSize >= 0, "Attempted to resize the pool to a negative amount");
while (_inactiveItems.Count > desiredPoolSize)
{
OnDestroyed(_inactiveItems.Pop());
}
while (desiredPoolSize > _inactiveItems.Count)
{
_inactiveItems.Push(AllocNew());
}
Assert.IsEqual(_inactiveItems.Count, desiredPoolSize);
}
void ExpandPool()
{
switch (_settings.ExpandMethod)
{
case PoolExpandMethods.Disabled:
{
throw new PoolExceededFixedSizeException(
"Pool factory '{0}' exceeded its fixed size of '{1}'!"
.Fmt(GetType(), _inactiveItems.Count));
}
case PoolExpandMethods.OneAtATime:
{
ExpandBy(1);
break;
}
case PoolExpandMethods.Double:
{
if (NumTotal == 0)
{
ExpandBy(1);
}
else
{
ExpandBy(NumTotal);
}
break;
}
default:
{
throw Assert.CreateException();
}
}
}
protected virtual void OnDespawned(TContract item)
{
// Optional
}
protected virtual void OnSpawned(TContract item)
{
// Optional
}
protected virtual void OnCreated(TContract item)
{
// Optional
}
protected virtual void OnDestroyed(TContract item)
{
// Optional
}
}
}