Merge branch 'master' of github.com:mono/mono
[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 hintThreshold = 20;
45                 
46                 int count;
47                 
48                 // We only use the add hints when number of slot is above hintThreshold
49                 // so to not waste memory space and the CAS overhead
50                 ConcurrentQueue<int> addHints = new ConcurrentQueue<int> ();
51                 
52                 ConcurrentDictionary<int, CyclicDeque<T>> container = new ConcurrentDictionary<int, CyclicDeque<T>> ();
53                 
54                 public ConcurrentBag ()
55                 {
56                 }
57                 
58                 public ConcurrentBag (IEnumerable<T> enumerable) : this ()
59                 {
60                         foreach (T item in enumerable)
61                                 Add (item);
62                 }
63                 
64                 public void Add (T item)
65                 {
66                         int index;
67                         CyclicDeque<T> bag = GetBag (out index);
68                         bag.PushBottom (item);
69                         
70                         // Cache operation ?
71                         if (container.Count > hintThreshold)
72                                 addHints.Enqueue (index);
73
74                         Interlocked.Increment (ref count);
75                 }
76
77                 bool IProducerConsumerCollection<T>.TryAdd (T element)
78                 {
79                         Add (element);
80                         return true;
81                 }
82                 
83                 public bool TryTake (out T item)
84                 {
85                         item = default (T);
86
87                         if (count == 0)
88                                 return false;
89
90                         int hintIndex;
91                         CyclicDeque<T> bag = GetBag (out hintIndex);
92                         bool hintEnabled = container.Count > hintThreshold;
93                         
94                         if (bag == null || bag.PopBottom (out item) != PopResult.Succeed) {
95                                 foreach (var other in container) {
96                                         // Try to retrieve something based on a hint
97                                         bool result = hintEnabled && addHints.TryDequeue (out hintIndex) && container[hintIndex].PopTop (out item) == PopResult.Succeed;
98
99                                         // We fall back to testing our slot
100                                         if (!result && other.Value != bag)
101                                                 result = other.Value.PopTop (out item) == PopResult.Succeed;
102                                         
103                                         // If we found something, stop
104                                         if (result) {
105                                                 Interlocked.Decrement (ref count);
106                                                 return true;
107                                         }
108                                 }
109                         } else {
110                                 Interlocked.Decrement (ref count);
111                                 return true;
112                         }
113                         
114                         return false;
115                 }
116                 
117                 public int Count {
118                         get {
119                                 return count;
120                         }
121                 }
122                 
123                 public bool IsEmpty {
124                         get {
125                                 return count == 0;
126                         }
127                 }
128                 
129                 object System.Collections.ICollection.SyncRoot  {
130                         get {
131                                 return this;
132                         }
133                 }
134                 
135                 bool System.Collections.ICollection.IsSynchronized  {
136                         get {
137                                 return true;
138                         }
139                 }
140                 
141                 IEnumerator IEnumerable.GetEnumerator ()
142                 {
143                         return GetEnumeratorInternal ();
144                 }
145                 
146                 public IEnumerator<T> GetEnumerator ()
147                 {
148                         return GetEnumeratorInternal ();
149                 }
150                 
151                 IEnumerator<T> GetEnumeratorInternal ()
152                 {
153                         foreach (var bag in container)
154                                 foreach (T item in bag.Value.GetEnumerable ())
155                                         yield return item;
156                 }
157                 
158                 void System.Collections.ICollection.CopyTo (Array array, int index)
159                 {
160                         T[] a = array as T[];
161                         if (a == null)
162                                 return;
163                         
164                         CopyTo (a, index);
165                 }
166                 
167                 public void CopyTo (T[] array, int index)
168                 {
169                         int c = count;
170                         if (array.Length < c + index)
171                                 throw new InvalidOperationException ("Array is not big enough");
172                         
173                         CopyTo (array, index, c);
174                 }
175                 
176                 void CopyTo (T[] array, int index, int num)
177                 {
178                         int i = index;
179                         
180                         foreach (T item in this) {
181                                 if (i >= num)
182                                         break;
183                                 
184                                 array[i++] = item;
185                         }
186                 }
187                 
188                 public T[] ToArray ()
189                 {
190                         int c = count;
191                         T[] temp = new T[c];
192                         
193                         CopyTo (temp, 0, c);
194                         
195                         return temp;
196                 }
197                         
198                 int GetIndex ()
199                 {
200                         return Thread.CurrentThread.ManagedThreadId;
201                 }
202                                 
203                 CyclicDeque<T> GetBag (out int index)
204                 {
205                         index = GetIndex ();
206                         CyclicDeque<T> value;
207                         if (container.TryGetValue (index, out value))
208                                 return value;
209
210                         return container.GetOrAdd (index, new CyclicDeque<T> ());
211                 }
212         }
213 }
214 #endif