public override void Return(T[] array, bool clearArray = false) { if (array == null) { throw new ArgumentNullException(nameof(array)); } // Determine with what bucket this array length is associated int bucketIndex = Utilities.SelectBucketIndex(array.Length); // If we can tell that the buffer was allocated (or empty), drop it. Otherwise, check if we have space in the pool. if (bucketIndex < _buckets.Length) { // Clear the array if the user requests. if (clearArray) { Array.Clear(array, 0, array.Length); } // Check to see if the buffer is the correct size for this bucket if (array.Length != _bucketArraySizes[bucketIndex]) { throw new ArgumentException(nameof(array)); } // Write through the TLS bucket. If there weren't any buckets, create them // and store this array into it. If there were, store this into it, and // if there was a previous one there, push that to the global stack. This // helps to keep LIFO access such that the most recently pushed stack will // be in TLS and the first to be popped next. T[]?[]? tlsBuckets = t_tlsBuckets; if (tlsBuckets == null) { t_tlsBuckets = tlsBuckets = new T[NumBuckets][]; tlsBuckets[bucketIndex] = array; if (s_trimBuffers) { Debug.Assert(s_allTlsBuckets != null, "Should be non-null iff s_trimBuffers is true"); s_allTlsBuckets.Add(tlsBuckets, null); if (Interlocked.Exchange(ref _callbackCreated, 1) != 1) { //Gen2GcCallback.Register(Gen2GcCallbackFunc, this); } } } else { T[]? prev = tlsBuckets[bucketIndex]; tlsBuckets[bucketIndex] = array; if (prev != null) { PerCoreLockedStacks stackBucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex); stackBucket.TryPush(prev); } } } }
public override void Return(T[] array, bool clearArray = false) { if (array == null) { throw new ArgumentNullException(nameof(array)); } // Determine with what bucket this array length is associated int bucketIndex = Utilities.SelectBucketIndex(array.Length); // If we can tell that the buffer was allocated (or empty), drop it. Otherwise, check if we have space in the pool. if (bucketIndex < _buckets.Length) { // Clear the array if the user requests. if (clearArray) { Array.Clear(array, 0, array.Length); } // Check to see if the buffer is the correct size for this bucket if (array.Length != _bucketArraySizes[bucketIndex]) { throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); } // Write through the TLS bucket. If there weren't any buckets, create them // and store this array into it. If there were, store this into it, and // if there was a previous one there, push that to the global stack. This // helps to keep LIFO access such that the most recently pushed stack will // be in TLS and the first to be popped next. T[][] tlsBuckets = t_tlsBuckets; if (tlsBuckets == null) { t_tlsBuckets = tlsBuckets = new T[NumBuckets][]; tlsBuckets[bucketIndex] = array; } else { T[] prev = tlsBuckets[bucketIndex]; tlsBuckets[bucketIndex] = array; if (prev != null) { PerCoreLockedStacks bucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex); bucket.TryPush(prev); } } } // Log that the buffer was returned ArrayPoolEventSource log = ArrayPoolEventSource.Log; if (log.IsEnabled()) { log.BufferReturned(array.GetHashCode(), array.Length, Id); } }
public override void Return(T[] array, bool clearArray = false) { if (array is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); } // Determine with what bucket this array length is associated int bucketIndex = Utilities.SelectBucketIndex(array.Length); // Make sure our TLS buckets are initialized. Technically we could avoid doing // this if the array being returned is erroneous or too large for the pool, but the // former condition is an error we don't need to optimize for, and the latter is incredibly // rare, given a max size of 1B elements. ThreadLocalArray[] tlsBuckets = t_tlsBuckets ?? InitializeTlsBucketsAndTrimming(); bool haveBucket = false; bool returned = true; if ((uint)bucketIndex < (uint)tlsBuckets.Length) { haveBucket = true; // Clear the array if the user requested it. if (clearArray) { Array.Clear(array); } // Check to see if the buffer is the correct size for this bucket. if (array.Length != Utilities.GetMaxSizeForBucket(bucketIndex)) { throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array)); } // Store the array into the TLS bucket. If there's already an array in it, // push that array down into the per-core stacks, preferring to keep the latest // one in TLS for better locality. ref ThreadLocalArray tla = ref tlsBuckets[bucketIndex]; T[]? prev = tla.Array; tla = new ThreadLocalArray(array); if (prev is not null) { PerCoreLockedStacks stackBucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex); returned = stackBucket.TryPush(prev); } }
/// <summary>Allocate a new PerCoreLockedStacks and try to store it into the <see cref="_buckets"/> array.</summary> private PerCoreLockedStacks CreatePerCoreLockedStacks(int bucketIndex) { var inst = new PerCoreLockedStacks(); return(Interlocked.CompareExchange(ref _buckets[bucketIndex], inst, null) ?? inst); }
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 to log the empty array. Our pool is effectively infinite // and we'll never allocate for rents and never store for returns. return(Array.Empty <T>()); } ArrayPoolEventSource log = ArrayPoolEventSource.Log; T[] buffer; // Get the bucket number for the array length int bucketIndex = Utilities.SelectBucketIndex(minimumLength); // If the array could come from a bucket... if (bucketIndex < _buckets.Length) { // First try to get it from TLS if possible. T[][] tlsBuckets = t_tlsBuckets; if (tlsBuckets != null) { buffer = tlsBuckets[bucketIndex]; if (buffer != null) { tlsBuckets[bucketIndex] = null; if (log.IsEnabled()) { log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); } return(buffer); } } // We couldn't get a buffer from TLS, so try the global stack. PerCoreLockedStacks b = _buckets[bucketIndex]; if (b != null) { buffer = b.TryPop(); if (buffer != null) { if (log.IsEnabled()) { log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex); } return(buffer); } } // No buffer available. Allocate a new buffer with a size corresponding to the appropriate bucket. buffer = new T[_bucketArraySizes[bucketIndex]]; } 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, bucketIndex >= _buckets.Length ? ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted); } return(buffer); }