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
;
30 using System
.Runtime
.Serialization
;
32 namespace System
.Collections
.Concurrent
34 public class ConcurrentDictionary
<TKey
, TValue
> : IDictionary
<TKey
, TValue
>,
35 ICollection
<KeyValuePair
<TKey
, TValue
>>, IEnumerable
<KeyValuePair
<TKey
, TValue
>>,
36 IDictionary
, ICollection
, IEnumerable
40 public readonly TKey Key
;
43 public Pair (TKey key
, TValue
value)
49 public override bool Equals (object obj
)
51 Pair rhs
= obj
as Pair
;
52 return rhs
== null ? false : Key
.Equals (rhs
.Key
) && Value
.Equals (rhs
.Value
);
55 public override int GetHashCode ()
57 return Key
.GetHashCode ();
61 class Basket
: List
<Pair
>
63 public SpinLock Lock
= new SpinLock ();
66 // Assumption: a List<T> is never empty
67 ConcurrentSkipList
<Basket
> container
68 = new ConcurrentSkipList
<Basket
> ((value) => value[0].GetHashCode ());
70 IEqualityComparer
<TKey
> comparer
;
72 public ConcurrentDictionary () : this (EqualityComparer
<TKey
>.Default
)
76 public ConcurrentDictionary (IEnumerable
<KeyValuePair
<TKey
, TValue
>> values
)
77 : this (values
, EqualityComparer
<TKey
>.Default
)
79 foreach (KeyValuePair
<TKey
, TValue
> pair
in values
)
80 Add (pair
.Key
, pair
.Value
);
83 public ConcurrentDictionary (IEqualityComparer
<TKey
> comparer
)
85 this.comparer
= comparer
;
88 public ConcurrentDictionary (IEnumerable
<KeyValuePair
<TKey
, TValue
>> values
, IEqualityComparer
<TKey
> comparer
)
91 foreach (KeyValuePair
<TKey
, TValue
> pair
in values
)
92 Add (pair
.Key
, pair
.Value
);
96 public ConcurrentDictionary (int concurrencyLevel
, int capacity
)
97 : this (EqualityComparer
<TKey
>.Default
)
102 public ConcurrentDictionary (int concurrencyLevel
,
103 IEnumerable
<KeyValuePair
<TKey
, TValue
>> values
,
104 IEqualityComparer
<TKey
> comparer
)
105 : this (values
, comparer
)
111 public ConcurrentDictionary (int concurrencyLevel
, int capacity
, IEqualityComparer
<TKey
> comparer
)
117 void Add (TKey key
, TValue
value)
119 while (!TryAdd (key
, value));
122 void IDictionary
<TKey
, TValue
>.Add (TKey key
, TValue
value)
127 public bool TryAdd (TKey key
, TValue
value)
132 // Add a value to an existing basket
133 if (TryGetBasket (key
, out basket
)) {
136 basket
.Lock
.Enter (ref taken
);
140 foreach (var p
in basket
) {
141 if (comparer
.Equals (p
.Key
, key
))
144 basket
.Add (new Pair (key
, value));
152 basket
= new Basket ();
153 basket
.Add (new Pair (key
, value));
155 if (container
.TryAdd (basket
)) {
156 Interlocked
.Increment (ref count
);
163 Interlocked
.Increment (ref count
);
168 void ICollection
<KeyValuePair
<TKey
,TValue
>>.Add (KeyValuePair
<TKey
, TValue
> pair
)
170 Add (pair
.Key
, pair
.Value
);
173 public TValue
AddOrUpdate (TKey key
, Func
<TKey
, TValue
> addValueFactory
, Func
<TKey
, TValue
, TValue
> updateValueFactory
)
176 TValue temp
= default (TValue
);
179 if (!TryGetBasket (key
, out basket
)) {
180 Add (key
, (temp
= addValueFactory (key
)));
184 basket
.Lock
.Enter (ref taken
);
188 Pair pair
= basket
.Find ((p
) => comparer
.Equals (p
.Key
, key
));
190 throw new InvalidOperationException ("pair is null, shouldn't be");
191 pair
.Value
= (temp
= updateValueFactory (key
, pair
.Value
));
202 public TValue
AddOrUpdate (TKey key
, TValue addValue
, Func
<TKey
, TValue
, TValue
> updateValueFactory
)
204 return AddOrUpdate (key
, (_
) => addValue
, updateValueFactory
);
207 TValue
GetValue (TKey key
)
210 if (!TryGetValue (key
, out temp
))
211 // TODO: find a correct Exception
212 throw new ArgumentException ("Not a valid key for this dictionary", "key");
216 public bool TryGetValue (TKey key
, out TValue
value)
219 value = default (TValue
);
222 if (!TryGetBasket (key
, out basket
))
227 basket
.Lock
.Enter (ref taken
);
231 Pair pair
= basket
.Find ((p
) => comparer
.Equals (p
.Key
, key
));
244 public bool TryUpdate (TKey key
, TValue newValue
, TValue comparand
)
249 if (!TryGetBasket (key
, out basket
))
254 basket
.Lock
.Enter (ref taken
);
258 Pair pair
= basket
.Find ((p
) => comparer
.Equals (p
.Key
, key
));
259 if (pair
.Value
.Equals (comparand
)) {
260 pair
.Value
= newValue
;
273 public TValue
this[TKey key
] {
275 return GetValue (key
);
281 if (!TryGetBasket (key
, out basket
)) {
288 basket
.Lock
.Enter (ref taken
);
292 Pair pair
= basket
.Find ((p
) => comparer
.Equals (p
.Key
, key
));
294 throw new InvalidOperationException ("pair is null, shouldn't be");
304 public TValue
GetOrAdd (TKey key
, Func
<TKey
, TValue
> valueFactory
)
307 TValue temp
= default (TValue
);
309 if (TryGetBasket (key
, out basket
)) {
315 basket
.Lock
.Enter (ref taken
);
318 pair
= basket
.Find ((p
) => comparer
.Equals (p
.Key
, key
));
328 Add (key
, (temp
= valueFactory (key
)));
330 Add (key
, (temp
= valueFactory (key
)));
336 public TValue
GetOrAdd (TKey key
, TValue
value)
338 return GetOrAdd (key
, (_
) => value);
341 public bool TryRemove(TKey key
, out TValue
value)
343 value = default (TValue
);
347 if (!TryGetBasket (key
, out b
))
352 b
.Lock
.Enter (ref taken
);
356 TValue temp
= default (TValue
);
357 // Should always be == 1 but who know
358 bool result
= b
.RemoveAll ((p
) => {
359 bool r
= comparer
.Equals (p
.Key
, key
);
360 if (r
) temp
= p
.Value
;
366 Interlocked
.Decrement (ref count
);
378 bool Remove (TKey key
)
382 return TryRemove (key
, out dummy
);
385 bool IDictionary
<TKey
, TValue
>.Remove (TKey key
)
390 bool ICollection
<KeyValuePair
<TKey
,TValue
>>.Remove (KeyValuePair
<TKey
,TValue
> pair
)
392 return Remove (pair
.Key
);
395 public bool ContainsKey (TKey key
)
397 return container
.ContainsFromHash (key
.GetHashCode ());
400 bool IDictionary
.Contains (object key
)
405 return ContainsKey ((TKey
)key
);
408 void IDictionary
.Remove (object key
)
416 object IDictionary
.this [object key
]
420 throw new ArgumentException ("key isn't of correct type", "key");
422 return this[(TKey
)key
];
425 if (!(key
is TKey
) || !(value is TValue
))
426 throw new ArgumentException ("key or value aren't of correct type");
428 this[(TKey
)key
] = (TValue
)value;
432 void IDictionary
.Add (object key
, object value)
434 if (!(key
is TKey
) || !(value is TValue
))
435 throw new ArgumentException ("key or value aren't of correct type");
437 Add ((TKey
)key
, (TValue
)value);
440 bool ICollection
<KeyValuePair
<TKey
,TValue
>>.Contains (KeyValuePair
<TKey
, TValue
> pair
)
442 return ContainsKey (pair
.Key
);
445 public KeyValuePair
<TKey
,TValue
>[] ToArray ()
447 // This is most certainly not optimum but there is
448 // not a lot of possibilities
450 return new List
<KeyValuePair
<TKey
,TValue
>> (this).ToArray ();
456 container
= new ConcurrentSkipList
<Basket
> ((value) => value [0].GetHashCode ());
465 public bool IsEmpty
{
471 bool ICollection
<KeyValuePair
<TKey
, TValue
>>.IsReadOnly
{
477 bool IDictionary
.IsReadOnly
{
483 public ICollection
<TKey
> Keys
{
485 return GetPart
<TKey
> ((kvp
) => kvp
.Key
);
489 public ICollection
<TValue
> Values
{
491 return GetPart
<TValue
> ((kvp
) => kvp
.Value
);
495 ICollection IDictionary
.Keys
{
497 return (ICollection
)Keys
;
501 ICollection IDictionary
.Values
{
503 return (ICollection
)Values
;
507 ICollection
<T
> GetPart
<T
> (Func
<KeyValuePair
<TKey
, TValue
>, T
> extractor
)
509 List
<T
> temp
= new List
<T
> ();
511 foreach (KeyValuePair
<TKey
, TValue
> kvp
in this)
512 temp
.Add (extractor (kvp
));
514 return temp
.AsReadOnly ();
517 void ICollection
.CopyTo (Array array
, int startIndex
)
519 KeyValuePair
<TKey
, TValue
>[] arr
= array
as KeyValuePair
<TKey
, TValue
>[];
523 CopyTo (arr
, startIndex
, count
);
526 void CopyTo (KeyValuePair
<TKey
, TValue
>[] array
, int startIndex
)
528 CopyTo (array
, startIndex
, count
);
531 void ICollection
<KeyValuePair
<TKey
, TValue
>>.CopyTo (KeyValuePair
<TKey
, TValue
>[] array
, int startIndex
)
533 CopyTo (array
, startIndex
);
536 void CopyTo (KeyValuePair
<TKey
, TValue
>[] array
, int startIndex
, int num
)
538 // TODO: This is quite unsafe as the count value will likely change during
539 // the copying. Watchout for IndexOutOfRange thingies
540 if (array
.Length
<= count
+ startIndex
)
541 throw new InvalidOperationException ("The array isn't big enough");
545 foreach (Basket b
in container
) {
550 b
.Lock
.Enter (ref taken
);
554 foreach (Pair p
in b
) {
557 array
[i
++] = new KeyValuePair
<TKey
, TValue
> (p
.Key
, p
.Value
);
567 public IEnumerator
<KeyValuePair
<TKey
, TValue
>> GetEnumerator ()
569 return GetEnumeratorInternal ();
572 IEnumerator IEnumerable
.GetEnumerator ()
574 return (IEnumerator
)GetEnumeratorInternal ();
577 IEnumerator
<KeyValuePair
<TKey
, TValue
>> GetEnumeratorInternal ()
579 foreach (Basket b
in container
) {
584 b
.Lock
.Enter (ref taken
);
588 foreach (Pair p
in b
)
589 yield return new KeyValuePair
<TKey
, TValue
> (p
.Key
, p
.Value
);
598 IDictionaryEnumerator IDictionary
.GetEnumerator ()
600 return new ConcurrentDictionaryEnumerator (GetEnumeratorInternal ());
603 class ConcurrentDictionaryEnumerator
: IDictionaryEnumerator
605 IEnumerator
<KeyValuePair
<TKey
, TValue
>> internalEnum
;
607 public ConcurrentDictionaryEnumerator (IEnumerator
<KeyValuePair
<TKey
, TValue
>> internalEnum
)
609 this.internalEnum
= internalEnum
;
612 public bool MoveNext ()
614 return internalEnum
.MoveNext ();
619 internalEnum
.Reset ();
622 public object Current
{
628 public DictionaryEntry Entry
{
630 KeyValuePair
<TKey
, TValue
> current
= internalEnum
.Current
;
631 return new DictionaryEntry (current
.Key
, current
.Value
);
637 return internalEnum
.Current
.Key
;
641 public object Value
{
643 return internalEnum
.Current
.Value
;
648 object ICollection
.SyncRoot
{
655 bool IDictionary
.IsFixedSize
{
661 bool ICollection
.IsSynchronized
{
665 bool TryGetBasket (TKey key
, out Basket basket
)
668 if (!container
.GetFromHash (key
.GetHashCode (), out basket
))