18e0f58ba448bf2aafdb92529afa7670e391c890
[mono.git] / mcs / class / corlib / System.Collections.Concurrent / ConcurrentQueue.cs
1 // ConcurrentQueue.cs
2 //
3 // Copyright (c) 2008 Jérémie "Garuma" Laval
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 //
24
25 #if NET_4_0 || MOBILE
26
27 using System;
28 using System.Threading;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Runtime.Serialization;
32
33 namespace System.Collections.Concurrent
34 {
35
36         [System.Diagnostics.DebuggerDisplay ("Count={Count}")]
37         [System.Diagnostics.DebuggerTypeProxy (typeof (CollectionDebuggerView<>))]
38         public class ConcurrentQueue<T> : IProducerConsumerCollection<T>, IEnumerable<T>, ICollection,
39                                           IEnumerable
40         {
41                 class Node
42                 {
43                         public T Value;
44                         public Node Next;
45                 }
46                 
47                 Node head = new Node ();
48                 Node tail;
49                 int count;
50
51                 class NodeObjectPool : ObjectPool<Node> {
52                         protected override Node Creator ()
53                         {
54                                 return new Node ();
55                         }
56                 }
57                 static readonly NodeObjectPool pool = new NodeObjectPool ();
58
59                 static Node ZeroOut (Node node)
60                 {
61                         node.Value = default(T);
62                         node.Next = null;
63                         return node;
64                 }
65
66                 public ConcurrentQueue ()
67                 {
68                         tail = head;
69                 }
70                 
71                 public ConcurrentQueue (IEnumerable<T> collection): this()
72                 {
73                         foreach (T item in collection)
74                                 Enqueue (item);
75                 }
76                 
77                 public void Enqueue (T item)
78                 {
79                         Node node = pool.Take ();
80                         node.Value = item;
81                         
82                         Node oldTail = null;
83                         Node oldNext = null;
84                         
85                         bool update = false;
86                         while (!update) {
87                                 oldTail = tail;
88                                 oldNext = oldTail.Next;
89                                 
90                                 // Did tail was already updated ?
91                                 if (tail == oldTail) {
92                                         if (oldNext == null) {
93                                                 // The place is for us
94                                                 update = Interlocked.CompareExchange (ref tail.Next, node, null) == null;
95                                         } else {
96                                                 // another Thread already used the place so give him a hand by putting tail where it should be
97                                                 Interlocked.CompareExchange (ref tail, oldNext, oldTail);
98                                         }
99                                 }
100                         }
101                         // At this point we added correctly our node, now we have to update tail. If it fails then it will be done by another thread
102                         Interlocked.CompareExchange (ref tail, node, oldTail);
103
104                         Interlocked.Increment (ref count);
105                 }
106                 
107                 bool IProducerConsumerCollection<T>.TryAdd (T item)
108                 {
109                         Enqueue (item);
110                         return true;
111                 }
112
113                 public bool TryDequeue (out T result)
114                 {
115                         result = default (T);
116                         bool advanced = false;
117
118                         while (!advanced) {
119                                 Node oldHead = head;
120                                 Node oldTail = tail;
121                                 Node oldNext = oldHead.Next;
122                                 
123                                 if (oldHead == head) {
124                                         // Empty case ?
125                                         if (oldHead == oldTail) {       
126                                                 // This should be false then
127                                                 if (oldNext != null) {
128                                                         // If not then the linked list is mal formed, update tail
129                                                         Interlocked.CompareExchange (ref tail, oldNext, oldTail);
130                                                 }
131                                                 result = default (T);
132                                                 return false;
133                                         } else {
134                                                 result = oldNext.Value;
135                                                 advanced = Interlocked.CompareExchange (ref head, oldNext, oldHead) == oldHead;
136                                                 if (advanced)
137                                                         pool.Release (ZeroOut (oldHead));
138                                         }
139                                 }
140                         }
141
142                         Interlocked.Decrement (ref count);
143
144                         return true;
145                 }
146                 
147                 public bool TryPeek (out T result)
148                 {
149                         if (IsEmpty) {
150                                 result = default (T);
151                                 return false;
152                         }
153                         
154                         Node first = head.Next;
155                         result = first.Value;
156                         return true;
157                 }
158                 
159                 internal void Clear ()
160                 {
161                         count = 0;
162                         tail = head = new Node ();
163                 }
164                 
165                 IEnumerator IEnumerable.GetEnumerator ()
166                 {
167                         return (IEnumerator)InternalGetEnumerator ();
168                 }
169                 
170                 public IEnumerator<T> GetEnumerator ()
171                 {
172                         return InternalGetEnumerator ();
173                 }
174                 
175                 IEnumerator<T> InternalGetEnumerator ()
176                 {
177                         Node my_head = head;
178                         while ((my_head = my_head.Next) != null) {
179                                 yield return my_head.Value;
180                         }
181                 }
182                 
183                 void ICollection.CopyTo (Array array, int index)
184                 {
185                         T[] dest = array as T[];
186                         if (dest == null)
187                                 return;
188                         CopyTo (dest, index);
189                 }
190                 
191                 public void CopyTo (T[] array, int index)
192                 {
193                         IEnumerator<T> e = InternalGetEnumerator ();
194                         int i = index;
195                         while (e.MoveNext ()) {
196                                 array [i++] = e.Current;
197                         }
198                 }
199                 
200                 public T[] ToArray ()
201                 {
202                         T[] dest = new T [count];
203                         CopyTo (dest, 0);
204                         return dest;
205                 }
206                 
207                 bool ICollection.IsSynchronized {
208                         get { return true; }
209                 }
210
211                 bool IProducerConsumerCollection<T>.TryTake (out T item)
212                 {
213                         return TryDequeue (out item);
214                 }
215                 
216                 object syncRoot = new object();
217                 object ICollection.SyncRoot {
218                         get { return syncRoot; }
219                 }
220                 
221                 public int Count {
222                         get {
223                                 return count;
224                         }
225                 }
226                 
227                 public bool IsEmpty {
228                         get {
229                                 return count == 0;
230                         }
231                 }
232         }
233 }
234 #endif