| | |
| | | private void InitializeFromCollection (IEnumerable<KeyValuePair<TKey, TValue>> collection) |
| | | { |
| | | TValue dummy; |
| | | foreach (KeyValuePair<TKey, TValue> pair in collection) { |
| | | foreach (KeyValuePair<TKey, TValue> pair in collection) |
| | | { |
| | | if (pair.Key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | |
| | | if (!TryAddInternal (pair.Key, pair.Value, false, false, out dummy)) { |
| | | if (!TryAddInternal(pair.Key, pair.Value, false, false, out dummy)) |
| | | { |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_SourceContainsDuplicateKeys")); |
| | | } |
| | | } |
| | |
| | | /// (Nothing in Visual Basic).</exception> |
| | | public ConcurrentDictionary (int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer) |
| | | { |
| | | if (concurrencyLevel < 1) { |
| | | if (concurrencyLevel < 1) |
| | | { |
| | | throw new ArgumentOutOfRangeException ("concurrencyLevel", GetResource ("ConcurrentDictionary_ConcurrencyLevelMustBePositive")); |
| | | } |
| | | if (capacity < 0) { |
| | | if (capacity < 0) |
| | | { |
| | | throw new ArgumentOutOfRangeException ("capacity", GetResource ("ConcurrentDictionary_CapacityMustNotBeNegative")); |
| | | } |
| | | if (comparer == null) |
| | |
| | | |
| | | // The capacity should be at least as large as the concurrency level. Otherwise, we would have locks that don't guard |
| | | // any buckets. |
| | | if (capacity < concurrencyLevel) { |
| | | if (capacity < concurrencyLevel) |
| | | { |
| | | capacity = concurrencyLevel; |
| | | } |
| | | |
| | | m_locks = new object[concurrencyLevel]; |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | m_locks [i] = new object (); |
| | | } |
| | | |
| | |
| | | /// <returns></returns> |
| | | private bool TryRemoveInternal (TKey key, out TValue value, bool matchValue, TValue oldValue) |
| | | { |
| | | while (true) { |
| | | while (true) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | |
| | | int bucketNo, lockNo; |
| | | GetBucketAndLockNo (m_comparer.GetHashCode (key), out bucketNo, out lockNo, buckets.Length); |
| | | |
| | | lock (m_locks[lockNo]) { |
| | | lock (m_locks[lockNo]) |
| | | { |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) { |
| | | if (buckets != m_buckets) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | Node prev = null; |
| | | for (Node curr = m_buckets [bucketNo]; curr != null; curr = curr.m_next) { |
| | | for (Node curr = m_buckets[bucketNo]; curr != null; curr = curr.m_next) |
| | | { |
| | | Assert ((prev == null && curr == m_buckets [bucketNo]) || prev.m_next == curr); |
| | | |
| | | if (m_comparer.Equals (curr.m_key, key)) { |
| | | if (matchValue) { |
| | | if (m_comparer.Equals(curr.m_key, key)) |
| | | { |
| | | if (matchValue) |
| | | { |
| | | bool valuesMatch = EqualityComparer<TValue>.Default.Equals (oldValue, curr.m_value); |
| | | if (!valuesMatch) { |
| | | if (!valuesMatch) |
| | | { |
| | | value = default(TValue); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | if (prev == null) { |
| | | if (prev == null) |
| | | { |
| | | m_buckets [bucketNo] = curr.m_next; |
| | | } else { |
| | | } |
| | | else |
| | | { |
| | | prev.m_next = curr.m_next; |
| | | } |
| | | |
| | |
| | | |
| | | // The memory barrier ensures that the load of the fields of 'n' doesn’t move before the load from buckets[i]. |
| | | Thread.MemoryBarrier (); |
| | | while (n != null) { |
| | | if (m_comparer.Equals (n.m_key, key)) { |
| | | while (n != null) |
| | | { |
| | | if (m_comparer.Equals(n.m_key, key)) |
| | | { |
| | | value = n.m_value; |
| | | return true; |
| | | } |
| | |
| | | int hashcode = m_comparer.GetHashCode (key); |
| | | IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default; |
| | | |
| | | while (true) { |
| | | while (true) |
| | | { |
| | | int bucketNo; |
| | | int lockNo; |
| | | |
| | | Node[] buckets = m_buckets; |
| | | GetBucketAndLockNo (hashcode, out bucketNo, out lockNo, buckets.Length); |
| | | |
| | | lock (m_locks[lockNo]) { |
| | | lock (m_locks[lockNo]) |
| | | { |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) { |
| | | if (buckets != m_buckets) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | // Try to find this key in the bucket |
| | | Node prev = null; |
| | | for (Node node = buckets [bucketNo]; node != null; node = node.m_next) { |
| | | for (Node node = buckets[bucketNo]; node != null; node = node.m_next) |
| | | { |
| | | Assert ((prev == null && node == m_buckets [bucketNo]) || prev.m_next == node); |
| | | if (m_comparer.Equals (node.m_key, key)) { |
| | | if (valueComparer.Equals (node.m_value, comparisonValue)) { |
| | | if (m_comparer.Equals(node.m_key, key)) |
| | | { |
| | | if (valueComparer.Equals(node.m_value, comparisonValue)) |
| | | { |
| | | // Replace the old node with a new node. Unfortunately, we cannot simply |
| | | // change node.m_value in place. We don't know the size of TValue, so |
| | | // its writes may not be atomic. |
| | | Node newNode = new Node (node.m_key, newValue, hashcode, node.m_next); |
| | | |
| | | if (prev == null) { |
| | | if (prev == null) |
| | | { |
| | | buckets [bucketNo] = newNode; |
| | | } else { |
| | | } |
| | | else |
| | | { |
| | | prev.m_next = newNode; |
| | | } |
| | | |
| | |
| | | public void Clear () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | try |
| | | { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | |
| | | m_buckets = new Node[DEFAULT_CAPACITY]; |
| | | Array.Clear (m_countPerLock, 0, m_countPerLock.Length); |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | |
| | | throw new ArgumentOutOfRangeException ("index", GetResource ("ConcurrentDictionary_IndexIsNegative")); |
| | | |
| | | int locksAcquired = 0; |
| | | try { |
| | | try |
| | | { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | |
| | | int count = 0; |
| | | |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | |
| | | if (array.Length - count < index || count < 0) { //"count" itself or "count + index" can overflow |
| | | if (array.Length - count < index || count < 0) |
| | | { //"count" itself or "count + index" can overflow |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayNotLargeEnough")); |
| | | } |
| | | |
| | | CopyToPairs (array, index); |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | |
| | | public KeyValuePair<TKey, TValue>[] ToArray () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | try |
| | | { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | int count = 0; |
| | | checked { |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | checked |
| | | { |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | } |
| | |
| | | |
| | | CopyToPairs (array, 0); |
| | | return array; |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | |
| | | private void CopyToPairs (KeyValuePair<TKey, TValue>[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (Node current = buckets [i]; current != null; current = current.m_next) { |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | for (Node current = buckets[i]; current != null; current = current.m_next) |
| | | { |
| | | array [index] = new KeyValuePair<TKey, TValue> (current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToPairs is only called when there's no overflow risk |
| | | } |
| | |
| | | private void CopyToEntries (DictionaryEntry[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (Node current = buckets [i]; current != null; current = current.m_next) { |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | for (Node current = buckets[i]; current != null; current = current.m_next) |
| | | { |
| | | array [index] = new DictionaryEntry (current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToEntries is only called when there's no overflow risk |
| | | } |
| | |
| | | private void CopyToObjects (object[] array, int index) |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (Node current = buckets [i]; current != null; current = current.m_next) { |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | for (Node current = buckets[i]; current != null; current = current.m_next) |
| | | { |
| | | array [index] = new KeyValuePair<TKey, TValue> (current.m_key, current.m_value); |
| | | index++; //this should never flow, CopyToObjects is only called when there's no overflow risk |
| | | } |
| | |
| | | { |
| | | Node[] buckets = m_buckets; |
| | | |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | Node current = buckets [i]; |
| | | |
| | | // The memory barrier ensures that the load of the fields of 'current' doesn’t move before the load from buckets[i]. |
| | | Thread.MemoryBarrier (); |
| | | while (current != null) { |
| | | while (current != null) |
| | | { |
| | | yield return new KeyValuePair<TKey, TValue> (current.m_key, current.m_value); |
| | | current = current.m_next; |
| | | } |
| | |
| | | { |
| | | int hashcode = m_comparer.GetHashCode (key); |
| | | |
| | | while (true) { |
| | | while (true) |
| | | { |
| | | int bucketNo, lockNo; |
| | | |
| | | Node[] buckets = m_buckets; |
| | |
| | | |
| | | bool resizeDesired = false; |
| | | bool lockTaken = false; |
| | | try { |
| | | if (acquireLock) { |
| | | try |
| | | { |
| | | if (acquireLock) |
| | | { |
| | | Monitor.Enter (m_locks [lockNo]); |
| | | lockTaken = true; |
| | | } |
| | | |
| | | // If the table just got resized, we may not be holding the right lock, and must retry. |
| | | // This should be a rare occurence. |
| | | if (buckets != m_buckets) { |
| | | if (buckets != m_buckets) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | // Try to find this key in the bucket |
| | | Node prev = null; |
| | | for (Node node = buckets [bucketNo]; node != null; node = node.m_next) { |
| | | for (Node node = buckets[bucketNo]; node != null; node = node.m_next) |
| | | { |
| | | Assert ((prev == null && node == m_buckets [bucketNo]) || prev.m_next == node); |
| | | if (m_comparer.Equals (node.m_key, key)) { |
| | | if (m_comparer.Equals(node.m_key, key)) |
| | | { |
| | | // The key was found in the dictionary. If updates are allowed, update the value for that key. |
| | | // We need to create a new node for the update, in order to support TValue types that cannot |
| | | // be written atomically, since lock-free reads may be happening concurrently. |
| | | if (updateIfExists) { |
| | | if (updateIfExists) |
| | | { |
| | | Node newNode = new Node (node.m_key, value, hashcode, node.m_next); |
| | | if (prev == null) { |
| | | if (prev == null) |
| | | { |
| | | buckets [bucketNo] = newNode; |
| | | } else { |
| | | } |
| | | else |
| | | { |
| | | prev.m_next = newNode; |
| | | } |
| | | resultingValue = value; |
| | | } else { |
| | | } |
| | | else |
| | | { |
| | | resultingValue = node.m_value; |
| | | } |
| | | return false; |
| | |
| | | |
| | | // The key was not found in the bucket. Insert the key-value pair. |
| | | buckets [bucketNo] = new Node (key, value, hashcode, buckets [bucketNo]); |
| | | checked { |
| | | checked |
| | | { |
| | | m_countPerLock [lockNo]++; |
| | | } |
| | | |
| | |
| | | // Note: the formula is chosen to avoid overflow, but has a small inaccuracy due to |
| | | // rounding. |
| | | // |
| | | if (m_countPerLock [lockNo] > buckets.Length / m_locks.Length) { |
| | | if (m_countPerLock[lockNo] > buckets.Length / m_locks.Length) |
| | | { |
| | | resizeDesired = true; |
| | | } |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | if (lockTaken) |
| | | Monitor.Exit (m_locks [lockNo]); |
| | | } |
| | |
| | | // - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0 |
| | | // and then verify that the table we passed to it as the argument is still the current table. |
| | | // |
| | | if (resizeDesired) { |
| | | if (resizeDesired) |
| | | { |
| | | GrowTable (buckets); |
| | | } |
| | | |
| | |
| | | /// <exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and |
| | | /// <paramref name="key"/> |
| | | /// does not exist in the collection.</exception> |
| | | public TValue this [TKey key] { |
| | | get { |
| | | public TValue this[TKey key] |
| | | { |
| | | get |
| | | { |
| | | TValue value; |
| | | if (!TryGetValue (key, out value)) { |
| | | if (!TryGetValue(key, out value)) |
| | | { |
| | | throw new KeyNotFoundException (); |
| | | } |
| | | return value; |
| | | } |
| | | set { |
| | | set |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | TValue dummy; |
| | |
| | | /// <remarks>Count has snapshot semantics and represents the number of items in the <see |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/> |
| | | /// at the moment when Count was accessed.</remarks> |
| | | public int Count { |
| | | get { |
| | | public int Count |
| | | { |
| | | get |
| | | { |
| | | int count = 0; |
| | | |
| | | int acquiredLocks = 0; |
| | | try { |
| | | try |
| | | { |
| | | // Acquire all locks |
| | | AcquireAllLocks (ref acquiredLocks); |
| | | |
| | | // Compute the count, we allow overflow |
| | | for (int i = 0; i < m_countPerLock.Length; i++) { |
| | | for (int i = 0; i < m_countPerLock.Length; i++) |
| | | { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | // Release locks that have been acquired earlier |
| | | ReleaseLocks (0, acquiredLocks); |
| | | } |
| | |
| | | throw new ArgumentNullException ("valueFactory"); |
| | | |
| | | TValue resultingValue; |
| | | if (TryGetValue (key, out resultingValue)) { |
| | | if (TryGetValue(key, out resultingValue)) |
| | | { |
| | | return resultingValue; |
| | | } |
| | | TryAddInternal (key, valueFactory (key), false, true, out resultingValue); |
| | |
| | | throw new ArgumentNullException ("updateValueFactory"); |
| | | |
| | | TValue newValue, resultingValue; |
| | | while (true) { |
| | | while (true) |
| | | { |
| | | TValue oldValue; |
| | | if (TryGetValue (key, out oldValue)) |
| | | { //key exists, try to update |
| | | newValue = updateValueFactory (key, oldValue); |
| | | if (TryUpdate (key, newValue, oldValue)) { |
| | | if (TryUpdate(key, newValue, oldValue)) |
| | | { |
| | | return newValue; |
| | | } |
| | | } else { //try add |
| | | } |
| | | else |
| | | { //try add |
| | | newValue = addValueFactory (key); |
| | | if (TryAddInternal (key, newValue, false, true, out resultingValue)) { |
| | | if (TryAddInternal(key, newValue, false, true, out resultingValue)) |
| | | { |
| | | return resultingValue; |
| | | } |
| | | } |
| | |
| | | if (updateValueFactory == null) |
| | | throw new ArgumentNullException ("updateValueFactory"); |
| | | TValue newValue, resultingValue; |
| | | while (true) { |
| | | while (true) |
| | | { |
| | | TValue oldValue; |
| | | if (TryGetValue (key, out oldValue)) |
| | | { //key exists, try to update |
| | | newValue = updateValueFactory (key, oldValue); |
| | | if (TryUpdate (key, newValue, oldValue)) { |
| | | if (TryUpdate(key, newValue, oldValue)) |
| | | { |
| | | return newValue; |
| | | } |
| | | } else { //try add |
| | | if (TryAddInternal (key, addValue, false, true, out resultingValue)) { |
| | | } |
| | | else |
| | | { //try add |
| | | if (TryAddInternal(key, addValue, false, true, out resultingValue)) |
| | | { |
| | | return resultingValue; |
| | | } |
| | | } |
| | |
| | | /// </summary> |
| | | /// <value>true if the <see cref="ConcurrentDictionary{TKey,TValue}"/> is empty; otherwise, |
| | | /// false.</value> |
| | | public bool IsEmpty { |
| | | get { |
| | | public bool IsEmpty |
| | | { |
| | | get |
| | | { |
| | | int acquiredLocks = 0; |
| | | try { |
| | | try |
| | | { |
| | | // Acquire all locks |
| | | AcquireAllLocks (ref acquiredLocks); |
| | | |
| | | for (int i = 0; i < m_countPerLock.Length; i++) { |
| | | if (m_countPerLock [i] != 0) { |
| | | for (int i = 0; i < m_countPerLock.Length; i++) |
| | | { |
| | | if (m_countPerLock[i] != 0) |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | // Release locks that have been acquired earlier |
| | | ReleaseLocks (0, acquiredLocks); |
| | | } |
| | |
| | | /// cref="ConcurrentDictionary{TKey,TValue}"/>.</exception> |
| | | void IDictionary<TKey, TValue>.Add (TKey key, TValue value) |
| | | { |
| | | if (!TryAdd (key, value)) { |
| | | if (!TryAdd(key, value)) |
| | | { |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_KeyAlreadyExisted")); |
| | | } |
| | | } |
| | |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.Generic.ICollection{TKey}"/> containing the keys in the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> |
| | | public ICollection<TKey> Keys { |
| | | public ICollection<TKey> Keys |
| | | { |
| | | get { return GetKeys (); } |
| | | } |
| | | |
| | |
| | | /// <value>An <see cref="T:System.Collections.Generic.ICollection{TValue}"/> containing the values in |
| | | /// the |
| | | /// <see cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.</value> |
| | | public ICollection<TValue> Values { |
| | | public ICollection<TValue> Values |
| | | { |
| | | get { return GetValues (); } |
| | | } |
| | | |
| | |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.Contains (KeyValuePair<TKey, TValue> keyValuePair) |
| | | { |
| | | TValue value; |
| | | if (!TryGetValue (keyValuePair.Key, out value)) { |
| | | if (!TryGetValue(keyValuePair.Key, out value)) |
| | | { |
| | | return false; |
| | | } |
| | | return EqualityComparer<TValue>.Default.Equals (value, keyValuePair.Value); |
| | |
| | | /// read-only; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>, this property always returns |
| | | /// false.</value> |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { |
| | | bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfKeyIncorrect")); |
| | | |
| | | TValue typedValue; |
| | | try { |
| | | try |
| | | { |
| | | typedValue = (TValue)value; |
| | | } catch (InvalidCastException) { |
| | | } |
| | | catch (InvalidCastException) |
| | | { |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfValueIncorrect")); |
| | | } |
| | | |
| | |
| | | /// fixed size; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool IDictionary.IsFixedSize { |
| | | bool IDictionary.IsFixedSize |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | |
| | | /// read-only; otherwise, false. For <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool IDictionary.IsReadOnly { |
| | | bool IDictionary.IsReadOnly |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.ICollection"/> containing the keys of the <see |
| | | /// cref="T:System.Collections.Generic.IDictionary{TKey,TValue}"/>.</value> |
| | | ICollection IDictionary.Keys { |
| | | ICollection IDictionary.Keys |
| | | { |
| | | get { return GetKeys (); } |
| | | } |
| | | |
| | |
| | | throw new ArgumentNullException ("key"); |
| | | |
| | | TValue throwAwayValue; |
| | | if (key is TKey) { |
| | | if (key is TKey) |
| | | { |
| | | this.TryRemove ((TKey)key, out throwAwayValue); |
| | | } |
| | | } |
| | |
| | | /// </summary> |
| | | /// <value>An <see cref="T:System.Collections.ICollection"/> containing the values in the <see |
| | | /// cref="T:System.Collections.IDictionary"/>.</value> |
| | | ICollection IDictionary.Values { |
| | | ICollection IDictionary.Values |
| | | { |
| | | get { return GetValues (); } |
| | | } |
| | | |
| | |
| | | /// <typeparamref name="TValue"/> of the <see |
| | | /// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/> |
| | | /// </exception> |
| | | object IDictionary.this [object key] { |
| | | get { |
| | | object IDictionary.this[object key] |
| | | { |
| | | get |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | |
| | | TValue value; |
| | | if (key is TKey && this.TryGetValue ((TKey)key, out value)) { |
| | | if (key is TKey && this.TryGetValue((TKey)key, out value)) |
| | | { |
| | | return value; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | set { |
| | | set |
| | | { |
| | | if (key == null) |
| | | throw new ArgumentNullException ("key"); |
| | | |
| | |
| | | throw new ArgumentOutOfRangeException ("index", GetResource ("ConcurrentDictionary_IndexIsNegative")); |
| | | |
| | | int locksAcquired = 0; |
| | | try { |
| | | try |
| | | { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | |
| | | int count = 0; |
| | | |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | count += m_countPerLock [i]; |
| | | } |
| | | |
| | | if (array.Length - count < index || count < 0) { //"count" itself or "count + index" can overflow |
| | | if (array.Length - count < index || count < 0) |
| | | { //"count" itself or "count + index" can overflow |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayNotLargeEnough")); |
| | | } |
| | | |
| | |
| | | // - an array of objects |
| | | |
| | | KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[]; |
| | | if (pairs != null) { |
| | | if (pairs != null) |
| | | { |
| | | CopyToPairs (pairs, index); |
| | | return; |
| | | } |
| | | |
| | | DictionaryEntry[] entries = array as DictionaryEntry[]; |
| | | if (entries != null) { |
| | | if (entries != null) |
| | | { |
| | | CopyToEntries (entries, index); |
| | | return; |
| | | } |
| | | |
| | | object[] objects = array as object[]; |
| | | if (objects != null) { |
| | | if (objects != null) |
| | | { |
| | | CopyToObjects (objects, index); |
| | | return; |
| | | } |
| | | |
| | | throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayIncorrectType"), "array"); |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | |
| | | /// (thread safe); otherwise, false. For <see |
| | | /// cref="T:System.Collections.Concurrent.ConcurrentDictionary{TKey,TValue}"/>, this property always |
| | | /// returns false.</value> |
| | | bool ICollection.IsSynchronized { |
| | | bool ICollection.IsSynchronized |
| | | { |
| | | get { return false; } |
| | | } |
| | | |
| | |
| | | /// cref="T:System.Collections.ICollection"/>. This property is not supported. |
| | | /// </summary> |
| | | /// <exception cref="T:System.NotSupportedException">The SyncRoot property is not supported.</exception> |
| | | object ICollection.SyncRoot { |
| | | get { |
| | | object ICollection.SyncRoot |
| | | { |
| | | get |
| | | { |
| | | throw new NotSupportedException (); |
| | | } |
| | | } |
| | |
| | | private void GrowTable (Node[] buckets) |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | try |
| | | { |
| | | // The thread that first obtains m_locks[0] will be the one doing the resize operation |
| | | AcquireLocks (0, 1, ref locksAcquired); |
| | | |
| | | // Make sure nobody resized the table while we were waiting for lock 0: |
| | | if (buckets != m_buckets) { |
| | | if (buckets != m_buckets) |
| | | { |
| | | // We assume that since the table reference is different, it was already resized. If we ever |
| | | // decide to do table shrinking, or replace the table for other reasons, we will have to revisit |
| | | // this logic. |
| | |
| | | // Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by |
| | | // 2,3,5 or 7. We can consider a different table-sizing policy in the future. |
| | | int newLength; |
| | | try { |
| | | checked { |
| | | try |
| | | { |
| | | checked |
| | | { |
| | | // Double the size of the buckets table and add one, so that we have an odd integer. |
| | | newLength = buckets.Length * 2 + 1; |
| | | |
| | | // Now, we only need to check odd integers, and find the first that is not divisible |
| | | // by 3, 5 or 7. |
| | | while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0) { |
| | | while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0) |
| | | { |
| | | newLength += 2; |
| | | } |
| | | |
| | | Assert (newLength % 2 != 0); |
| | | } |
| | | } catch (OverflowException) { |
| | | } |
| | | catch (OverflowException) |
| | | { |
| | | // If we were to resize the table, its new size will not fit into a 32-bit signed int. Just return. |
| | | return; |
| | | } |
| | |
| | | AcquireLocks (1, m_locks.Length, ref locksAcquired); |
| | | |
| | | // Copy all data into a new table, creating new nodes for all elements |
| | | for (int i = 0; i < buckets.Length; i++) { |
| | | for (int i = 0; i < buckets.Length; i++) |
| | | { |
| | | Node current = buckets [i]; |
| | | while (current != null) { |
| | | while (current != null) |
| | | { |
| | | Node next = current.m_next; |
| | | int newBucketNo, newLockNo; |
| | | GetBucketAndLockNo (current.m_hashcode, out newBucketNo, out newLockNo, newBuckets.Length); |
| | | |
| | | newBuckets [newBucketNo] = new Node (current.m_key, current.m_value, current.m_hashcode, newBuckets [newBucketNo]); |
| | | |
| | | checked { |
| | | checked |
| | | { |
| | | newCountPerLock [newLockNo]++; |
| | | } |
| | | |
| | |
| | | m_buckets = newBuckets; |
| | | m_countPerLock = newCountPerLock; |
| | | |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | // Release all locks that we took earlier |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | |
| | | /// <summary> |
| | | /// The number of concurrent writes for which to optimize by default. |
| | | /// </summary> |
| | | private static int DefaultConcurrencyLevel { |
| | | private static int DefaultConcurrencyLevel |
| | | { |
| | | |
| | | get { return DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount; } |
| | | } |
| | |
| | | { |
| | | Assert (fromInclusive <= toExclusive); |
| | | |
| | | for (int i = fromInclusive; i < toExclusive; i++) { |
| | | for (int i = fromInclusive; i < toExclusive; i++) |
| | | { |
| | | bool lockTaken = false; |
| | | try { |
| | | try |
| | | { |
| | | Monitor.Enter (m_locks [i]); |
| | | lockTaken = true; |
| | | } finally { |
| | | if (lockTaken) { |
| | | } |
| | | finally |
| | | { |
| | | if (lockTaken) |
| | | { |
| | | locksAcquired++; |
| | | } |
| | | } |
| | |
| | | { |
| | | Assert (fromInclusive <= toExclusive); |
| | | |
| | | for (int i = fromInclusive; i < toExclusive; i++) { |
| | | for (int i = fromInclusive; i < toExclusive; i++) |
| | | { |
| | | Monitor.Exit (m_locks [i]); |
| | | } |
| | | } |
| | |
| | | private ReadOnlyCollection<TKey> GetKeys () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | try |
| | | { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | List<TKey> keys = new List<TKey> (); |
| | | |
| | | for (int i = 0; i < m_buckets.Length; i++) { |
| | | for (int i = 0; i < m_buckets.Length; i++) |
| | | { |
| | | Node current = m_buckets [i]; |
| | | while (current != null) { |
| | | while (current != null) |
| | | { |
| | | keys.Add (current.m_key); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | |
| | | return new ReadOnlyCollection<TKey> (keys); |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | |
| | | private ReadOnlyCollection<TValue> GetValues () |
| | | { |
| | | int locksAcquired = 0; |
| | | try { |
| | | try |
| | | { |
| | | AcquireAllLocks (ref locksAcquired); |
| | | List<TValue> values = new List<TValue> (); |
| | | |
| | | for (int i = 0; i < m_buckets.Length; i++) { |
| | | for (int i = 0; i < m_buckets.Length; i++) |
| | | { |
| | | Node current = m_buckets [i]; |
| | | while (current != null) { |
| | | while (current != null) |
| | | { |
| | | values.Add (current.m_value); |
| | | current = current.m_next; |
| | | } |
| | | } |
| | | |
| | | return new ReadOnlyCollection<TValue> (values); |
| | | } finally { |
| | | } |
| | | finally |
| | | { |
| | | ReleaseLocks (0, locksAcquired); |
| | | } |
| | | } |
| | |
| | | [Conditional ("DEBUG")] |
| | | private void Assert (bool condition) |
| | | { |
| | | if (!condition) { |
| | | if (!condition) |
| | | { |
| | | throw new Exception ("Assertion failed."); |
| | | } |
| | | } |
| | |
| | | m_enumerator = dictionary.GetEnumerator (); |
| | | } |
| | | |
| | | public DictionaryEntry Entry { |
| | | public DictionaryEntry Entry |
| | | { |
| | | get { return new DictionaryEntry (m_enumerator.Current.Key, m_enumerator.Current.Value); } |
| | | } |
| | | |
| | | public object Key { |
| | | public object Key |
| | | { |
| | | get { return m_enumerator.Current.Key; } |
| | | } |
| | | |
| | | public object Value { |
| | | public object Value |
| | | { |
| | | get { return m_enumerator.Current.Value; } |
| | | } |
| | | |
| | | public object Current { |
| | | public object Current |
| | | { |
| | | get { return this.Entry; } |
| | | } |
| | | |
| | |
| | | m_countPerLock = new int[m_serializationConcurrencyLevel]; |
| | | |
| | | m_locks = new object[m_serializationConcurrencyLevel]; |
| | | for (int i = 0; i < m_locks.Length; i++) { |
| | | for (int i = 0; i < m_locks.Length; i++) |
| | | { |
| | | m_locks [i] = new object (); |
| | | } |
| | | |