CyclicDeque.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. //
  2. // CyclicDeque.cs
  3. //
  4. // Author:
  5. // Jérémie "Garuma" Laval <[email protected]>
  6. //
  7. // Copyright (c) 2009 Jérémie "Garuma" Laval
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in
  17. // all copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. // THE SOFTWARE.
  26. #if NET_4_0 || MOBILE
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Threading;
  30. #if INSIDE_MONO_PARALLEL
  31. namespace Mono.Threading.Tasks
  32. #else
  33. namespace System.Threading.Tasks
  34. #endif
  35. {
  36. #if INSIDE_MONO_PARALLEL
  37. public
  38. #endif
  39. enum PopResult {
  40. Succeed,
  41. Empty,
  42. Abort
  43. }
  44. #if INSIDE_MONO_PARALLEL
  45. public
  46. #endif
  47. interface IConcurrentDeque<T>
  48. {
  49. void PushBottom (T obj);
  50. PopResult PopBottom (out T obj);
  51. PopResult PopTop (out T obj);
  52. IEnumerable<T> GetEnumerable ();
  53. }
  54. #if INSIDE_MONO_PARALLEL
  55. public
  56. #endif
  57. class CyclicDeque<T> : IConcurrentDeque<T>
  58. {
  59. const int BaseSize = 11;
  60. long bottom;
  61. long top;
  62. long upperBound;
  63. CircularArray<T> array = new CircularArray<T> (BaseSize);
  64. public void PushBottom (T obj)
  65. {
  66. /* Read is implemented as a simple load operation on 64bits
  67. * so no need to make the distinction ourselves
  68. */
  69. long b = Interlocked.Read (ref bottom);
  70. var a = array;
  71. // Take care of growing
  72. if (b - upperBound >= a.Size - 1) {
  73. upperBound = Interlocked.Read (ref top);
  74. a = a.Grow (b, upperBound);
  75. array = a;
  76. }
  77. // Register the new value
  78. a.segment[b % a.size] = obj;
  79. Interlocked.Increment (ref bottom);
  80. }
  81. public PopResult PopBottom (out T obj)
  82. {
  83. obj = default (T);
  84. long b = Interlocked.Decrement (ref bottom);
  85. var a = array;
  86. long t = Interlocked.Read (ref top);
  87. long size = b - t;
  88. if (size < 0) {
  89. // Set bottom to t
  90. Interlocked.Add (ref bottom, t - b);
  91. return PopResult.Empty;
  92. }
  93. obj = a.segment[b % a.size];
  94. if (size > 0)
  95. return PopResult.Succeed;
  96. Interlocked.Add (ref bottom, t + 1 - b);
  97. if (Interlocked.CompareExchange (ref top, t + 1, t) != t)
  98. return PopResult.Empty;
  99. return PopResult.Succeed;
  100. }
  101. public PopResult PopTop (out T obj)
  102. {
  103. obj = default (T);
  104. long t = Interlocked.Read (ref top);
  105. long b = Interlocked.Read (ref bottom);
  106. if (b - t <= 0)
  107. return PopResult.Empty;
  108. if (Interlocked.CompareExchange (ref top, t + 1, t) != t)
  109. return PopResult.Abort;
  110. var a = array;
  111. obj = a.segment[t % a.size];
  112. return PopResult.Succeed;
  113. }
  114. public IEnumerable<T> GetEnumerable ()
  115. {
  116. var a = array;
  117. return a.GetEnumerable (bottom, ref top);
  118. }
  119. }
  120. internal class CircularArray<T>
  121. {
  122. readonly int baseSize;
  123. public readonly int size;
  124. public readonly T[] segment;
  125. public CircularArray (int baseSize)
  126. {
  127. this.baseSize = baseSize;
  128. this.size = 1 << baseSize;
  129. this.segment = new T[size];
  130. }
  131. public long Size {
  132. get {
  133. return size;
  134. }
  135. }
  136. public T this[long index] {
  137. get {
  138. return segment[index % size];
  139. }
  140. set {
  141. segment[index % size] = value;
  142. }
  143. }
  144. public CircularArray<T> Grow (long bottom, long top)
  145. {
  146. var grow = new CircularArray<T> (baseSize + 1);
  147. for (long i = top; i < bottom; i++) {
  148. grow.segment[i] = segment[i % size];
  149. }
  150. return grow;
  151. }
  152. public IEnumerable<T> GetEnumerable (long bottom, ref long top)
  153. {
  154. long instantTop = top;
  155. T[] slice = new T[bottom - instantTop];
  156. int destIndex = -1;
  157. for (long i = instantTop; i < bottom; i++)
  158. slice[++destIndex] = segment[i % size];
  159. return RealGetEnumerable (slice, bottom, top, instantTop);
  160. }
  161. IEnumerable<T> RealGetEnumerable (T[] slice, long bottom, long realTop, long initialTop)
  162. {
  163. int destIndex = (int)(realTop - initialTop - 1);
  164. for (long i = realTop; i < bottom; ++i)
  165. yield return slice[++destIndex];
  166. }
  167. }
  168. }
  169. #endif