ArrayPool.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
  2. using System;
  3. using System.Threading;
  4. namespace Cysharp.Threading.Tasks.Internal
  5. {
  6. // Same interface as System.Buffers.ArrayPool<T> but only provides Shared.
  7. internal sealed class ArrayPool<T>
  8. {
  9. // Same size as System.Buffers.DefaultArrayPool<T>
  10. const int DefaultMaxNumberOfArraysPerBucket = 50;
  11. static readonly T[] EmptyArray = new T[0];
  12. public static readonly ArrayPool<T> Shared = new ArrayPool<T>();
  13. readonly MinimumQueue<T[]>[] buckets;
  14. readonly SpinLock[] locks;
  15. ArrayPool()
  16. {
  17. // see: GetQueueIndex
  18. buckets = new MinimumQueue<T[]>[18];
  19. locks = new SpinLock[18];
  20. for (int i = 0; i < buckets.Length; i++)
  21. {
  22. buckets[i] = new MinimumQueue<T[]>(4);
  23. locks[i] = new SpinLock(false);
  24. }
  25. }
  26. public T[] Rent(int minimumLength)
  27. {
  28. if (minimumLength < 0)
  29. {
  30. throw new ArgumentOutOfRangeException("minimumLength");
  31. }
  32. else if (minimumLength == 0)
  33. {
  34. return EmptyArray;
  35. }
  36. var size = CalculateSize(minimumLength);
  37. var index = GetQueueIndex(size);
  38. if (index != -1)
  39. {
  40. var q = buckets[index];
  41. var lockTaken = false;
  42. try
  43. {
  44. locks[index].Enter(ref lockTaken);
  45. if (q.Count != 0)
  46. {
  47. return q.Dequeue();
  48. }
  49. }
  50. finally
  51. {
  52. if (lockTaken) locks[index].Exit(false);
  53. }
  54. }
  55. return new T[size];
  56. }
  57. public void Return(T[] array, bool clearArray = false)
  58. {
  59. if (array == null || array.Length == 0)
  60. {
  61. return;
  62. }
  63. var index = GetQueueIndex(array.Length);
  64. if (index != -1)
  65. {
  66. if (clearArray)
  67. {
  68. Array.Clear(array, 0, array.Length);
  69. }
  70. var q = buckets[index];
  71. var lockTaken = false;
  72. try
  73. {
  74. locks[index].Enter(ref lockTaken);
  75. if (q.Count > DefaultMaxNumberOfArraysPerBucket)
  76. {
  77. return;
  78. }
  79. q.Enqueue(array);
  80. }
  81. finally
  82. {
  83. if (lockTaken) locks[index].Exit(false);
  84. }
  85. }
  86. }
  87. static int CalculateSize(int size)
  88. {
  89. size--;
  90. size |= size >> 1;
  91. size |= size >> 2;
  92. size |= size >> 4;
  93. size |= size >> 8;
  94. size |= size >> 16;
  95. size += 1;
  96. if (size < 8)
  97. {
  98. size = 8;
  99. }
  100. return size;
  101. }
  102. static int GetQueueIndex(int size)
  103. {
  104. switch (size)
  105. {
  106. case 8: return 0;
  107. case 16: return 1;
  108. case 32: return 2;
  109. case 64: return 3;
  110. case 128: return 4;
  111. case 256: return 5;
  112. case 512: return 6;
  113. case 1024: return 7;
  114. case 2048: return 8;
  115. case 4096: return 9;
  116. case 8192: return 10;
  117. case 16384: return 11;
  118. case 32768: return 12;
  119. case 65536: return 13;
  120. case 131072: return 14;
  121. case 262144: return 15;
  122. case 524288: return 16;
  123. case 1048576: return 17; // max array length
  124. default:
  125. return -1;
  126. }
  127. }
  128. }
  129. }