Merge pull request #980 from StephenMcConnel/bug-18638
[mono.git] / mcs / class / corlib / System.Threading.Tasks / CyclicDeque.cs
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;
30 using System.Collections.Generic;
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                 int bottom;
47                 int top;
48                 int upperBound;
49                 CircularArray<T> array = new CircularArray<T> (BaseSize);
50                 
51                 public void PushBottom (T obj)
52                 {
53                         int b = bottom;
54                         var a = array;
55                         
56                         // Take care of growing
57                         var size = b - top - upperBound;
58                         if (size >= a.Size) {
59                                 upperBound = top;
60                                 a = a.Grow (b, upperBound);
61                                 array = a;
62                         }
63                         
64                         // Register the new value
65                         a.segment[b % a.size] = obj;
66                         Interlocked.Increment (ref bottom);
67                 }
68                 
69                 public PopResult PopBottom (out T obj)
70                 {
71                         obj = default (T);
72                         
73                         int b = Interlocked.Decrement (ref bottom);
74                         var a = array;
75                         int t = top;
76                         int size = b - t;
77                         
78                         if (size < 0) {
79                                 // Set bottom to t
80                                 Interlocked.Add (ref bottom, t - b);
81                                 return PopResult.Empty;
82                         }
83                         
84                         obj = a.segment[b % a.size];
85                         if (size > 0)
86                                 return PopResult.Succeed;
87                         Interlocked.Add (ref bottom, t + 1 - b);
88                         
89                         if (Interlocked.CompareExchange (ref top, t + 1, t) != t)
90                                 return PopResult.Empty;
91                         
92                         return PopResult.Succeed;
93                 }
94
95                 public bool PeekBottom (out T obj)
96                 {
97                         obj = default (T);
98
99                         int b = Interlocked.Decrement (ref bottom);
100                         var a = array;
101                         int t = top;
102                         int size = b - t;
103
104                         if (size < 0)
105                                 return false;
106
107                         obj = a.segment[b % a.size];
108                         return true;
109                 }
110                 
111                 public PopResult PopTop (out T obj)
112                 {
113                         obj = default (T);
114                         
115                         int t = top;
116                         int b = bottom;
117                         
118                         if (b - t <= 0)
119                                 return PopResult.Empty;
120                         
121                         if (Interlocked.CompareExchange (ref top, t + 1, t) != t)
122                                 return PopResult.Abort;
123                         
124                         var a = array;
125                         obj = a.segment[t % a.size];
126                         
127                         return PopResult.Succeed;
128                 }
129
130                 internal bool PeekTop (out T obj)
131                 {
132                         obj = default (T);
133
134                         int t = top;
135                         int b = bottom;
136
137                         if (b - t <= 0)
138                                 return false;
139
140                         var a = array;
141                         obj = a.segment[t % a.size];
142
143                         return true;
144                 }
145                 
146                 public IEnumerable<T> GetEnumerable ()
147                 {
148                         var a = array;
149                         return a.GetEnumerable (bottom, ref top);
150                 }
151
152                 public bool IsEmpty {
153                         get {
154                                 int t = top;
155                                 int b = bottom;
156                                 return b - t <= 0;
157                         }
158                 }
159         }
160         
161         internal class CircularArray<T>
162         {
163                 readonly int baseSize;
164                 public readonly int size;
165                 public readonly T[] segment;
166                 
167                 public CircularArray (int baseSize)
168                 {
169                         this.baseSize = baseSize;
170                         this.size = 1 << baseSize;
171                         this.segment = new T[size];
172                 }
173                 
174                 public int Size {
175                         get {
176                                 return size;
177                         }
178                 }
179                 
180                 public T this[int index] {
181                         get {
182                                 return segment[index % size];
183                         }
184                         set {
185                                 segment[index % size] = value;
186                         }
187                 }
188                 
189                 public CircularArray<T> Grow (int bottom, int top)
190                 {
191                         var grow = new CircularArray<T> (baseSize + 1);
192                         
193                         for (int i = top; i < bottom; i++) {
194                                 grow.segment[i] = segment[i % size];
195                         }
196                         
197                         return grow;
198                 }
199                 
200                 public IEnumerable<T> GetEnumerable (int bottom, ref int top)
201                 {
202                         int instantTop = top;
203                         T[] slice = new T[bottom - instantTop];
204                         int destIndex = -1;
205                         for (int i = instantTop; i < bottom; i++)
206                                 slice[++destIndex] = segment[i % size];
207
208                         return RealGetEnumerable (slice, bottom, top, instantTop);
209                 }
210
211                 IEnumerable<T> RealGetEnumerable (T[] slice, int bottom, int realTop, int initialTop)
212                 {
213                         int destIndex = (int)(realTop - initialTop - 1);
214                         for (int i = realTop; i < bottom; ++i)
215                                 yield return slice[++destIndex];
216                 }
217         }
218 }
219 #endif