// 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. #if NETCORE using System; using System.Diagnostics; using System.Threading; namespace SharpCompress.Buffers { internal sealed partial class DefaultArrayPool : ArrayPool { /// Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd. 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; /// /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length. /// 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; } /// Gets an ID for the bucket to use with events. internal int Id => GetHashCode(); /// Takes an array from the bucket. If the bucket is empty, returns null. 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]; } return buffer; } /// /// 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. /// 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("Buffer not from pool", 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); } } } } } #endif