Merge pull request #1529 from alistair/xmlreader_read_to_next_sibling_bug
[mono.git] / mcs / class / System.Web / System.Collections.Concurrent / SplitOrderedList.cs
1 // SplitOrderedList.cs
2 //
3 // Copyright (c) 2010 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
26 using System;
27 using System.Threading;
28 using System.Collections;
29 using System.Collections.Generic;
30 using System.Runtime.Serialization;
31
32 namespace System.Collections.Concurrent
33 {
34         internal class SplitOrderedList<TKey, T>
35         {
36                 class Node
37                 {
38                         public bool Marked;
39                         public ulong Key;
40                         public TKey SubKey;
41                         public T Data;
42                         public Node Next;
43
44                         public Node Init (ulong key, TKey subKey, T data)
45                         {
46                                 this.Key = key;
47                                 this.SubKey = subKey;
48                                 this.Data = data;
49
50                                 this.Marked = false;
51                                 this.Next = null;
52
53                                 return this;
54                         }
55
56                         // Used to create dummy node
57                         public Node Init (ulong key)
58                         {
59                                 this.Key = key;
60                                 this.Data = default (T);
61
62                                 this.Next = null;
63                                 this.Marked = false;
64                                 this.SubKey = default (TKey);
65
66                                 return this;
67                         }
68
69                         // Used to create marked node
70                         public Node Init (Node wrapped)
71                         {
72                                 this.Marked = true;
73                                 this.Next = wrapped;
74
75                                 this.Key = 0;
76                                 this.Data = default (T);
77                                 this.SubKey = default (TKey);
78
79                                 return this;
80                         }
81                 }
82
83                 const int MaxLoad = 5;
84                 const uint BucketSize = 512;
85
86                 Node head;
87                 Node tail;
88
89                 Node[] buckets = new Node [BucketSize];
90                 int count;
91                 int size = 2;
92
93                 SimpleRwLock slim = new SimpleRwLock ();
94
95                 readonly IEqualityComparer<TKey> comparer;
96
97                 public SplitOrderedList (IEqualityComparer<TKey> comparer)
98                 {
99                         this.comparer = comparer;
100                         head = new Node ().Init (0);
101                         tail = new Node ().Init (ulong.MaxValue);
102                         head.Next = tail;
103                         SetBucket (0, head);
104                 }
105
106                 public int Count {
107                         get {
108                                 return count;
109                         }
110                 }
111
112                 public T InsertOrUpdate (uint key, TKey subKey, Func<T> addGetter, Func<T, T> updateGetter)
113                 {
114                         Node current;
115                         bool result = InsertInternal (key, subKey, default (T), addGetter, out current);
116
117                         if (result)
118                                 return current.Data;
119
120                         // FIXME: this should have a CAS-like behavior
121                         return current.Data = updateGetter (current.Data);
122                 }
123
124                 public T InsertOrUpdate (uint key, TKey subKey, T addValue, T updateValue)
125                 {
126                         Node current;
127                         if (InsertInternal (key, subKey, addValue, null, out current))
128                                 return current.Data;
129
130                         // FIXME: this should have a CAS-like behavior
131                         return current.Data = updateValue;
132                 }
133                 
134                 public bool Insert (uint key, TKey subKey, T data)
135                 {
136                         Node current;
137                         return InsertInternal (key, subKey, data, null, out current);
138                 }
139
140                 public T InsertOrGet (uint key, TKey subKey, T data, Func<T> dataCreator)
141                 {
142                         Node current;
143                         InsertInternal (key, subKey, data, dataCreator, out current);
144                         return current.Data;
145                 }
146
147                 bool InsertInternal (uint key, TKey subKey, T data, Func<T> dataCreator, out Node current)
148                 {
149                         Node node = new Node ().Init (ComputeRegularKey (key), subKey, data);
150
151                         uint b = key % (uint)size;
152                         Node bucket;
153
154                         if ((bucket = GetBucket (b)) == null)
155                                 bucket = InitializeBucket (b);
156
157                         if (!ListInsert (node, bucket, out current, dataCreator))
158                                 return false;
159
160                         int csize = size;
161                         if (Interlocked.Increment (ref count) / csize > MaxLoad && (csize & 0x40000000) == 0)
162                                 Interlocked.CompareExchange (ref size, 2 * csize, csize);
163
164                         current = node;
165
166                         return true;
167                 }
168                 
169                 public bool Find (uint key, TKey subKey, out T data)
170                 {
171                         Node node;
172                         uint b = key % (uint)size;
173                         data = default (T);
174                         Node bucket;
175
176                         if ((bucket = GetBucket (b)) == null)
177                                 bucket = InitializeBucket (b);
178
179                         if (!ListFind (ComputeRegularKey (key), subKey, bucket, out node))
180                                 return false;
181
182                         data = node.Data;
183
184                         return !node.Marked;
185                 }
186
187                 public bool CompareExchange (uint key, TKey subKey, T data, Func<T, bool> check)
188                 {
189                         Node node;
190                         uint b = key % (uint)size;
191                         Node bucket;
192
193                         if ((bucket = GetBucket (b)) == null)
194                                 bucket = InitializeBucket (b);
195
196                         if (!ListFind (ComputeRegularKey (key), subKey, bucket, out node))
197                                 return false;
198
199                         if (!check (node.Data))
200                                 return false;
201
202                         node.Data = data;
203
204                         return true;
205                 }
206
207                 public bool Delete (uint key, TKey subKey, out T data)
208                 {
209                         uint b = key % (uint)size;
210                         Node bucket;
211
212                         if ((bucket = GetBucket (b)) == null)
213                                 bucket = InitializeBucket (b);
214
215                         if (!ListDelete (bucket, ComputeRegularKey (key), subKey, out data))
216                                 return false;
217
218                         Interlocked.Decrement (ref count);
219                         return true;
220                 }
221
222                 public IEnumerator<T> GetEnumerator ()
223                 {
224                         Node node = head.Next;
225
226                         while (node != tail) {
227                                 while (node.Marked || (node.Key & 1) == 0) {
228                                         node = node.Next;
229                                         if (node == tail)
230                                                 yield break;
231                                 }
232                                 yield return node.Data;
233                                 node = node.Next;
234                         }
235                 }
236
237                 Node InitializeBucket (uint b)
238                 {
239                         Node current;
240                         uint parent = GetParent (b);
241                         Node bucket;
242
243                         if ((bucket = GetBucket (parent)) == null)
244                                 bucket = InitializeBucket (parent);
245
246                         Node dummy = new Node ().Init (ComputeDummyKey (b));
247                         if (!ListInsert (dummy, bucket, out current, null))
248                                 return current;
249
250                         return SetBucket (b, dummy);
251                 }
252                 
253                 // Turn v's MSB off
254                 static uint GetParent (uint v)
255                 {
256                         uint t, tt;
257                         
258                         // Find MSB position in v
259                         var pos = (tt = v >> 16) > 0 ?
260                                 (t = tt >> 8) > 0 ? 24 + logTable[t] : 16 + logTable[tt] :
261                                 (t = v >> 8) > 0 ? 8 + logTable[t] : logTable[v];
262
263                         return (uint)(v & ~(1 << pos));
264                 }
265
266                 // Reverse integer bits and make sure LSB is set
267                 static ulong ComputeRegularKey (uint key)
268                 {
269                         return ComputeDummyKey (key) | 1;
270                 }
271                 
272                 // Reverse integer bits
273                 static ulong ComputeDummyKey (uint key)
274                 {
275                         return ((ulong)(((uint)reverseTable[key & 0xff] << 24) |
276                                         ((uint)reverseTable[(key >> 8) & 0xff] << 16) |
277                                         ((uint)reverseTable[(key >> 16) & 0xff] << 8) |
278                                         ((uint)reverseTable[(key >> 24) & 0xff]))) << 1;
279                 }
280
281                 // Bucket storage is abstracted in a simple two-layer tree to avoid too much memory resize
282                 Node GetBucket (uint index)
283                 {
284                         if (index >= buckets.Length)
285                                 return null;
286                         return buckets[index];
287                 }
288
289                 Node SetBucket (uint index, Node node)
290                 {
291                         try {
292                                 slim.EnterReadLock ();
293                                 CheckSegment (index, true);
294
295                                 Interlocked.CompareExchange (ref buckets[index], node, null);
296                                 return buckets[index];
297                         } finally {
298                                 slim.ExitReadLock ();
299                         }
300                 }
301
302                 // When we run out of space for bucket storage, we use a lock-based array resize
303                 void CheckSegment (uint segment, bool readLockTaken)
304                 {
305                         if (segment < buckets.Length)
306                                 return;
307
308                         if (readLockTaken)
309                                 slim.ExitReadLock ();
310                         try {
311                                 slim.EnterWriteLock ();
312                                 while (segment >= buckets.Length)
313                                         Array.Resize (ref buckets, buckets.Length * 2);
314                         } finally {
315                                 slim.ExitWriteLock ();
316                         }
317                         if (readLockTaken)
318                                 slim.EnterReadLock ();
319                 }
320
321                 Node ListSearch (ulong key, TKey subKey, ref Node left, Node h)
322                 {
323                         Node leftNodeNext = null, rightNode = null;
324
325                         do {
326                                 Node t = h;
327                                 Node tNext = t.Next;
328                                 do {
329                                         if (!tNext.Marked) {
330                                                 left = t;
331                                                 leftNodeNext = tNext;
332                                         }
333                                         t = tNext.Marked ? tNext.Next : tNext;
334                                         if (t == tail)
335                                                 break;
336                                         
337                                         tNext = t.Next;
338                                 } while (tNext.Marked || t.Key < key || (tNext.Key == key && !comparer.Equals (subKey, t.SubKey)));
339                                 
340                                 rightNode = t;
341                                 
342                                 if (leftNodeNext == rightNode) {
343                                         if (rightNode != tail && rightNode.Next.Marked)
344                                                 continue;
345                                         else 
346                                                 return rightNode;
347                                 }
348                                 
349                                 if (Interlocked.CompareExchange (ref left.Next, rightNode, leftNodeNext) == leftNodeNext) {
350                                         if (rightNode != tail && rightNode.Next.Marked)
351                                                 continue;
352                                         else
353                                                 return rightNode;
354                                 }
355                         } while (true);
356                 }
357
358                 bool ListDelete (Node startPoint, ulong key, TKey subKey, out T data)
359                 {
360                         Node rightNode = null, rightNodeNext = null, leftNode = null;
361                         data = default (T);
362                         Node markedNode = null;
363                         
364                         do {
365                                 rightNode = ListSearch (key, subKey, ref leftNode, startPoint);
366                                 if (rightNode == tail || rightNode.Key != key || !comparer.Equals (subKey, rightNode.SubKey))
367                                         return false;
368
369                                 data = rightNode.Data;
370                                 rightNodeNext = rightNode.Next;
371
372                                 if (!rightNodeNext.Marked) {
373                                         if (markedNode == null)
374                                                 markedNode = new Node ();
375                                         markedNode.Init (rightNodeNext);
376
377                                         if (Interlocked.CompareExchange (ref rightNode.Next, markedNode, rightNodeNext) == rightNodeNext)
378                                                 break;
379                                 }
380                         } while (true);
381                         
382                         if (Interlocked.CompareExchange (ref leftNode.Next, rightNodeNext, rightNode) != rightNode)
383                                 ListSearch (rightNode.Key, subKey, ref leftNode, startPoint);
384                         
385                         return true;
386                 }
387                 
388                 bool ListInsert (Node newNode, Node startPoint, out Node current, Func<T> dataCreator)
389                 {
390                         ulong key = newNode.Key;
391                         Node rightNode = null, leftNode = null;
392                         
393                         do {
394                                 rightNode = current = ListSearch (key, newNode.SubKey, ref leftNode, startPoint);
395                                 if (rightNode != tail && rightNode.Key == key && comparer.Equals (newNode.SubKey, rightNode.SubKey))
396                                         return false;
397                                 
398                                 newNode.Next = rightNode;
399                                 if (dataCreator != null)
400                                         newNode.Data = dataCreator ();
401                                 if (Interlocked.CompareExchange (ref leftNode.Next, newNode, rightNode) == rightNode)
402                                         return true;
403                         } while (true);
404                 }
405                 
406                 bool ListFind (ulong key, TKey subKey, Node startPoint, out Node data)
407                 {
408                         Node rightNode = null, leftNode = null;
409                         data = null;
410                         
411                         rightNode = ListSearch (key, subKey, ref leftNode, startPoint);
412                         data = rightNode;
413                         
414                         return rightNode != tail && rightNode.Key == key && comparer.Equals (subKey, rightNode.SubKey);
415                 }
416
417                 static readonly byte[] reverseTable = {
418                         0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
419                 };
420
421                 static readonly byte[] logTable = {
422                         0xFF, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
423                 };
424
425                 struct SimpleRwLock
426                 {
427                         const int RwWait = 1;
428                         const int RwWrite = 2;
429                         const int RwRead = 4;
430
431                         int rwlock;
432
433                         public void EnterReadLock ()
434                         {
435                                 SpinWait sw = new SpinWait ();
436                                 do {
437                                         while ((rwlock & (RwWrite | RwWait)) > 0)
438                                                 sw.SpinOnce ();
439
440                                         if ((Interlocked.Add (ref rwlock, RwRead) & (RwWait | RwWait)) == 0)
441                                                 return;
442
443                                         Interlocked.Add (ref rwlock, -RwRead);
444                                 } while (true);
445                         }
446
447                         public void ExitReadLock ()
448                         {
449                                 Interlocked.Add (ref rwlock, -RwRead);
450                         }
451
452                         public void EnterWriteLock ()
453                         {
454                                 SpinWait sw = new SpinWait ();
455                                 do {
456                                         int state = rwlock;
457                                         if (state < RwWrite) {
458                                                 if (Interlocked.CompareExchange (ref rwlock, RwWrite, state) == state)
459                                                         return;
460                                                 state = rwlock;
461                                         }
462                                         // We register our interest in taking the Write lock (if upgradeable it's already done)
463                                         while ((state & RwWait) == 0 && Interlocked.CompareExchange (ref rwlock, state | RwWait, state) != state)
464                                                 state = rwlock;
465                                         // Before falling to sleep
466                                         while (rwlock > RwWait)
467                                                 sw.SpinOnce ();
468                                 } while (true);
469                         }
470
471                         public void ExitWriteLock ()
472                         {
473                                 Interlocked.Add (ref rwlock, -RwWrite);
474                         }
475                 }
476         }
477
478 }
479