fix thread safety of Pools, minor FishTable improvements

This commit is contained in:
bbradson
2024-03-10 19:45:02 +01:00
parent af759661bd
commit c584f58dfd
11 changed files with 267 additions and 132 deletions
+80 -13
View File
@@ -12,8 +12,15 @@ public record struct PooledIList<T>() : IDisposable where T : IList, new()
{
public T List = Get();
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 void Dispose()
{
@@ -23,27 +30,87 @@ public record struct PooledIList<T>() : IDisposable where T : IList, new()
Return(List);
List = default!;
}
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.Clear();
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);
}