using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; namespace ARMeilleure.Common { internal class ThreadStaticPool where T : class, new() { private const int PoolSizeIncrement = 200; [ThreadStatic] private static ThreadStaticPool _instance; public static ThreadStaticPool Instance { get { if (_instance == null) { PreparePool(0); // So that we can still use a pool when blindly initializing one. } return _instance; } } private static ConcurrentDictionary>> _pools = new ConcurrentDictionary>>(); private static Stack> GetPools(int groupId) { return _pools.GetOrAdd(groupId, x => new Stack>()); } public static void PreparePool(int groupId) { // Prepare the pool for this thread, ideally using an existing one from the specified group. if (_instance == null) { Stack> pools = GetPools(groupId); lock (pools) { _instance = (pools.Count != 0) ? pools.Pop() : new ThreadStaticPool(PoolSizeIncrement * 2); } } } public static void ReturnPool(int groupId) { // Reset and return the pool for this thread to the specified group. Stack> pools = GetPools(groupId); lock (pools) { _instance.Clear(); pools.Push(_instance); _instance = null; } } private T[] _pool; private int _poolUsed = -1; private int _poolSize; public ThreadStaticPool(int initialSize) { _pool = new T[initialSize]; for (int i = 0; i < initialSize; i++) { _pool[i] = new T(); } _poolSize = initialSize; } public T Allocate() { int index = Interlocked.Increment(ref _poolUsed); if (index >= _poolSize) { IncreaseSize(); } return _pool[index]; } private void IncreaseSize() { _poolSize += PoolSizeIncrement; T[] newArray = new T[_poolSize]; Array.Copy(_pool, 0, newArray, 0, _pool.Length); for (int i = _pool.Length; i < _poolSize; i++) { newArray[i] = new T(); } Interlocked.Exchange(ref _pool, newArray); } public void Clear() { _poolUsed = -1; } } }