DefaultArrayPoolBucket.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. #if NETCORE
  5. using System;
  6. using System.Diagnostics;
  7. using System.Threading;
  8. namespace SharpCompress.Buffers
  9. {
  10. internal sealed partial class DefaultArrayPool<T> : ArrayPool<T>
  11. {
  12. /// <summary>Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd.</summary>
  13. private sealed class Bucket
  14. {
  15. internal readonly int _bufferLength;
  16. private readonly T[][] _buffers;
  17. private readonly int _poolId;
  18. private SpinLock _lock; // do not make this readonly; it's a mutable struct
  19. private int _index;
  20. /// <summary>
  21. /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length.
  22. /// </summary>
  23. internal Bucket(int bufferLength, int numberOfBuffers, int poolId)
  24. {
  25. _lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit
  26. _buffers = new T[numberOfBuffers][];
  27. _bufferLength = bufferLength;
  28. _poolId = poolId;
  29. }
  30. /// <summary>Gets an ID for the bucket to use with events.</summary>
  31. internal int Id => GetHashCode();
  32. /// <summary>Takes an array from the bucket. If the bucket is empty, returns null.</summary>
  33. internal T[] Rent()
  34. {
  35. T[][] buffers = _buffers;
  36. T[] buffer = null;
  37. // While holding the lock, grab whatever is at the next available index and
  38. // update the index. We do as little work as possible while holding the spin
  39. // lock to minimize contention with other threads. The try/finally is
  40. // necessary to properly handle thread aborts on platforms which have them.
  41. bool lockTaken = false, allocateBuffer = false;
  42. try
  43. {
  44. _lock.Enter(ref lockTaken);
  45. if (_index < buffers.Length)
  46. {
  47. buffer = buffers[_index];
  48. buffers[_index++] = null;
  49. allocateBuffer = buffer == null;
  50. }
  51. }
  52. finally
  53. {
  54. if (lockTaken) _lock.Exit(false);
  55. }
  56. // While we were holding the lock, we grabbed whatever was at the next available index, if
  57. // there was one. If we tried and if we got back null, that means we hadn't yet allocated
  58. // for that slot, in which case we should do so now.
  59. if (allocateBuffer)
  60. {
  61. buffer = new T[_bufferLength];
  62. }
  63. return buffer;
  64. }
  65. /// <summary>
  66. /// Attempts to return the buffer to the bucket. If successful, the buffer will be stored
  67. /// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false
  68. /// will be returned.
  69. /// </summary>
  70. internal void Return(T[] array)
  71. {
  72. // Check to see if the buffer is the correct size for this bucket
  73. if (array.Length != _bufferLength)
  74. {
  75. throw new ArgumentException("Buffer not from pool", nameof(array));
  76. }
  77. // While holding the spin lock, if there's room available in the bucket,
  78. // put the buffer into the next available slot. Otherwise, we just drop it.
  79. // The try/finally is necessary to properly handle thread aborts on platforms
  80. // which have them.
  81. bool lockTaken = false;
  82. try
  83. {
  84. _lock.Enter(ref lockTaken);
  85. if (_index != 0)
  86. {
  87. _buffers[--_index] = array;
  88. }
  89. }
  90. finally
  91. {
  92. if (lockTaken) _lock.Exit(false);
  93. }
  94. }
  95. }
  96. }
  97. }
  98. #endif