fix thread safety of Pools, minor FishTable improvements
This commit is contained in:
@@ -9,29 +9,96 @@ namespace FisheryLib.Pools;
|
||||
|
||||
public static class FishPool<T> where T : IFishPoolable
|
||||
{
|
||||
private static T?[] _freeItems = new T[4];
|
||||
private static int _freeItemCount;
|
||||
[ThreadStatic]
|
||||
private static T?[]? _itemsThreadStatic;
|
||||
|
||||
[ThreadStatic]
|
||||
private static int _itemCountThreadStatic;
|
||||
|
||||
private static object _lockObject = new();
|
||||
private static T?[] _itemsGlobal = new T[16];
|
||||
private static int _itemCountGlobal;
|
||||
|
||||
public static T Get()
|
||||
{
|
||||
if (_freeItemCount == 0)
|
||||
return Reflection.New<T>();
|
||||
if (_itemCountThreadStatic == 0)
|
||||
FetchThroughLock();
|
||||
|
||||
ref var itemBucket = ref _itemsThreadStatic![--_itemCountThreadStatic];
|
||||
var item = itemBucket;
|
||||
itemBucket = default;
|
||||
return item!;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void FetchThroughLock()
|
||||
{
|
||||
var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic();
|
||||
var createNew = false;
|
||||
|
||||
var freeItem = _freeItems[--_freeItemCount];
|
||||
_freeItems[_freeItemCount] = default;
|
||||
return freeItem!;
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_itemCountGlobal != 0)
|
||||
{
|
||||
for (var i = 8; i-- > 0;)
|
||||
{
|
||||
itemsThreadStatic[i] = _itemsGlobal[--_itemCountGlobal];
|
||||
_itemsGlobal[_itemCountGlobal] = default;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
createNew = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (createNew)
|
||||
{
|
||||
for (var i = 8; i-- > 0;)
|
||||
itemsThreadStatic[i] = Reflection.New<T>();
|
||||
}
|
||||
|
||||
_itemCountThreadStatic = 8;
|
||||
}
|
||||
|
||||
public static void Return(T item)
|
||||
{
|
||||
item.Reset();
|
||||
|
||||
if (++_freeItemCount >= _freeItems.Length)
|
||||
ExpandFreeItems();
|
||||
|
||||
_freeItems[_freeItemCount - 1] = item;
|
||||
var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic();
|
||||
|
||||
if (_itemCountThreadStatic == 16)
|
||||
PushThroughLock();
|
||||
|
||||
itemsThreadStatic[_itemCountThreadStatic++] = item;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ExpandFreeItems() => Array.Resize(ref _freeItems, _freeItems.Length << 1);
|
||||
private static void PushThroughLock()
|
||||
{
|
||||
var itemsThreadStatic = _itemsThreadStatic!;
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_itemCountGlobal + 8 > _itemsGlobal.Length)
|
||||
ExpandGlobalItems();
|
||||
|
||||
for (var i = 16; i-- > 8;)
|
||||
{
|
||||
ref var itemBucket = ref itemsThreadStatic[i];
|
||||
var item = itemBucket;
|
||||
itemBucket = default;
|
||||
|
||||
_itemsGlobal[_itemCountGlobal++] = item;
|
||||
}
|
||||
}
|
||||
|
||||
_itemCountThreadStatic = 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static T?[] InitializeItemsThreadStatic() => new T?[16];
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ExpandGlobalItems() => Array.Resize(ref _itemsGlobal, _itemsGlobal.Length << 1);
|
||||
}
|
||||
Reference in New Issue
Block a user