// ConcurrentOrderedList.cs // // Copyright (c) 2010 Jérémie "Garuma" Laval // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // using System; using System.Threading; using System.Collections; using System.Collections.Generic; using System.Runtime.Serialization; #if INSIDE_MONO_PARALLEL using System.Collections.Concurrent; namespace Mono.Collections.Concurrent #else namespace System.Collections.Concurrent #endif { #if INSIDE_MONO_PARALLEL public #endif class ConcurrentOrderedList: ICollection, IEnumerable { class Node { public T Data; public int Key; public Node Next; public bool Marked; public Node () { } public Node (Node wrapped) { Marked = true; Next = wrapped; } } Node head; Node tail; IEqualityComparer comparer; int count; public ConcurrentOrderedList () : this (EqualityComparer.Default) { } public ConcurrentOrderedList (IEqualityComparer comparer) { if (comparer == null) throw new ArgumentNullException ("comparer"); this.comparer = comparer; head = new Node (); tail = new Node (); head.Next = tail; } public bool TryAdd (T data) { Node node = new Node (); node.Data = data; node.Key = comparer.GetHashCode (data); if (ListInsert (node)) { Interlocked.Increment (ref count); return true; } return false; } public bool TryRemove (T data) { T dummy; return TryRemoveHash (comparer.GetHashCode (data), out dummy); } public bool TryRemoveHash (int key, out T data) { if (ListDelete (key, out data)) { Interlocked.Decrement (ref count); return true; } return false; } public bool TryPop (out T data) { return ListPop (out data); } public bool Contains (T data) { return ContainsHash (comparer.GetHashCode (data)); } public bool ContainsHash (int key) { Node node; if (!ListFind (key, out node)) return false; return true; } public bool TryGetFromHash (int key, out T data) { data = default (T); Node node; if (!ListFind (key, out node)) return false; data = node.Data; return true; } public void Clear () { head.Next = tail; } public void CopyTo (T[] array, int startIndex) { if (array == null) throw new ArgumentNullException ("array"); if (startIndex < 0) throw new ArgumentOutOfRangeException ("startIndex"); if (count > array.Length - startIndex) throw new ArgumentException ("array", "The number of elements is greater than the available space from startIndex to the end of the destination array."); foreach (T item in this) { if (startIndex >= array.Length) break; array[startIndex++] = item; } } public IEqualityComparer Comparer { get { return comparer; } } public int Count { get { return count; } } Node ListSearch (int key, ref Node left) { Node leftNodeNext = null, rightNode = null; do { Node t = head; Node tNext = t.Next; do { if (!tNext.Marked) { left = t; leftNodeNext = tNext; } t = tNext.Marked ? tNext.Next : tNext; if (t == tail) break; tNext = t.Next; } while (tNext.Marked || t.Key < key); rightNode = t; if (leftNodeNext == rightNode) { if (rightNode != tail && rightNode.Next.Marked) continue; else return rightNode; } if (Interlocked.CompareExchange (ref left.Next, rightNode, leftNodeNext) == leftNodeNext) { if (rightNode != tail && rightNode.Next.Marked) continue; else return rightNode; } } while (true); } bool ListDelete (int key, out T data) { Node rightNode = null, rightNodeNext = null, leftNode = null; data = default (T); do { rightNode = ListSearch (key, ref leftNode); if (rightNode == tail || rightNode.Key != key) return false; data = rightNode.Data; rightNodeNext = rightNode.Next; if (!rightNodeNext.Marked) if (Interlocked.CompareExchange (ref rightNode.Next, new Node (rightNodeNext), rightNodeNext) == rightNodeNext) break; } while (true); if (Interlocked.CompareExchange (ref leftNode.Next, rightNodeNext, rightNode) != rightNodeNext) ListSearch (rightNode.Key, ref leftNode); return true; } bool ListPop (out T data) { Node rightNode = null, rightNodeNext = null, leftNode = head; data = default (T); do { rightNode = head.Next; if (rightNode == tail) return false; data = rightNode.Data; rightNodeNext = rightNode.Next; if (!rightNodeNext.Marked) if (Interlocked.CompareExchange (ref rightNode.Next, new Node (rightNodeNext), rightNodeNext) == rightNodeNext) break; } while (true); if (Interlocked.CompareExchange (ref leftNode.Next, rightNodeNext, rightNode) != rightNodeNext) ListSearch (rightNode.Key, ref leftNode); return true; } bool ListInsert (Node newNode) { int key = newNode.Key; Node rightNode = null, leftNode = null; do { rightNode = ListSearch (key, ref leftNode); if (rightNode != tail && rightNode.Key == key) return false; newNode.Next = rightNode; if (Interlocked.CompareExchange (ref leftNode.Next, newNode, rightNode) == rightNode) return true; } while (true); } bool ListFind (int key, out Node data) { Node rightNode = null, leftNode = null; data = null; data = rightNode = ListSearch (key, ref leftNode); return rightNode != tail && rightNode.Key == key; } IEnumerator IEnumerable.GetEnumerator () { return GetEnumeratorInternal (); } IEnumerator IEnumerable.GetEnumerator () { return GetEnumeratorInternal (); } IEnumerator GetEnumeratorInternal () { Node node = head.Next; while (node != tail) { while (node.Marked) { node = node.Next; if (node == tail) yield break; } yield return node.Data; node = node.Next; } } bool ICollection.IsReadOnly { get { return false; } } void ICollection.Add (T item) { TryAdd (item); } bool ICollection.Remove (T item) { return TryRemove (item); } } }