2009-12-02 Jb Evain <jbevain@novell.com>
[mcs.git] / class / corlib / System.Collections.Concurrent / ConcurrentBag.cs
blob6eb16842b6aca45f3a1fe421a26d3154869e9334
1 #if NET_4_0
2 //
3 // ConcurrentBag.cs
4 //
5 // Author:
6 // Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
7 //
8 // Copyright (c) 2009 Jérémie "Garuma" Laval
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
32 using System.Threading;
33 using System.Threading.Tasks;
35 namespace System.Collections.Concurrent
38 public class ConcurrentBag<T> : IProducerConsumerCollection<T>, IEnumerable<T>, IEnumerable
40 int size = Environment.ProcessorCount + 1;
41 int multiplier = 2;
42 int count;
44 CyclicDeque<T>[] container;
46 object syncLock = new object ();
48 public ConcurrentBag ()
50 container = new CyclicDeque<T>[size];
51 for (int i = 0; i < container.Length; i++)
52 container[i] = new CyclicDeque<T> ();
55 public ConcurrentBag (IEnumerable<T> enumerable) : this ()
57 foreach (T item in enumerable)
58 Add (item);
61 public bool TryAdd (T item)
63 Add (item);
65 return true;
68 public void Add (T item)
70 Interlocked.Increment (ref count);
71 GrowIfNecessary ();
73 CyclicDeque<T> bag = GetBag ();
74 bag.PushBottom (item);
77 public bool TryTake (out T item)
79 item = default (T);
80 CyclicDeque<T> bag = GetBag ();
82 if (bag == null || bag.PopBottom (out item) != PopResult.Succeed) {
83 for (int i = 0; i < container.Length; i++) {
84 if (container[i].PopTop (out item) == PopResult.Succeed) {
85 Interlocked.Decrement (ref count);
86 return true;
89 } else {
90 Interlocked.Decrement (ref count);
91 return true;
94 return false;
97 public int Count {
98 get {
99 return count;
103 public bool IsEmpty {
104 get {
105 return count == 0;
109 public object SyncRoot {
110 get {
111 return this;
115 public bool IsSynchronized {
116 get {
117 return true;
121 IEnumerator IEnumerable.GetEnumerator ()
123 return GetEnumeratorInternal ();
126 IEnumerator<T> IEnumerable<T>.GetEnumerator ()
128 return GetEnumeratorInternal ();
131 IEnumerator<T> GetEnumeratorInternal ()
133 for (int i = 0; i < size; i++) {
134 CyclicDeque<T> bag = container[i];
135 foreach (T item in bag.GetEnumerable ()) {
136 yield return item;
141 public void CopyTo (Array array, int index)
143 T[] a = array as T[];
144 if (a == null)
145 return;
147 CopyTo (a, index);
150 public void CopyTo (T[] array, int index)
152 int c = count;
153 if (array.Length < c + index)
154 throw new InvalidOperationException ("Array is not big enough");
156 CopyTo (array, index, c);
159 void CopyTo (T[] array, int index, int num)
161 int i = index;
163 foreach (T item in this) {
164 if (i >= num)
165 break;
167 array[i++] = item;
171 public T[] ToArray ()
173 int c = count;
174 T[] temp = new T[c];
176 CopyTo (temp, 0, c);
178 return temp;
181 int GetIndex ()
183 return Thread.CurrentThread.ManagedThreadId - 1;
186 void GrowIfNecessary ()
188 int index = GetIndex ();
189 int currentSize = size;
191 while (index > currentSize - 1) {
192 currentSize = size;
193 Grow (currentSize);
197 CyclicDeque<T> GetBag ()
199 int i = GetIndex ();
201 return i < container.Length ? container[i] : null;
204 void Grow (int referenceSize)
206 lock (syncLock) {
207 if (referenceSize != size)
208 return;
210 CyclicDeque<T>[] slice = new CyclicDeque<T>[size * multiplier];
211 int i = 0;
212 for (i = 0; i < container.Length; i++)
213 slice[i] = container[i];
214 for (; i < slice.Length; i++)
215 slice[i] = new CyclicDeque<T> ();
217 container = slice;
218 size = slice.Length;
223 #endif