Fix NullReferenceException in ObjectPool when reusing all returned items (#904)

* Replaced delegate with Action<T>

* Change delegate to Action<T>

* Add unit test to confirm issue

* Fix NullReferenceException in ObjectPool when reusing all returned items

The ObjectPool's Use() method was throwing a NullReferenceException when
all items had been returned to the pool and New() was called again. This
was due to _tailNode being null in this scenario.

Added a null check for _tailNode in Use() method to properly reinitialize
the linked list structure when the pool has been emptied and refilled.
This commit is contained in:
Christopher Whitley
2024-06-29 17:50:43 -04:00
committed by GitHub
parent 93473744d7
commit 1f3f1c5aa0
3 changed files with 65 additions and 8 deletions
@@ -1,12 +1,12 @@
namespace MonoGame.Extended.Collections
{
public delegate void ReturnToPoolDelegate(IPoolable poolable);
using System;
namespace MonoGame.Extended.Collections
{
public interface IPoolable
{
IPoolable NextNode { get; set; }
IPoolable PreviousNode { get; set; }
void Initialize(ReturnToPoolDelegate returnDelegate);
void Initialize(Action<IPoolable> returnDelegate);
void Return();
}
}
}
@@ -15,7 +15,7 @@ namespace MonoGame.Extended.Collections
public class ObjectPool<T> : IEnumerable<T>
where T : class, IPoolable
{
private readonly ReturnToPoolDelegate _returnToPoolDelegate;
private readonly Action<IPoolable> _returnToPoolDelegate;
private readonly Deque<T> _freeItems; // circular buffer for O(1) operations
private T _headNode; // linked list for iteration
@@ -144,7 +144,13 @@ namespace MonoGame.Extended.Collections
{
item.Initialize(_returnToPoolDelegate);
item.NextNode = null;
if (item != _tailNode)
if(_tailNode is null)
{
_headNode = item;
_tailNode = item;
item.PreviousNode = null;
}
else
{
item.PreviousNode = _tailNode;
_tailNode.NextNode = item;
@@ -154,4 +160,4 @@ namespace MonoGame.Extended.Collections
ItemUsed?.Invoke(item);
}
}
}
}