2 // ConcurrentSkipList.cs
4 // Copyright (c) 2009 Jérémie "Garuma" Laval
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 using System.Threading;
28 using System.Collections;
29 using System.Collections.Generic;
31 namespace System.Threading.Collections
33 public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>
37 public readonly TKey Key;
40 public Pair(TKey key, TValue value)
46 public override bool Equals (object obj)
48 Pair rhs = obj as Pair;
49 return rhs == null ? false : Key.Equals(rhs.Key) && Value.Equals(rhs.Value);
52 public override int GetHashCode ()
54 return Key.GetHashCode();
58 class Basket: List<Pair>
62 // Assumption: a List<T> is never empty
63 ConcurrentSkipList<Basket> container
64 = new ConcurrentSkipList<Basket>((value) => value[0].GetHashCode());
66 int stamp = int.MinValue;
68 public ConcurrentDictionary()
72 public void Add(TKey key, TValue value)
75 // Add a value to an existing basket
76 if (TryGetBasket(key, out basket)) {
77 // Find a maybe more sexy locking scheme later
79 foreach (var p in basket) {
80 if (p.Key.Equals(key))
81 throw new ArgumentException("An element with the same key already exists");
83 basket.Add(new Pair(key, value));
87 basket = new Basket();
88 basket.Add(new Pair(key, value));
89 container.Add(basket);
91 Interlocked.Increment(ref count);
92 Interlocked.Increment(ref stamp);
95 void ICollection<KeyValuePair<TKey,TValue>>.Add(KeyValuePair<TKey, TValue> pair)
97 Add(pair.Key, pair.Value);
100 public TValue GetValue(TKey key)
103 if (!TryGetValue(key, out temp))
104 // TODO: find a correct Exception
105 throw new ArgumentOutOfRangeException("key");
109 public bool TryGetValue(TKey key, out TValue value)
112 value = default(TValue);
114 if (!TryGetBasket(key, out basket))
118 Pair pair = basket.Find((p) => p.Key.Equals(key));
127 public TValue this[TKey key] {
129 return GetValue(key);
133 if (!TryGetBasket(key, out basket)) {
138 Pair pair = basket.Find((p) => p.Key.Equals(key));
140 throw new InvalidOperationException("pair is null, shouldn't be");
142 Interlocked.Increment(ref stamp);
147 public bool Remove(TKey key)
150 if (!TryGetBasket(key, out b))
154 // Should always be == 1 but who know
155 return b.RemoveAll((p) => p.Key.Equals(key)) >= 1;
159 bool ICollection<KeyValuePair<TKey,TValue>>.Remove(KeyValuePair<TKey,TValue> pair)
161 return Remove(pair.Key);
164 public bool ContainsKey(TKey key)
166 return container.ContainsFromHash(key.GetHashCode());
169 bool ICollection<KeyValuePair<TKey,TValue>>.Contains(KeyValuePair<TKey, TValue> pair)
171 return ContainsKey(pair.Key);
177 container = new ConcurrentSkipList<Basket>((value) => value[0].GetHashCode());
186 public bool IsReadOnly {
192 public ICollection<TKey> Keys {
198 public ICollection<TValue> Values {
204 public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
206 return GetEnumeratorInternal();
209 IEnumerator IEnumerable.GetEnumerator()
211 return (IEnumerator)GetEnumeratorInternal();
214 IEnumerator<KeyValuePair<TKey, TValue>> GetEnumeratorInternal()
216 foreach (Basket b in container) {
218 foreach (Pair p in b)
219 yield return new KeyValuePair<TKey, TValue>(p.Key, p.Value);
224 public void CopyTo(KeyValuePair<TKey,TValue>[] array, int startIndex)
229 /*void UpdateKeysValuesColl()
231 foreach (Pair p in this) {
232 // Read from time to time to see if we became useless
233 if (currStamp != tempStamp)
240 bool TryGetBasket(TKey key, out Basket basket)
243 if (!container.GetFromHash(key.GetHashCode(), out basket))