116 lines
2.5 KiB
C#
116 lines
2.5 KiB
C#
// Copyright (c) 2023 bradson
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
using System.Collections;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace FisheryLib.Pools;
|
|
|
|
public record struct PooledIList<T>() : IDisposable where T : IList, new()
|
|
{
|
|
public T List = Get();
|
|
|
|
[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()
|
|
{
|
|
if (List == null)
|
|
return;
|
|
|
|
Return(List);
|
|
List = default!;
|
|
}
|
|
|
|
public static T Get()
|
|
{
|
|
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;
|
|
|
|
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();
|
|
|
|
var itemsThreadStatic = _itemsThreadStatic ??= InitializeItemsThreadStatic();
|
|
|
|
if (_itemCountThreadStatic == 16)
|
|
PushThroughLock();
|
|
|
|
itemsThreadStatic[_itemCountThreadStatic++] = item;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
|
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);
|
|
} |