CyclicDeque.cs
Go to the documentation of this file.
1 //
2 // CyclicDeque.cs
3 //
4 // Author:
5 // Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
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 
27 #if NET_4_0
28 
29 using System;
31 using System.Threading;
32 
33 #if INSIDE_MONO_PARALLEL
34 namespace Mono.Threading.Tasks
35 #else
36 namespace System.Threading.Tasks
37 #endif
38 {
39 #if INSIDE_MONO_PARALLEL
40  public
41 #endif
42  class CyclicDeque<T> : IConcurrentDeque<T>
43  {
44  const int BaseSize = 11;
45 
46  long bottom;
47  long top;
48  long upperBound;
49  CircularArray<T> array = new CircularArray<T> (BaseSize);
50 
51  public void PushBottom (T obj)
52  {
53  /* Read is implemented as a simple load operation on 64bits
54  * so no need to make the distinction ourselves
55  */
56  long b = Interlocked.Read (ref bottom);
57  var a = array;
58 
59  // Take care of growing
60  if (b - upperBound >= a.Size - 1) {
61  upperBound = Interlocked.Read (ref top);
62  a = a.Grow (b, upperBound);
63  array = a;
64  }
65 
66  // Register the new value
67  a.segment[b % a.size] = obj;
68  Interlocked.Increment (ref bottom);
69  }
70 
71  public PopResult PopBottom (out T obj)
72  {
73  obj = default (T);
74 
75  long b = Interlocked.Decrement (ref bottom);
76  var a = array;
77  long t = Interlocked.Read (ref top);
78  long size = b - t;
79 
80  if (size < 0) {
81  // Set bottom to t
82  Interlocked.Add (ref bottom, t - b);
83  return PopResult.Empty;
84  }
85 
86  obj = a.segment[b % a.size];
87  if (size > 0)
88  return PopResult.Succeed;
89  Interlocked.Add (ref bottom, t + 1 - b);
90 
91  if (Interlocked.CompareExchange (ref top, t + 1, t) != t)
92  return PopResult.Empty;
93 
94  return PopResult.Succeed;
95  }
96 
97  public bool PeekBottom (out T obj)
98  {
99  obj = default (T);
100 
101  long b = Interlocked.Decrement (ref bottom);
102  var a = array;
103  long t = Interlocked.Read (ref top);
104  long size = b - t;
105 
106  if (size < 0)
107  return false;
108 
109  obj = a.segment[b % a.size];
110  return true;
111  }
112 
113  public PopResult PopTop (out T obj)
114  {
115  obj = default (T);
116 
117  long t = Interlocked.Read (ref top);
118  long b = Interlocked.Read (ref bottom);
119 
120  if (b - t <= 0)
121  return PopResult.Empty;
122 
123  if (Interlocked.CompareExchange (ref top, t + 1, t) != t)
124  return PopResult.Abort;
125 
126  var a = array;
127  obj = a.segment[t % a.size];
128 
129  return PopResult.Succeed;
130  }
131 
132  internal bool PeekTop (out T obj)
133  {
134  obj = default (T);
135 
136  long t = Interlocked.Read (ref top);
137  long b = Interlocked.Read (ref bottom);
138 
139  if (b - t <= 0)
140  return false;
141 
142  var a = array;
143  obj = a.segment[t % a.size];
144 
145  return true;
146  }
147 
148  public IEnumerable<T> GetEnumerable ()
149  {
150  var a = array;
151  return a.GetEnumerable (bottom, ref top);
152  }
153 
154  public bool IsEmpty {
155  get {
156  long t = Interlocked.Read (ref top);
157  long b = Interlocked.Read (ref bottom);
158  return b - t <= 0;
159  }
160  }
161  }
162 
163  internal class CircularArray<T>
164  {
165  readonly int baseSize;
166  public readonly int size;
167  public readonly T[] segment;
168 
169  public CircularArray (int baseSize)
170  {
171  this.baseSize = baseSize;
172  this.size = 1 << baseSize;
173  this.segment = new T[size];
174  }
175 
176  public long Size {
177  get {
178  return size;
179  }
180  }
181 
182  public T this[long index] {
183  get {
184  return segment[index % size];
185  }
186  set {
187  segment[index % size] = value;
188  }
189  }
190 
191  public CircularArray<T> Grow (long bottom, long top)
192  {
193  var grow = new CircularArray<T> (baseSize + 1);
194 
195  for (long i = top; i < bottom; i++) {
196  grow.segment[i] = segment[i % size];
197  }
198 
199  return grow;
200  }
201 
202  public IEnumerable<T> GetEnumerable (long bottom, ref long top)
203  {
204  long instantTop = top;
205  T[] slice = new T[bottom - instantTop];
206  int destIndex = -1;
207  for (long i = instantTop; i < bottom; i++)
208  slice[++destIndex] = segment[i % size];
209 
210  return RealGetEnumerable (slice, bottom, top, instantTop);
211  }
212 
213  IEnumerable<T> RealGetEnumerable (T[] slice, long bottom, long realTop, long initialTop)
214  {
215  int destIndex = (int)(realTop - initialTop - 1);
216  for (long i = realTop; i < bottom; ++i)
217  yield return slice[++destIndex];
218  }
219  }
220 }
221 #endif