diff options
Diffstat (limited to 'src/mscorlib/corefx/System/Buffers/ConfigurableArrayPool.cs')
-rw-r--r-- | src/mscorlib/corefx/System/Buffers/ConfigurableArrayPool.cs | 265 |
1 files changed, 0 insertions, 265 deletions
diff --git a/src/mscorlib/corefx/System/Buffers/ConfigurableArrayPool.cs b/src/mscorlib/corefx/System/Buffers/ConfigurableArrayPool.cs deleted file mode 100644 index f7b6034d20..0000000000 --- a/src/mscorlib/corefx/System/Buffers/ConfigurableArrayPool.cs +++ /dev/null @@ -1,265 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Threading; - -namespace System.Buffers -{ - internal sealed partial class ConfigurableArrayPool<T> : ArrayPool<T> - { - /// <summary>The default maximum length of each array in the pool (2^20).</summary> - private const int DefaultMaxArrayLength = 1024 * 1024; - /// <summary>The default maximum number of arrays per bucket that are available for rent.</summary> - private const int DefaultMaxNumberOfArraysPerBucket = 50; - - private readonly Bucket[] _buckets; - - internal ConfigurableArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket) - { - } - - internal ConfigurableArrayPool(int maxArrayLength, int maxArraysPerBucket) - { - if (maxArrayLength <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxArrayLength)); - } - if (maxArraysPerBucket <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket)); - } - - // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30. - // Constrain the actual max used to those values. - const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000; - if (maxArrayLength > MaximumArrayLength) - { - maxArrayLength = MaximumArrayLength; - } - else if (maxArrayLength < MinimumArrayLength) - { - maxArrayLength = MinimumArrayLength; - } - - // Create the buckets. - int poolId = Id; - int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength); - var buckets = new Bucket[maxBuckets + 1]; - for (int i = 0; i < buckets.Length; i++) - { - buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId); - } - _buckets = buckets; - } - - /// <summary>Gets an ID for the pool to use with events.</summary> - private int Id => GetHashCode(); - - public override T[] Rent(int minimumLength) - { - // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though - // pooling such an array isn't valuable) as it's a valid length array, and we want the pool - // to be usable in general instead of using `new`, even for computed lengths. - if (minimumLength < 0) - { - throw new ArgumentOutOfRangeException(nameof(minimumLength)); - } - else if (minimumLength == 0) - { - // No need for events with the empty array. Our pool is effectively infinite - // and we'll never allocate for rents and never store for returns. - return Array.Empty<T>(); - } - - var log = ArrayPoolEventSource.Log; - T[] buffer = null; - - int index = Utilities.SelectBucketIndex(minimumLength); - if (index < _buckets.Length) - { - // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the - // next higher bucket and try that one, but only try at most a few buckets. - const int MaxBucketsToTry = 2; - int i = index; - do - { - // Attempt to rent from the bucket. If we get a buffer from it, return it. - buffer = _buckets[i].Rent(); - if (buffer != null) - { - if (log.IsEnabled()) - { - log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, _buckets[i].Id); - } - return buffer; - } - } - while (++i < _buckets.Length && i != index + MaxBucketsToTry); - - // The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding - // to the appropriate bucket. - buffer = new T[_buckets[index]._bufferLength]; - } - else - { - // The request was for a size too large for the pool. Allocate an array of exactly the requested length. - // When it's returned to the pool, we'll simply throw it away. - buffer = new T[minimumLength]; - } - - if (log.IsEnabled()) - { - int bufferId = buffer.GetHashCode(), bucketId = -1; // no bucket for an on-demand allocated buffer - log.BufferRented(bufferId, buffer.Length, Id, bucketId); - log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, index >= _buckets.Length ? - ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); - } - - return buffer; - } - - public override void Return(T[] array, bool clearArray = false) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - else if (array.Length == 0) - { - // Ignore empty arrays. When a zero-length array is rented, we return a singleton - // rather than actually taking a buffer out of the lowest bucket. - return; - } - - // Determine with what bucket this array length is associated - int bucket = Utilities.SelectBucketIndex(array.Length); - - // If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool - if (bucket < _buckets.Length) - { - // Clear the array if the user requests - if (clearArray) - { - Array.Clear(array, 0, array.Length); - } - - // Return the buffer to its bucket. In the future, we might consider having Return return false - // instead of dropping a bucket, in which case we could try to return to a lower-sized bucket, - // just as how in Rent we allow renting from a higher-sized bucket. - _buckets[bucket].Return(array); - } - - // Log that the buffer was returned - var log = ArrayPoolEventSource.Log; - if (log.IsEnabled()) - { - log.BufferReturned(array.GetHashCode(), array.Length, Id); - } - } - - /// <summary>Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd.</summary> - private sealed class Bucket - { - internal readonly int _bufferLength; - private readonly T[][] _buffers; - private readonly int _poolId; - - private SpinLock _lock; // do not make this readonly; it's a mutable struct - private int _index; - - /// <summary> - /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length. - /// </summary> - internal Bucket(int bufferLength, int numberOfBuffers, int poolId) - { - _lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit - _buffers = new T[numberOfBuffers][]; - _bufferLength = bufferLength; - _poolId = poolId; - } - - /// <summary>Gets an ID for the bucket to use with events.</summary> - internal int Id => GetHashCode(); - - /// <summary>Takes an array from the bucket. If the bucket is empty, returns null.</summary> - internal T[] Rent() - { - T[][] buffers = _buffers; - T[] buffer = null; - - // While holding the lock, grab whatever is at the next available index and - // update the index. We do as little work as possible while holding the spin - // lock to minimize contention with other threads. The try/finally is - // necessary to properly handle thread aborts on platforms which have them. - bool lockTaken = false, allocateBuffer = false; - try - { - _lock.Enter(ref lockTaken); - - if (_index < buffers.Length) - { - buffer = buffers[_index]; - buffers[_index++] = null; - allocateBuffer = buffer == null; - } - } - finally - { - if (lockTaken) _lock.Exit(false); - } - - // While we were holding the lock, we grabbed whatever was at the next available index, if - // there was one. If we tried and if we got back null, that means we hadn't yet allocated - // for that slot, in which case we should do so now. - if (allocateBuffer) - { - buffer = new T[_bufferLength]; - - var log = ArrayPoolEventSource.Log; - if (log.IsEnabled()) - { - log.BufferAllocated(buffer.GetHashCode(), _bufferLength, _poolId, Id, - ArrayPoolEventSource.BufferAllocatedReason.Pooled); - } - } - - return buffer; - } - - /// <summary> - /// Attempts to return the buffer to the bucket. If successful, the buffer will be stored - /// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false - /// will be returned. - /// </summary> - internal void Return(T[] array) - { - // Check to see if the buffer is the correct size for this bucket - if (array.Length != _bufferLength) - { - throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); - } - - // While holding the spin lock, if there's room available in the bucket, - // put the buffer into the next available slot. Otherwise, we just drop it. - // The try/finally is necessary to properly handle thread aborts on platforms - // which have them. - bool lockTaken = false; - try - { - _lock.Enter(ref lockTaken); - - if (_index != 0) - { - _buffers[--_index] = array; - } - } - finally - { - if (lockTaken) _lock.Exit(false); - } - } - } - } -} |