New tests.
[mono.git] / mcs / class / System / System.Collections.Concurrent / ConcurrentBag.cs
1 // 
2 // ConcurrentBag.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 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Diagnostics;
32 using System.Runtime.InteropServices;
33
34 using System.Threading;
35 using System.Threading.Tasks;
36
37 namespace System.Collections.Concurrent
38 {
39         [ComVisible (false)]
40         [DebuggerDisplay ("Count={Count}")]
41         [DebuggerTypeProxy (typeof (CollectionDebuggerView<>))]
42         public class ConcurrentBag<T> : IProducerConsumerCollection<T>, IEnumerable<T>, IEnumerable
43         {
44                 const int multiplier = 2;
45                 int size = Environment.ProcessorCount + 1;
46                 int count;
47                 
48                 CyclicDeque<T>[] container;
49                 
50                 object syncLock = new object ();
51                 
52                 public ConcurrentBag ()
53                 {
54                         container = new CyclicDeque<T>[size];
55                         for (int i = 0; i < container.Length; i++)
56                                 container[i] = new CyclicDeque<T> ();
57                 }
58                 
59                 public ConcurrentBag (IEnumerable<T> enumerable) : this ()
60                 {
61                         foreach (T item in enumerable)
62                                 Add (item);
63                 }
64                 
65                 public bool TryAdd (T item)
66                 {
67                         Add (item);
68                         
69                         return true;
70                 }
71                 
72                 public void Add (T item)
73                 {
74                         Interlocked.Increment (ref count);
75                         GrowIfNecessary ();
76                         
77                         CyclicDeque<T> bag = GetBag ();
78                         bag.PushBottom (item);
79                 }
80                 
81                 public bool TryTake (out T item)
82                 {
83                         item = default (T);
84                         CyclicDeque<T> bag = GetBag ();
85                         
86                         if (bag == null || bag.PopBottom (out item) != PopResult.Succeed) {
87                                 for (int i = 0; i < container.Length; i++) {
88                                         if (container[i].PopTop (out item) == PopResult.Succeed) {
89                                                 Interlocked.Decrement (ref count);
90                                                 return true;
91                                         }
92                                 }
93                         } else {
94                                 Interlocked.Decrement (ref count);
95                                 return true;
96                         }
97                         
98                         return false;
99                 }
100                 
101                 public int Count {
102                         get {
103                                 return count;
104                         }
105                 }
106                 
107                 public bool IsEmpty {
108                         get {
109                                 return count == 0;
110                         }
111                 }
112                 
113                 object System.Collections.ICollection.SyncRoot  {
114                         get {
115                                 return this;
116                         }
117                 }
118                 
119                 bool System.Collections.ICollection.IsSynchronized  {
120                         get {
121                                 return true;
122                         }
123                 }
124                 
125                 IEnumerator IEnumerable.GetEnumerator ()
126                 {
127                         return GetEnumeratorInternal ();
128                 }
129                 
130                 IEnumerator<T> IEnumerable<T>.GetEnumerator ()
131                 {
132                         return GetEnumeratorInternal ();
133                 }
134                 
135                 IEnumerator<T> GetEnumeratorInternal ()
136                 {
137                         for (int i = 0; i < size; i++) {
138                                 CyclicDeque<T> bag = container[i];
139                                 foreach (T item in bag.GetEnumerable ()) {
140                                         yield return item;
141                                 }
142                         }
143                 }
144                 
145                 void System.Collections.ICollection.CopyTo (Array array, int index)
146                 {
147                         T[] a = array as T[];
148                         if (a == null)
149                                 return;
150                         
151                         CopyTo (a, index);
152                 }
153                 
154                 public void CopyTo (T[] array, int index)
155                 {
156                         int c = count;
157                         if (array.Length < c + index)
158                                 throw new InvalidOperationException ("Array is not big enough");
159                         
160                         CopyTo (array, index, c);
161                 }
162                 
163                 void CopyTo (T[] array, int index, int num)
164                 {
165                         int i = index;
166                         
167                         foreach (T item in this) {
168                                 if (i >= num)
169                                         break;
170                                 
171                                 array[i++] = item;
172                         }
173                 }
174                 
175                 public T[] ToArray ()
176                 {
177                         int c = count;
178                         T[] temp = new T[c];
179                         
180                         CopyTo (temp, 0, c);
181                         
182                         return temp;
183                 }
184                         
185                 int GetIndex ()
186                 {
187                         return Thread.CurrentThread.ManagedThreadId - 1;
188                 }
189                 
190                 void GrowIfNecessary ()
191                 {
192                         int index = GetIndex ();
193                         int currentSize = size;
194                         
195                         while (index > currentSize - 1) {
196                                 currentSize = size;
197                                 Grow (currentSize);
198                         }
199                 }
200                 
201                 CyclicDeque<T> GetBag ()
202                 {                       
203                         int i = GetIndex ();
204                         
205                         return i < container.Length ? container[i] : null;
206                 }
207                 
208                 void Grow (int referenceSize)
209                 {
210                         lock (syncLock) {
211                                 if (referenceSize != size)
212                                         return;
213                                 
214                                 CyclicDeque<T>[] slice = new CyclicDeque<T>[size * multiplier];
215                                 int i = 0;
216                                 for (i = 0; i < container.Length; i++)
217                                         slice[i] = container[i];
218                                 for (; i < slice.Length; i++)
219                                         slice[i] = new CyclicDeque<T> ();
220                                 
221                                 container = slice;
222                                 size = slice.Length;
223                         }
224                 }
225         }
226 }
227 #endif