/// <summary> /// Resize the <see cref="ArraySegmentPool{T}"/> and update the instance reference. /// </summary> /// <param name="pool"></param> /// <returns>New pool</returns> private _pool GetNewPool(_pool pool) { lock (_poolLock) { if (_isAutoResize == false) { throw new OverflowException("ArraySegmentPool size out of max capacity"); } //check if other thread already create new resized pool. if (pool != _currentPool) { return(_currentPool); } //check limits. if (pool.ArrayLayout.Length == _maxCapacity) { throw new OverflowException("ArraySegmentPool size out of max capacity"); } //create new resized pool and refresh current ref. int newLayoutLength = pool.ArrayLayout.Length * 2L < _maxCapacity ? pool.ArrayLayout.Length * 2 : _maxCapacity; int newLength = _maxArraySegmentLength * newLayoutLength; _currentPool = new _pool() { ArrayLayout = new int[newLayoutLength], Array = new T[newLength] }; //return new pool. return(_currentPool); } }
/// <summary> /// Sets the capacity of <see cref="ArraySegmentPool{T}"/> to the size of the used <see cref="ArraySegment{T}"/>. This method can be used to minimize a pool's memory overhead once it is known that no new <see cref = "ArraySegment{T}" /> will be added to the <see cref= "ArraySegmentPool{T}"/>. /// </summary> public void TrimExcess() { lock (_poolLock) { if (_isAutoResize == false) { throw new AccessViolationException("Can't trim while auto resize false"); } int count = _currentPool.Count; int new_layout_length = count > 1 ? count : 1; int new_length = new_layout_length * _maxArraySegmentLength; _currentPool = new _pool() { ArrayLayout = new int[new_layout_length], Array = new T[new_length] }; } }
/// <summary> /// (!) Dangerous. Gets an <see cref="ArraySegment{T}"/> of the custom length. <see cref="ArraySegment{T}"/> must be returned via <see cref="Return"/> on the same <see cref="ArraySegmentPool{T}"/> instance to avoid memory leaks. /// </summary> /// <param name="length">Lenght of the rented <see cref="ArraySegment{T}"/>. Lenght must be equal or smaller than <see cref="defaultLength"/></param> /// <returns><see cref="ArraySegment{T}"/></returns> public ArraySegment <T> DangerousRent(int length) { if (length <1 | length> _maxArraySegmentLength) { throw new ArgumentOutOfRangeException("Length must be greater than one and smaller than default length"); } _pool pool = _currentPool; //Get new resized pool if free segment not finded. if (pool.Count >= pool.ArrayLayout.Length) { pool = GetNewPool(pool); } //Try find free segment and ocupy. int position = pool.LastRentedSegment + 1; int searchCount = 0; do { if (position > pool.ArrayLayout.GetUpperBound(0)) { position = 0; } if (System.Threading.Interlocked.CompareExchange(ref pool.ArrayLayout[position], 1, 0) == 0) { System.Threading.Interlocked.Increment(ref pool.Count); pool.LastRentedSegment = position; return(new ArraySegment <T>(pool.Array, position * _maxArraySegmentLength, length)); } #if UT System.Threading.Interlocked.Increment(ref _failsCount); #endif position += 1; searchCount += 1; //That check prevent state, where thread will loop forever. if (searchCount == pool.ArrayLayout.Length) { pool = GetNewPool(pool); position = 0; searchCount = 0; } }while (true); }
/// <summary> /// Returns to the <see cref="ArraySegmentPool{T}"/> an <see cref="ArraySegment{T}"/> that was previously obtained via <see cref="DangerousRent"/> on the same <see cref="ArraySegmentPool{T}"/> instance. /// </summary> /// <param name="ArraySegment"></param> public void Return(ref ArraySegment <T> ArraySegment) { if (ArraySegment.Count == 0) { throw new ArgumentException("Do not Slice rented ArraySegment to zero, since the pool will not be able to free memory"); } _pool pool = _currentPool; if (ArraySegment.Array == pool.Array) { //return segment. int position = ArraySegment.Offset / _maxArraySegmentLength; if (System.Threading.Interlocked.Exchange(ref pool.ArrayLayout[position], 0) == 0) { throw new Exception("ArraySegment was returned already"); } System.Threading.Interlocked.Decrement(ref pool.Count); } ArraySegment = ArraySegment <T> .Empty; }
private ArraySegmentPool(int maxArraySegmentLength, int initialCapacity, int maxCapacity, bool autoResize) { if (maxArraySegmentLength < 1 | initialCapacity < 1 | maxCapacity < 1) { throw new ArgumentOutOfRangeException("Arguments must be greater than 1"); } if (initialCapacity > maxCapacity) { throw new ArgumentOutOfRangeException("InitialCapacity > MaxCapacity"); } if ((long)maxArraySegmentLength * maxCapacity > int.MaxValue) { throw new OverflowException("MaxCapacity"); } _maxArraySegmentLength = maxArraySegmentLength; _maxCapacity = maxCapacity; _isAutoResize = autoResize; _currentPool = new _pool() { ArrayLayout = new int[initialCapacity], Array = new T[_maxArraySegmentLength * initialCapacity] }; }