Files
Recyclable.Collections/Recyclable.Collections/OneSizeArrayPool.cs
T

180 lines
4.1 KiB
C#

using System.Buffers;
using System.Collections.Concurrent;
namespace Recyclable.Collections
{
public class MemoryBucket<T>
{
public MemoryBucket(T[] memory, MemoryBucket<T> nextBucket)
{
_memory = memory;
_nextBucket = nextBucket;
}
public MemoryBucket(T[] memory)
{
_memory = memory;
}
private T[] _memory;
public T[] Memory { get => _memory; set => _memory = value; }
private MemoryBucket<T>? _nextBucket;
public MemoryBucket<T>? NextBucket { get => _nextBucket; set => _nextBucket = value; }
}
public static class RecyclableArrayPool<T>
{
public static readonly OneSizeArrayPool<T> Null = new(0, 0);
private static readonly ConcurrentDictionary<int, ArrayPool<T>> _pools = new();
public static ArrayPool<T> Shared(int blockSize)
{
//lock (_lock)
{
if (!_pools.TryGetValue(blockSize, out ArrayPool<T>? pool))
{
pool = new OneSizeArrayPool<T>(blockSize, 100);
pool = _pools.TryAdd(blockSize, pool) ? pool : _pools[blockSize];
}
return pool;
}
//_lock.Wait();
//try
//{
//}
//finally
//{
// _lock.Release();
//}
}
}
public class OneSizeArrayPool<T> : ArrayPool<T>
{
private readonly int _blockSize;
private readonly int _minBlockCount;
private int _blockCount;
private MemoryBucket<T>? _currentBucket;
//SemaphoreSlim _lock = new(1);
//ManualResetEventSlim _lock = new(true, 1);
private readonly object _updateLock = new();
//private Task? _bufferFillUpTask;
//private Task CreateAdditionalBucket(int minimumLength, int blockCount)
//{
// return Task.Factory.StartNew(() =>
// {
// MemoryBucket<T> firstBucket = new(new T[minimumLength]);
// MemoryBucket<T> currentBucket = firstBucket;
// for (var i = 0; i < blockCount; i++)
// {
// currentBucket = new(new T[minimumLength], currentBucket);
// }
// lock (_updateLock)
// //_lock.Wait();
// //try
// {
// currentBucket.NextBucket = _currentBucket;
// _currentBucket = firstBucket;
// _blockCount += blockCount;
// //_bufferFillUpTask = null;
// }
// //finally
// //{
// // _ = _lock.Release();
// // //_lock.Set();
// //}
// });
//}
public OneSizeArrayPool(int blockSize, int minBlockCount)
{
_blockSize = blockSize;
_minBlockCount = minBlockCount;
}
public override T[] Rent(int minimumLength)
{
if (minimumLength != _blockSize)
{
ThrowWrongRentedArraySizeException(minimumLength, _blockSize);
}
MemoryBucket<T>? bucket;
lock (_updateLock)
//_lock.Wait();
//try
{
if (_currentBucket != null)
{
_blockCount--;
bucket = _currentBucket;
_currentBucket = _currentBucket.NextBucket;
}
else
{
bucket = null;
//if (_bufferFillUpTask == null)
//{
// _bufferFillUpTask = CreateAdditionalBucket(minimumLength, _minBlockCount);
//}
}
}
//finally
//{
// _ = _lock.Release();
// //_lock.Set();
//}
return bucket != null ? bucket.Memory : new T[_blockSize];
}
public override void Return(T[] array, bool clearArray = false)
{
if (array.Length != _blockSize)
{
ThrowWrongReturnedArraySizeException(array.Length, _blockSize);
}
MemoryBucket<T> bucket = new(array);
if (clearArray)
{
Array.Clear(array, 0, array.Length);
}
lock (_updateLock)
//lock (_lock)
//_lock.Wait();
//try
{
if (_blockCount < _minBlockCount)
{
bucket.NextBucket = _currentBucket;
_currentBucket = bucket;
_blockCount++;
}
}
//finally
//{
// _ = _lock.Release();
// //_lock.Set();
//}
}
private static void ThrowWrongReturnedArraySizeException(int returnedLength, int blockSize)
{
throw new InvalidOperationException($"The array size {returnedLength} doesn't match the size {blockSize} of the pool buffer and cannot be returned to this pool.");
}
private static void ThrowWrongRentedArraySizeException(int requestedSize, int blockSize)
{
throw new InvalidOperationException($"The requested size {requestedSize} doesn't match the size {blockSize} of the pool buffer and cannot be taken from this pool.");
}
}
}