Skip to content

Instantly share code, notes, and snippets.

@gamemachine
Last active August 21, 2019 07:06
Show Gist options
  • Select an option

  • Save gamemachine/d9ab8246558dac8e6db747d52de127c6 to your computer and use it in GitHub Desktop.

Select an option

Save gamemachine/d9ab8246558dac8e6db747d52de127c6 to your computer and use it in GitHub Desktop.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
namespace GameCommon.Pooling
{
public class ByteArrayPool
{
private const int MaxArrayLength = 512;
private const int MaxRentTime = 1000; // in milliseconds
static readonly ByteArrayPool instance = new ByteArrayPool();
private ConcurrentDictionary<int, ConcurrentQueue<ByteArrayWrapper>> Pools = new ConcurrentDictionary<int, ConcurrentQueue<ByteArrayWrapper>>();
private List<ByteArrayWrapper> Wrappers = new List<ByteArrayWrapper>();
private int LargeMessageCount = 0;
private int ForceReturnCount = 0;
private Stopwatch StopWatch;
public static ByteArrayPool Instance
{
get
{
return instance;
}
}
private ByteArrayPool()
{
StopWatch = Stopwatch.StartNew();
}
public void Clear()
{
Pools.Clear();
Wrappers.Clear();
}
public ByteArrayPoolStats Update()
{
ByteArrayPoolStats stats = new ByteArrayPoolStats();
stats.WrapperCount = Wrappers.Count;
stats.LargeMessageCount = LargeMessageCount;
long now = StopWatch.ElapsedMilliseconds;
foreach (ByteArrayWrapper wrapper in Wrappers)
{
if (wrapper.IsRented)
{
long elapsed = now - wrapper.RentedAt;
if (elapsed > MaxRentTime)
{
wrapper.Return();
ForceReturnCount++;
} else
{
stats.RentedCount++;
}
}
}
stats.ForceReturnCount = ForceReturnCount;
return stats;
}
public ByteArrayWrapper Rent(int length)
{
ByteArrayWrapper wrapper = null;
if (length > MaxArrayLength)
{
LargeMessageCount++;
wrapper = new ByteArrayWrapper(length);
wrapper.Rent(StopWatch.ElapsedMilliseconds);
wrapper.InPool = false;
return wrapper;
}
ConcurrentQueue<ByteArrayWrapper> pool;
if (!Pools.TryGetValue(length, out pool))
{
pool = new ConcurrentQueue<ByteArrayWrapper>();
Pools[length] = pool;
}
ByteArrayWrapper result;
while (!pool.TryDequeue(out result))
{
wrapper = new ByteArrayWrapper(length);
Wrappers.Add(wrapper);
pool.Enqueue(wrapper);
}
result.Rent(StopWatch.ElapsedMilliseconds);
return result;
}
public void Return(ByteArrayWrapper wrapper)
{
ConcurrentQueue<ByteArrayWrapper> pool;
if (!Pools.TryGetValue(wrapper.Length, out pool))
{
throw new ByteArrayPoolException("Return failed - pool not found");
}
wrapper.Reclaim();
pool.Enqueue(wrapper);
}
}
}
namespace GameCommon.Pooling
{
public class ByteArrayWrapper
{
public bool InPool;
public int Length;
public bool IsRented { get; private set; }
private byte[] Value;
public long RentedAt { get; private set; }
public ByteArrayWrapper(int length)
{
InPool = true;
Length = length;
IsRented = false;
Value = new byte[length];
}
public byte[] GetBytes()
{
if (!IsRented)
{
throw new ByteArrayPoolException("Attempt to access value before renting");
}
return Value;
}
public void Return()
{
if (!InPool)
{
return;
}
if (!IsRented)
{
throw new ByteArrayPoolException("Already returned");
}
ByteArrayPool.Instance.Return(this);
}
public void Rent(long rentedAt)
{
RentedAt = rentedAt;
IsRented = true;
}
public void Reclaim()
{
IsRented = false;
}
}
}
namespace GameCommon.Pooling
{
public struct ByteArrayPoolStats
{
public int WrapperCount;
public int RentedCount;
public int LargeMessageCount;
public int ForceReturnCount;
}
}
using System;
namespace GameCommon.Pooling
{
public class ByteArrayPoolException : Exception
{
public ByteArrayPoolException()
{
}
public ByteArrayPoolException(string message)
: base(message)
{
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment