#if NET_4_0 // ConcurrentSkipList.cs // // Copyright (c) 2009 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; namespace System.Collections.Concurrent { public class ConcurrentDictionary : IDictionary, ICollection>, IEnumerable>, IDictionary, ICollection, IEnumerable { class Pair { public readonly TKey Key; public TValue Value; public Pair (TKey key, TValue value) { Key = key; Value = value; } public override bool Equals (object obj) { Pair rhs = obj as Pair; return rhs == null ? false : Key.Equals (rhs.Key) && Value.Equals (rhs.Value); } public override int GetHashCode () { return Key.GetHashCode (); } } class Basket: List { public SpinLock Lock = new SpinLock (true); } // Assumption: a List is never empty ConcurrentSkipList container = new ConcurrentSkipList ((value) => value[0].GetHashCode ()); int count; IEqualityComparer comparer; public ConcurrentDictionary () : this (EqualityComparer.Default) { } public ConcurrentDictionary (IEnumerable> values) : this (values, EqualityComparer.Default) { foreach (KeyValuePair pair in values) Add (pair.Key, pair.Value); } public ConcurrentDictionary (IEqualityComparer comparer) { this.comparer = comparer; } public ConcurrentDictionary (IEnumerable> values, IEqualityComparer comparer) : this (comparer) { foreach (KeyValuePair pair in values) Add (pair.Key, pair.Value); } // Parameters unused public ConcurrentDictionary (int concurrencyLevel, int capacity) : this (EqualityComparer.Default) { } public ConcurrentDictionary (int concurrencyLevel, IEnumerable> values, IEqualityComparer comparer) : this (values, comparer) { } // Parameters unused public ConcurrentDictionary (int concurrencyLevel, int capacity, IEqualityComparer comparer) : this (comparer) { } void Add (TKey key, TValue value) { while (!TryAdd (key, value)); } void IDictionary.Add (TKey key, TValue value) { Add (key, value); } public bool TryAdd (TKey key, TValue value) { Basket basket; bool taken = false; // Add a value to an existing basket if (TryGetBasket (key, out basket)) { try { basket.Lock.Enter (ref taken); foreach (var p in basket) { if (comparer.Equals (p.Key, key)) return false; } basket.Add (new Pair (key, value)); } finally { if (taken) basket.Lock.Exit (); } } else { // Add a new basket basket = new Basket (); basket.Add (new Pair (key, value)); if (container.TryAdd (basket)) { Interlocked.Increment (ref count); return true; } else { return false; } } Interlocked.Increment (ref count); return true; } void ICollection>.Add (KeyValuePair pair) { Add (pair.Key, pair.Value); } public TValue AddOrUpdate (TKey key, Func addValueFactory, Func updateValueFactory) { Basket basket; TValue temp = default (TValue); bool taken = false; if (!TryGetBasket (key, out basket)) { Add (key, (temp = addValueFactory (key))); } else { try { basket.Lock.Enter (ref taken); Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key)); if (pair == null) throw new InvalidOperationException ("pair is null, shouldn't be"); pair.Value = (temp = updateValueFactory (key, pair.Value)); } finally { if (taken) basket.Lock.Exit (); } } return temp; } public TValue AddOrUpdate (TKey key, TValue addValue, Func updateValueFactory) { return AddOrUpdate (key, (_) => addValue, updateValueFactory); } TValue GetValue (TKey key) { TValue temp; if (!TryGetValue (key, out temp)) // TODO: find a correct Exception throw new ArgumentException ("Not a valid key for this dictionary", "key"); return temp; } public bool TryGetValue (TKey key, out TValue value) { Basket basket; value = default (TValue); bool taken = false; if (!TryGetBasket (key, out basket)) return false; try { basket.Lock.Enter (ref taken); Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key)); if (pair == null) return false; value = pair.Value; } finally { if (taken) basket.Lock.Exit (); } return true; } public bool TryUpdate (TKey key, TValue newValue, TValue comparand) { Basket basket; bool taken = false; if (!TryGetBasket (key, out basket)) return false; try { basket.Lock.Enter (ref taken); Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key)); if (pair.Value.Equals (comparand)) { pair.Value = newValue; return true; } } finally { if (taken) basket.Lock.Exit (); } return false; } public TValue this[TKey key] { get { return GetValue (key); } set { Basket basket; bool taken = false; if (!TryGetBasket (key, out basket)) { Add (key, value); return; } try { basket.Lock.Enter (ref taken); Pair pair = basket.Find ((p) => comparer.Equals (p.Key, key)); if (pair == null) throw new InvalidOperationException ("pair is null, shouldn't be"); pair.Value = value; } finally { if (taken) basket.Lock.Exit (); } } } public TValue GetOrAdd (TKey key, Func valueFactory) { Basket basket; TValue temp = default (TValue); if (TryGetBasket (key, out basket)) { Pair pair = null; bool taken = false; try { basket.Lock.Enter (ref taken); pair = basket.Find ((p) => comparer.Equals (p.Key, key)); if (pair != null) temp = pair.Value; } finally { if (taken) basket.Lock.Exit (); } if (pair == null) Add (key, (temp = valueFactory (key))); } else { Add (key, (temp = valueFactory (key))); } return temp; } public TValue GetOrAdd (TKey key, TValue value) { return GetOrAdd (key, (_) => value); } public bool TryRemove(TKey key, out TValue value) { value = default (TValue); Basket b; bool taken = false; if (!TryGetBasket (key, out b)) return false; try { b.Lock.Enter (ref taken); TValue temp = default (TValue); // Should always be == 1 but who know bool result = b.RemoveAll ((p) => { bool r = comparer.Equals (p.Key, key); if (r) temp = p.Value; return r; }) >= 1; value = temp; if (result) Interlocked.Decrement (ref count); return result; } finally { if (taken) b.Lock.Exit (); } return false; } bool Remove (TKey key) { TValue dummy; return TryRemove (key, out dummy); } bool IDictionary.Remove (TKey key) { return Remove (key); } bool ICollection>.Remove (KeyValuePair pair) { return Remove (pair.Key); } public bool ContainsKey (TKey key) { return container.ContainsFromHash (key.GetHashCode ()); } bool IDictionary.Contains (object key) { if (!(key is TKey)) return false; return ContainsKey ((TKey)key); } void IDictionary.Remove (object key) { if (!(key is TKey)) return; Remove ((TKey)key); } object IDictionary.this [object key] { get { if (!(key is TKey)) throw new ArgumentException ("key isn't of correct type", "key"); return this[(TKey)key]; } set { if (!(key is TKey) || !(value is TValue)) throw new ArgumentException ("key or value aren't of correct type"); this[(TKey)key] = (TValue)value; } } void IDictionary.Add (object key, object value) { if (!(key is TKey) || !(value is TValue)) throw new ArgumentException ("key or value aren't of correct type"); Add ((TKey)key, (TValue)value); } bool ICollection>.Contains (KeyValuePair pair) { return ContainsKey (pair.Key); } public KeyValuePair[] ToArray () { // This is most certainly not optimum but there is // not a lot of possibilities return new List> (this).ToArray (); } public void Clear() { // Pronk container = new ConcurrentSkipList ((value) => value [0].GetHashCode ()); } public int Count { get { return count; } } public bool IsEmpty { get { return count == 0; } } bool ICollection>.IsReadOnly { get { return false; } } bool IDictionary.IsReadOnly { get { return false; } } public ICollection Keys { get { return GetPart ((kvp) => kvp.Key); } } public ICollection Values { get { return GetPart ((kvp) => kvp.Value); } } ICollection IDictionary.Keys { get { return (ICollection)Keys; } } ICollection IDictionary.Values { get { return (ICollection)Values; } } ICollection GetPart (Func, T> extractor) { List temp = new List (); foreach (KeyValuePair kvp in this) temp.Add (extractor (kvp)); return temp.AsReadOnly (); } void ICollection.CopyTo (Array array, int startIndex) { KeyValuePair[] arr = array as KeyValuePair[]; if (arr == null) return; CopyTo (arr, startIndex, count); } void CopyTo (KeyValuePair[] array, int startIndex) { CopyTo (array, startIndex, count); } void ICollection>.CopyTo (KeyValuePair[] array, int startIndex) { CopyTo (array, startIndex); } void CopyTo (KeyValuePair[] array, int startIndex, int num) { // TODO: This is quite unsafe as the count value will likely change during // the copying. Watchout for IndexOutOfRange thingies if (array.Length <= count + startIndex) throw new InvalidOperationException ("The array isn't big enough"); int i = startIndex; foreach (Basket b in container) { bool taken = false; try { b.Lock.Enter (ref taken); foreach (Pair p in b) { if (i >= num) break; array[i++] = new KeyValuePair (p.Key, p.Value); } } finally { if (taken) b.Lock.Exit (); } } } public IEnumerator> GetEnumerator () { return GetEnumeratorInternal (); } IEnumerator IEnumerable.GetEnumerator () { return (IEnumerator)GetEnumeratorInternal (); } IEnumerator> GetEnumeratorInternal () { foreach (Basket b in container) { bool taken = false; try { b.Lock.Enter (ref taken); foreach (Pair p in b) yield return new KeyValuePair (p.Key, p.Value); } finally { if (taken) b.Lock.Exit (); } } } IDictionaryEnumerator IDictionary.GetEnumerator () { return new ConcurrentDictionaryEnumerator (GetEnumeratorInternal ()); } class ConcurrentDictionaryEnumerator : IDictionaryEnumerator { IEnumerator> internalEnum; public ConcurrentDictionaryEnumerator (IEnumerator> internalEnum) { this.internalEnum = internalEnum; } public bool MoveNext () { return internalEnum.MoveNext (); } public void Reset () { internalEnum.Reset (); } public object Current { get { return Entry; } } public DictionaryEntry Entry { get { KeyValuePair current = internalEnum.Current; return new DictionaryEntry (current.Key, current.Value); } } public object Key { get { return internalEnum.Current.Key; } } public object Value { get { return internalEnum.Current.Value; } } } object ICollection.SyncRoot { get { return this; } } bool IDictionary.IsFixedSize { get { return false; } } bool ICollection.IsSynchronized { get { return true; } } bool TryGetBasket (TKey key, out Basket basket) { basket = null; if (!container.GetFromHash (key.GetHashCode (), out basket)) return false; return true; } } } #endif