#if !UNITY_WSA
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Threading;
namespace TcpServer
{
///
/// Represents a thread-safe collection of keys and values.
///
/// The type of the keys in the dictionary.
/// The type of the values in the dictionary.
///
/// All public and protected members of are thread-safe and may be used
/// concurrently from multiple threads.
///
[Serializable]
[ComVisible (false)]
[DebuggerDisplay ("Count = {Count}")]
// [HostProtection(Synchronization = true, ExternalThreading = true)]
public class ConcurrentDictionary : IDictionary, IDictionary
{
[NonSerialized]
private volatile Node[] m_buckets;
// A singly-linked list for each bucket.
[NonSerialized]
private object[] m_locks;
// A set of locks, each guarding a section of the table.
[NonSerialized]
private volatile int[] m_countPerLock;
// The number of elements guarded by each lock.
private IEqualityComparer m_comparer;
// Key equality comparer
private KeyValuePair[] m_serializationArray;
// Used for custom serialization
private int m_serializationConcurrencyLevel;
// used to save the concurrency level in serialization
private int m_serializationCapacity;
// used to save the capacity in serialization
// The default concurrency level is DEFAULT_CONCURRENCY_MULTIPLIER * #CPUs. The higher the
// DEFAULT_CONCURRENCY_MULTIPLIER, the more concurrent writes can take place without interference
// and blocking, but also the more expensive operations that require all locks become (e.g. table
// resizing, ToArray, Count, etc). According to brief benchmarks that we ran, 4 seems like a good
// compromise.
private const int DEFAULT_CONCURRENCY_MULTIPLIER = 4;
// The default capacity, i.e. the initial # of buckets. When choosing this value, we are making
// a trade-off between the size of a very small dictionary, and the number of resizes when
// constructing a large dictionary. Also, the capacity should not be divisible by a small prime.
private const int DEFAULT_CAPACITY = 31;
///
/// Initializes a new instance of the
/// class that is empty, has the default concurrency level, has the default initial capacity, and
/// uses the default comparer for the key type.
///
public ConcurrentDictionary () : this (DefaultConcurrencyLevel, DEFAULT_CAPACITY)
{
}
///
/// Initializes a new instance of the
/// class that is empty, has the specified concurrency level and capacity, and uses the default
/// comparer for the key type.
///
/// The estimated number of threads that will update the
/// concurrently.
/// The initial number of elements that the
/// can contain.
/// is
/// less than 1.
/// is less than
/// 0.
public ConcurrentDictionary (int concurrencyLevel, int capacity) : this (concurrencyLevel, capacity, EqualityComparer.Default)
{
}
///
/// Initializes a new instance of the
/// class that contains elements copied from the specified , has the default concurrency
/// level, has the default initial capacity, and uses the default comparer for the key type.
///
/// The whose elements are copied to
/// the new
/// .
/// is a null reference
/// (Nothing in Visual Basic).
/// contains one or more
/// duplicate keys.
public ConcurrentDictionary (IEnumerable> collection) : this (collection, EqualityComparer.Default)
{
}
///
/// Initializes a new instance of the
/// class that is empty, has the specified concurrency level and capacity, and uses the specified
/// .
///
/// The
/// implementation to use when comparing keys.
/// is a null reference
/// (Nothing in Visual Basic).
public ConcurrentDictionary (IEqualityComparer comparer) : this (DefaultConcurrencyLevel, DEFAULT_CAPACITY, comparer)
{
}
///
/// Initializes a new instance of the
/// class that contains elements copied from the specified , has the default concurrency level, has the default
/// initial capacity, and uses the specified
/// .
///
/// The whose elements are copied to
/// the new
/// .
/// The
/// implementation to use when comparing keys.
/// is a null reference
/// (Nothing in Visual Basic). -or-
/// is a null reference (Nothing in Visual Basic).
///
public ConcurrentDictionary (IEnumerable> collection, IEqualityComparer comparer)
: this (DefaultConcurrencyLevel, collection, comparer)
{
}
///
/// Initializes a new instance of the
/// class that contains elements copied from the specified ,
/// has the specified concurrency level, has the specified initial capacity, and uses the specified
/// .
///
/// The estimated number of threads that will update the
/// concurrently.
/// The whose elements are copied to the new
/// .
/// The implementation to use
/// when comparing keys.
///
/// is a null reference (Nothing in Visual Basic).
/// -or-
/// is a null reference (Nothing in Visual Basic).
///
///
/// is less than 1.
///
/// contains one or more duplicate keys.
public ConcurrentDictionary (
int concurrencyLevel, IEnumerable> collection, IEqualityComparer comparer)
: this (concurrencyLevel, DEFAULT_CAPACITY, comparer)
{
if (collection == null)
throw new ArgumentNullException ("collection");
if (comparer == null)
throw new ArgumentNullException ("comparer");
InitializeFromCollection (collection);
}
private void InitializeFromCollection (IEnumerable> collection)
{
TValue dummy;
foreach (KeyValuePair pair in collection) {
if (pair.Key == null)
throw new ArgumentNullException ("key");
if (!TryAddInternal (pair.Key, pair.Value, false, false, out dummy)) {
throw new ArgumentException (GetResource ("ConcurrentDictionary_SourceContainsDuplicateKeys"));
}
}
}
///
/// Initializes a new instance of the
/// class that is empty, has the specified concurrency level, has the specified initial capacity, and
/// uses the specified .
///
/// The estimated number of threads that will update the
/// concurrently.
/// The initial number of elements that the
/// can contain.
/// The
/// implementation to use when comparing keys.
///
/// is less than 1. -or-
/// is less than 0.
///
/// is a null reference
/// (Nothing in Visual Basic).
public ConcurrentDictionary (int concurrencyLevel, int capacity, IEqualityComparer comparer)
{
if (concurrencyLevel < 1) {
throw new ArgumentOutOfRangeException ("concurrencyLevel", GetResource ("ConcurrentDictionary_ConcurrencyLevelMustBePositive"));
}
if (capacity < 0) {
throw new ArgumentOutOfRangeException ("capacity", GetResource ("ConcurrentDictionary_CapacityMustNotBeNegative"));
}
if (comparer == null)
throw new ArgumentNullException ("comparer");
// 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) {
capacity = concurrencyLevel;
}
m_locks = new object[concurrencyLevel];
for (int i = 0; i < m_locks.Length; i++) {
m_locks [i] = new object ();
}
m_countPerLock = new int[m_locks.Length];
m_buckets = new Node[capacity];
m_comparer = comparer;
}
///
/// Attempts to add the specified key and value to the .
///
/// The key of the element to add.
/// The value of the element to add. The value can be a null reference (Nothing
/// in Visual Basic) for reference types.
/// true if the key/value pair was added to the
/// successfully; otherwise, false.
/// is null reference
/// (Nothing in Visual Basic).
/// The
/// contains too many elements.
public bool TryAdd (TKey key, TValue value)
{
if (key == null)
throw new ArgumentNullException ("key");
TValue dummy;
return TryAddInternal (key, value, false, true, out dummy);
}
///
/// Determines whether the contains the specified
/// key.
///
/// The key to locate in the .
/// true if the contains an element with
/// the specified key; otherwise, false.
/// is a null reference
/// (Nothing in Visual Basic).
public bool ContainsKey (TKey key)
{
if (key == null)
throw new ArgumentNullException ("key");
TValue throwAwayValue;
return TryGetValue (key, out throwAwayValue);
}
///
/// Attempts to remove and return the the value with the specified key from the
/// .
///
/// The key of the element to remove and return.
/// When this method returns, contains the object removed from the
/// or the default value of
/// if the operation failed.
/// true if an object was removed successfully; otherwise, false.
/// is a null reference
/// (Nothing in Visual Basic).
public bool TryRemove (TKey key, out TValue value)
{
if (key == null)
throw new ArgumentNullException ("key");
return TryRemoveInternal (key, out value, false, default(TValue));
}
///
/// Removes the specified key from the dictionary if it exists and returns its associated value.
/// If matchValue flag is set, the key will be removed only if is associated with a particular
/// value.
///
/// The key to search for and remove if it exists.
/// The variable into which the removed value, if found, is stored.
/// Whether removal of the key is conditional on its value.
/// The conditional value to compare against if is true
///
private bool TryRemoveInternal (TKey key, out TValue value, bool matchValue, TValue oldValue)
{
while (true) {
Node[] buckets = m_buckets;
int bucketNo, lockNo;
GetBucketAndLockNo (m_comparer.GetHashCode (key), out bucketNo, out lockNo, buckets.Length);
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) {
continue;
}
Node prev = null;
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) {
bool valuesMatch = EqualityComparer.Default.Equals (oldValue, curr.m_value);
if (!valuesMatch) {
value = default(TValue);
return false;
}
}
if (prev == null) {
m_buckets [bucketNo] = curr.m_next;
} else {
prev.m_next = curr.m_next;
}
value = curr.m_value;
m_countPerLock [lockNo]--;
return true;
}
prev = curr;
}
}
value = default(TValue);
return false;
}
}
///
/// Attempts to get the value associated with the specified key from the .
///
/// The key of the value to get.
/// When this method returns, contains the object from
/// the
/// with the spedified key or the default value of
/// , if the operation failed.
/// true if the key was found in the ;
/// otherwise, false.
/// is a null reference
/// (Nothing in Visual Basic).
public bool TryGetValue (TKey key, out TValue value)
{
if (key == null)
throw new ArgumentNullException ("key");
int bucketNo, lockNoUnused;
// We must capture the m_buckets field in a local variable. It is set to a new table on each table resize.
Node[] buckets = m_buckets;
GetBucketAndLockNo (m_comparer.GetHashCode (key), out bucketNo, out lockNoUnused, buckets.Length);
// We can get away w/out a lock here.
Node n = buckets [bucketNo];
// 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)) {
value = n.m_value;
return true;
}
n = n.m_next;
}
value = default(TValue);
return false;
}
///
/// Compares the existing value for the specified key with a specified value, and if they’re equal,
/// updates the key with a third value.
///
/// The key whose value is compared with and
/// possibly replaced.
/// The value that replaces the value of the element with if the comparison results in equality.
/// The value that is compared to the value of the element with
/// .
/// true if the value with was equal to and replaced with ; otherwise,
/// false.
/// is a null
/// reference.
public bool TryUpdate (TKey key, TValue newValue, TValue comparisonValue)
{
if (key == null)
throw new ArgumentNullException ("key");
int hashcode = m_comparer.GetHashCode (key);
IEqualityComparer valueComparer = EqualityComparer.Default;
while (true) {
int bucketNo;
int lockNo;
Node[] buckets = m_buckets;
GetBucketAndLockNo (hashcode, out bucketNo, out lockNo, buckets.Length);
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) {
continue;
}
// Try to find this key in the bucket
Node prev = null;
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)) {
// 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) {
buckets [bucketNo] = newNode;
} else {
prev.m_next = newNode;
}
return true;
}
return false;
}
prev = node;
}
//didn't find the key
return false;
}
}
}
///
/// Removes all keys and values from the .
///
public void Clear ()
{
int locksAcquired = 0;
try {
AcquireAllLocks (ref locksAcquired);
m_buckets = new Node[DEFAULT_CAPACITY];
Array.Clear (m_countPerLock, 0, m_countPerLock.Length);
} finally {
ReleaseLocks (0, locksAcquired);
}
}
///
/// Copies the elements of the to an array of
/// type , starting at the
/// specified array index.
///
/// The one-dimensional array of type
/// that is the destination of the elements copied from the . The array must have zero-based indexing.
/// The zero-based index in at which copying
/// begins.
/// is a null reference
/// (Nothing in Visual Basic).
/// is less than
/// 0.
/// is equal to or greater than
/// the length of the . -or- The number of elements in the source
/// is greater than the available space from to the end of the destination
/// .
void ICollection>.CopyTo (KeyValuePair[] array, int index)
{
if (array == null)
throw new ArgumentNullException ("array");
if (index < 0)
throw new ArgumentOutOfRangeException ("index", GetResource ("ConcurrentDictionary_IndexIsNegative"));
int locksAcquired = 0;
try {
AcquireAllLocks (ref locksAcquired);
int count = 0;
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
throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayNotLargeEnough"));
}
CopyToPairs (array, index);
} finally {
ReleaseLocks (0, locksAcquired);
}
}
///
/// Copies the key and value pairs stored in the to a
/// new array.
///
/// A new array containing a snapshot of key and value pairs copied from the .
public KeyValuePair[] ToArray ()
{
int locksAcquired = 0;
try {
AcquireAllLocks (ref locksAcquired);
int count = 0;
checked {
for (int i = 0; i < m_locks.Length; i++) {
count += m_countPerLock [i];
}
}
KeyValuePair[] array = new KeyValuePair[count];
CopyToPairs (array, 0);
return array;
} finally {
ReleaseLocks (0, locksAcquired);
}
}
///
/// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
///
/// Important: the caller must hold all locks in m_locks before calling CopyToPairs.
///
private void CopyToPairs (KeyValuePair[] 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) {
array [index] = new KeyValuePair (current.m_key, current.m_value);
index++; //this should never flow, CopyToPairs is only called when there's no overflow risk
}
}
}
///
/// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
///
/// Important: the caller must hold all locks in m_locks before calling CopyToEntries.
///
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) {
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
}
}
}
///
/// Copy dictionary contents to an array - shared implementation between ToArray and CopyTo.
///
/// Important: the caller must hold all locks in m_locks before calling CopyToObjects.
///
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) {
array [index] = new KeyValuePair (current.m_key, current.m_value);
index++; //this should never flow, CopyToObjects is only called when there's no overflow risk
}
}
}
/// Returns an enumerator that iterates through the .
/// An enumerator for the .
///
/// The enumerator returned from the dictionary is safe to use concurrently with
/// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot
/// of the dictionary. The contents exposed through the enumerator may contain modifications
/// made to the dictionary after was called.
///
public IEnumerator> GetEnumerator ()
{
Node[] buckets = m_buckets;
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) {
yield return new KeyValuePair (current.m_key, current.m_value);
current = current.m_next;
}
}
}
///
/// Shared internal implementation for inserts and updates.
/// If key exists, we always return false; and if updateIfExists == true we force update with value;
/// If key doesn't exist, we always add value and return true;
///
private bool TryAddInternal (TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue)
{
int hashcode = m_comparer.GetHashCode (key);
while (true) {
int bucketNo, lockNo;
Node[] buckets = m_buckets;
GetBucketAndLockNo (hashcode, out bucketNo, out lockNo, buckets.Length);
bool resizeDesired = false;
bool lockTaken = false;
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) {
continue;
}
// Try to find this key in the bucket
Node prev = null;
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)) {
// 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) {
Node newNode = new Node (node.m_key, value, hashcode, node.m_next);
if (prev == null) {
buckets [bucketNo] = newNode;
} else {
prev.m_next = newNode;
}
resultingValue = value;
} else {
resultingValue = node.m_value;
}
return false;
}
prev = node;
}
// The key was not found in the bucket. Insert the key-value pair.
buckets [bucketNo] = new Node (key, value, hashcode, buckets [bucketNo]);
checked {
m_countPerLock [lockNo]++;
}
//
// If this lock has element / bucket ratio greater than 1, resize the entire table.
// 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) {
resizeDesired = true;
}
} finally {
if (lockTaken)
Monitor.Exit (m_locks [lockNo]);
}
//
// The fact that we got here means that we just performed an insertion. If necessary, we will grow the table.
//
// Concurrency notes:
// - Notice that we are not holding any locks at when calling GrowTable. This is necessary to prevent deadlocks.
// - 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) {
GrowTable (buckets);
}
resultingValue = value;
return true;
}
}
///
/// Gets or sets the value associated with the specified key.
///
/// The key of the value to get or set.
/// The value associated with the specified key. If the specified key is not found, a get
/// operation throws a
/// , and a set operation creates a new
/// element with the specified key.
/// is a null reference
/// (Nothing in Visual Basic).
/// The property is retrieved and
///
/// does not exist in the collection.
public TValue this [TKey key] {
get {
TValue value;
if (!TryGetValue (key, out value)) {
throw new KeyNotFoundException ();
}
return value;
}
set {
if (key == null)
throw new ArgumentNullException ("key");
TValue dummy;
TryAddInternal (key, value, true, true, out dummy);
}
}
///
/// Gets the number of key/value pairs contained in the .
///
/// The dictionary contains too many
/// elements.
/// The number of key/value paris contained in the .
/// Count has snapshot semantics and represents the number of items in the
/// at the moment when Count was accessed.
public int Count {
get {
int count = 0;
int acquiredLocks = 0;
try {
// Acquire all locks
AcquireAllLocks (ref acquiredLocks);
// Compute the count, we allow overflow
for (int i = 0; i < m_countPerLock.Length; i++) {
count += m_countPerLock [i];
}
} finally {
// Release locks that have been acquired earlier
ReleaseLocks (0, acquiredLocks);
}
return count;
}
}
///
/// Adds a key/value pair to the
/// if the key does not already exist.
///
/// The key of the element to add.
/// The function used to generate a value for the key
/// is a null reference
/// (Nothing in Visual Basic).
/// is a null reference
/// (Nothing in Visual Basic).
/// The dictionary contains too many
/// elements.
/// The value for the key. This will be either the existing value for the key if the
/// key is already in the dictionary, or the new value for the key as returned by valueFactory
/// if the key was not in the dictionary.
public TValue GetOrAdd (TKey key, Func valueFactory)
{
if (key == null)
throw new ArgumentNullException ("key");
if (valueFactory == null)
throw new ArgumentNullException ("valueFactory");
TValue resultingValue;
if (TryGetValue (key, out resultingValue)) {
return resultingValue;
}
TryAddInternal (key, valueFactory (key), false, true, out resultingValue);
return resultingValue;
}
///
/// Adds a key/value pair to the
/// if the key does not already exist.
///
/// The key of the element to add.
/// the value to be added, if the key does not already exist
/// is a null reference
/// (Nothing in Visual Basic).
/// The dictionary contains too many
/// elements.
/// The value for the key. This will be either the existing value for the key if the
/// key is already in the dictionary, or the new value if the key was not in the dictionary.
public TValue GetOrAdd (TKey key, TValue value)
{
if (key == null)
throw new ArgumentNullException ("key");
TValue resultingValue;
TryAddInternal (key, value, false, true, out resultingValue);
return resultingValue;
}
///
/// Adds a key/value pair to the if the key does not already
/// exist, or updates a key/value pair in the if the key
/// already exists.
///
/// The key to be added or whose value should be updated
/// The function used to generate a value for an absent key
/// The function used to generate a new value for an existing key
/// based on the key's existing value
/// is a null reference
/// (Nothing in Visual Basic).
/// is a null reference
/// (Nothing in Visual Basic).
/// is a null reference
/// (Nothing in Visual Basic).
/// The dictionary contains too many
/// elements.
/// The new value for the key. This will be either be the result of addValueFactory (if the key was
/// absent) or the result of updateValueFactory (if the key was present).
public TValue AddOrUpdate (TKey key, Func addValueFactory, Func updateValueFactory)
{
if (key == null)
throw new ArgumentNullException ("key");
if (addValueFactory == null)
throw new ArgumentNullException ("addValueFactory");
if (updateValueFactory == null)
throw new ArgumentNullException ("updateValueFactory");
TValue newValue, resultingValue;
while (true) {
TValue oldValue;
if (TryGetValue (key, out oldValue))
{ //key exists, try to update
newValue = updateValueFactory (key, oldValue);
if (TryUpdate (key, newValue, oldValue)) {
return newValue;
}
} else { //try add
newValue = addValueFactory (key);
if (TryAddInternal (key, newValue, false, true, out resultingValue)) {
return resultingValue;
}
}
}
}
///
/// Adds a key/value pair to the if the key does not already
/// exist, or updates a key/value pair in the if the key
/// already exists.
///
/// The key to be added or whose value should be updated
/// The value to be added for an absent key
/// The function used to generate a new value for an existing key based on
/// the key's existing value
/// is a null reference
/// (Nothing in Visual Basic).
/// is a null reference
/// (Nothing in Visual Basic).
/// The dictionary contains too many
/// elements.
/// The new value for the key. This will be either be the result of addValueFactory (if the key was
/// absent) or the result of updateValueFactory (if the key was present).
public TValue AddOrUpdate (TKey key, TValue addValue, Func updateValueFactory)
{
if (key == null)
throw new ArgumentNullException ("key");
if (updateValueFactory == null)
throw new ArgumentNullException ("updateValueFactory");
TValue newValue, resultingValue;
while (true) {
TValue oldValue;
if (TryGetValue (key, out oldValue))
{ //key exists, try to update
newValue = updateValueFactory (key, oldValue);
if (TryUpdate (key, newValue, oldValue)) {
return newValue;
}
} else { //try add
if (TryAddInternal (key, addValue, false, true, out resultingValue)) {
return resultingValue;
}
}
}
}
///
/// Gets a value that indicates whether the is empty.
///
/// true if the is empty; otherwise,
/// false.
public bool IsEmpty {
get {
int acquiredLocks = 0;
try {
// Acquire all locks
AcquireAllLocks (ref acquiredLocks);
for (int i = 0; i < m_countPerLock.Length; i++) {
if (m_countPerLock [i] != 0) {
return false;
}
}
} finally {
// Release locks that have been acquired earlier
ReleaseLocks (0, acquiredLocks);
}
return true;
}
}
#region IDictionary members
///
/// Adds the specified key and value to the .
///
/// The object to use as the key of the element to add.
/// The object to use as the value of the element to add.
/// is a null reference
/// (Nothing in Visual Basic).
/// The dictionary contains too many
/// elements.
///
/// An element with the same key already exists in the .
void IDictionary.Add (TKey key, TValue value)
{
if (!TryAdd (key, value)) {
throw new ArgumentException (GetResource ("ConcurrentDictionary_KeyAlreadyExisted"));
}
}
///
/// Removes the element with the specified key from the .
///
/// The key of the element to remove.
/// true if the element is successfully remove; otherwise false. This method also returns
/// false if
/// was not found in the original .
///
/// is a null reference
/// (Nothing in Visual Basic).
bool IDictionary.Remove (TKey key)
{
TValue throwAwayValue;
return TryRemove (key, out throwAwayValue);
}
///
/// Gets a collection containing the keys in the .
///
/// An containing the keys in the
/// .
public ICollection Keys {
get { return GetKeys (); }
}
///
/// Gets a collection containing the values in the .
///
/// An containing the values in
/// the
/// .
public ICollection Values {
get { return GetValues (); }
}
#endregion
#region ICollection> Members
///
/// Adds the specified value to the
/// with the specified key.
///
/// The
/// structure representing the key and value to add to the .
/// The of is null.
/// The
/// contains too many elements.
/// An element with the same key already exists in the
///
void ICollection>.Add (KeyValuePair keyValuePair)
{
((IDictionary)this).Add (keyValuePair.Key, keyValuePair.Value);
}
///
/// Determines whether the
/// contains a specific key and value.
///
/// The
/// structure to locate in the .
/// true if the is found in the ; otherwise, false.
bool ICollection>.Contains (KeyValuePair keyValuePair)
{
TValue value;
if (!TryGetValue (keyValuePair.Key, out value)) {
return false;
}
return EqualityComparer.Default.Equals (value, keyValuePair.Value);
}
///
/// Gets a value indicating whether the dictionary is read-only.
///
/// true if the is
/// read-only; otherwise, false. For , this property always returns
/// false.
bool ICollection>.IsReadOnly {
get { return false; }
}
///
/// Removes a key and value from the dictionary.
///
/// The
/// structure representing the key and value to remove from the .
/// true if the key and value represented by is successfully
/// found and removed; otherwise, false.
/// The Key property of is a null reference (Nothing in Visual Basic).
bool ICollection>.Remove (KeyValuePair keyValuePair)
{
if (keyValuePair.Key == null)
throw new ArgumentNullException (GetResource ("ConcurrentDictionary_ItemKeyIsNull"));
TValue throwAwayValue;
return TryRemoveInternal (keyValuePair.Key, out throwAwayValue, true, keyValuePair.Value);
}
#endregion
#region IEnumerable Members
/// Returns an enumerator that iterates through the .
/// An enumerator for the .
///
/// The enumerator returned from the dictionary is safe to use concurrently with
/// reads and writes to the dictionary, however it does not represent a moment-in-time snapshot
/// of the dictionary. The contents exposed through the enumerator may contain modifications
/// made to the dictionary after was called.
///
IEnumerator IEnumerable.GetEnumerator ()
{
return ((ConcurrentDictionary)this).GetEnumerator ();
}
#endregion
#region IDictionary Members
///
/// Adds the specified key and value to the dictionary.
///
/// The object to use as the key.
/// The object to use as the value.
/// is a null reference
/// (Nothing in Visual Basic).
/// The dictionary contains too many
/// elements.
///
/// is of a type that is not assignable to the key type of the . -or-
/// is of a type that is not assignable to ,
/// the type of values in the .
/// -or- A value with the same key already exists in the .
///
void IDictionary.Add (object key, object value)
{
if (key == null)
throw new ArgumentNullException ("key");
if (!(key is TKey))
throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfKeyIncorrect"));
TValue typedValue;
try {
typedValue = (TValue)value;
} catch (InvalidCastException) {
throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfValueIncorrect"));
}
((IDictionary)this).Add ((TKey)key, typedValue);
}
///
/// Gets whether the contains an
/// element with the specified key.
///
/// The key to locate in the .
/// true if the contains
/// an element with the specified key; otherwise, false.
/// is a null reference
/// (Nothing in Visual Basic).
bool IDictionary.Contains (object key)
{
if (key == null)
throw new ArgumentNullException ("key");
return (key is TKey) && ((ConcurrentDictionary)this).ContainsKey ((TKey)key);
}
/// Provides an for the
/// .
/// An for the .
IDictionaryEnumerator IDictionary.GetEnumerator ()
{
return new DictionaryEnumerator (this);
}
///
/// Gets a value indicating whether the has a fixed size.
///
/// true if the has a
/// fixed size; otherwise, false. For , this property always
/// returns false.
bool IDictionary.IsFixedSize {
get { return false; }
}
///
/// Gets a value indicating whether the is read-only.
///
/// true if the is
/// read-only; otherwise, false. For , this property always
/// returns false.
bool IDictionary.IsReadOnly {
get { return false; }
}
///
/// Gets an containing the keys of the .
///
/// An containing the keys of the .
ICollection IDictionary.Keys {
get { return GetKeys (); }
}
///
/// Removes the element with the specified key from the .
///
/// The key of the element to remove.
/// is a null reference
/// (Nothing in Visual Basic).
void IDictionary.Remove (object key)
{
if (key == null)
throw new ArgumentNullException ("key");
TValue throwAwayValue;
if (key is TKey) {
this.TryRemove ((TKey)key, out throwAwayValue);
}
}
///
/// Gets an containing the values in the .
///
/// An containing the values in the .
ICollection IDictionary.Values {
get { return GetValues (); }
}
///
/// Gets or sets the value associated with the specified key.
///
/// The key of the value to get or set.
/// The value associated with the specified key, or a null reference (Nothing in Visual Basic)
/// if is not in the dictionary or is of a type that is
/// not assignable to the key type of the .
/// is a null reference
/// (Nothing in Visual Basic).
///
/// A value is being assigned, and is of a type that is not assignable to the
/// key type of the . -or- A value is being
/// assigned, and is of a type that is not assignable to the value type
/// of the
///
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)) {
return value;
}
return null;
}
set {
if (key == null)
throw new ArgumentNullException ("key");
if (!(key is TKey))
throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfKeyIncorrect"));
if (!(value is TValue))
throw new ArgumentException (GetResource ("ConcurrentDictionary_TypeOfValueIncorrect"));
((ConcurrentDictionary)this) [(TKey)key] = (TValue)value;
}
}
#endregion
#region ICollection Members
///
/// Copies the elements of the to an array, starting
/// at the specified array index.
///
/// The one-dimensional array that is the destination of the elements copied from
/// the . The array must have zero-based
/// indexing.
/// The zero-based index in at which copying
/// begins.
/// is a null reference
/// (Nothing in Visual Basic).
/// is less than
/// 0.
/// is equal to or greater than
/// the length of the . -or- The number of elements in the source
/// is greater than the available space from to the end of the destination
/// .
void ICollection.CopyTo (Array array, int index)
{
if (array == null)
throw new ArgumentNullException ("array");
if (index < 0)
throw new ArgumentOutOfRangeException ("index", GetResource ("ConcurrentDictionary_IndexIsNegative"));
int locksAcquired = 0;
try {
AcquireAllLocks (ref locksAcquired);
int count = 0;
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
throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayNotLargeEnough"));
}
// To be consistent with the behavior of ICollection.CopyTo() in Dictionary,
// we recognize three types of target arrays:
// - an array of KeyValuePair structs
// - an array of DictionaryEntry structs
// - an array of objects
KeyValuePair[] pairs = array as KeyValuePair[];
if (pairs != null) {
CopyToPairs (pairs, index);
return;
}
DictionaryEntry[] entries = array as DictionaryEntry[];
if (entries != null) {
CopyToEntries (entries, index);
return;
}
object[] objects = array as object[];
if (objects != null) {
CopyToObjects (objects, index);
return;
}
throw new ArgumentException (GetResource ("ConcurrentDictionary_ArrayIncorrectType"), "array");
} finally {
ReleaseLocks (0, locksAcquired);
}
}
///
/// Gets a value indicating whether access to the is
/// synchronized with the SyncRoot.
///
/// true if access to the is synchronized
/// (thread safe); otherwise, false. For , this property always
/// returns false.
bool ICollection.IsSynchronized {
get { return false; }
}
///
/// Gets an object that can be used to synchronize access to the . This property is not supported.
///
/// The SyncRoot property is not supported.
object ICollection.SyncRoot {
get {
throw new NotSupportedException ();
}
}
#endregion
///
/// Replaces the internal table with a larger one. To prevent multiple threads from resizing the
/// table as a result of ----s, the table of buckets that was deemed too small is passed in as
/// an argument to GrowTable(). GrowTable() obtains a lock, and then checks whether the bucket
/// table has been replaced in the meantime or not.
///
/// Reference to the bucket table that was deemed too small.
private void GrowTable (Node[] buckets)
{
int locksAcquired = 0;
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) {
// 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.
return;
}
// 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 {
// 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) {
newLength += 2;
}
Assert (newLength % 2 != 0);
}
} catch (OverflowException) {
// If we were to resize the table, its new size will not fit into a 32-bit signed int. Just return.
return;
}
Node[] newBuckets = new Node[newLength];
int[] newCountPerLock = new int[m_locks.Length];
// Now acquire all other locks for the table
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++) {
Node current = buckets [i];
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 {
newCountPerLock [newLockNo]++;
}
current = next;
}
}
// And finally adjust m_buckets and m_countPerLock to point to data for the new table
m_buckets = newBuckets;
m_countPerLock = newCountPerLock;
} finally {
// Release all locks that we took earlier
ReleaseLocks (0, locksAcquired);
}
}
///
/// Computes the bucket and lock number for a particular key.
///
private void GetBucketAndLockNo (
int hashcode, out int bucketNo, out int lockNo, int bucketCount)
{
bucketNo = (hashcode & 0x7fffffff) % bucketCount;
lockNo = bucketNo % m_locks.Length;
Assert (bucketNo >= 0 && bucketNo < bucketCount);
Assert (lockNo >= 0 && lockNo < m_locks.Length);
}
///
/// The number of concurrent writes for which to optimize by default.
///
private static int DefaultConcurrencyLevel {
get { return DEFAULT_CONCURRENCY_MULTIPLIER * Environment.ProcessorCount; }
}
///
/// Acquires all locks for this hash table, and increments locksAcquired by the number
/// of locks that were successfully acquired. The locks are acquired in an increasing
/// order.
///
private void AcquireAllLocks (ref int locksAcquired)
{
AcquireLocks (0, m_locks.Length, ref locksAcquired);
Assert (locksAcquired == m_locks.Length);
}
///
/// Acquires a contiguous range of locks for this hash table, and increments locksAcquired
/// by the number of locks that were successfully acquired. The locks are acquired in an
/// increasing order.
///
private void AcquireLocks (int fromInclusive, int toExclusive, ref int locksAcquired)
{
Assert (fromInclusive <= toExclusive);
for (int i = fromInclusive; i < toExclusive; i++) {
bool lockTaken = false;
try {
Monitor.Enter (m_locks [i]);
lockTaken = true;
} finally {
if (lockTaken) {
locksAcquired++;
}
}
}
}
///
/// Releases a contiguous range of locks.
///
private void ReleaseLocks (int fromInclusive, int toExclusive)
{
Assert (fromInclusive <= toExclusive);
for (int i = fromInclusive; i < toExclusive; i++) {
Monitor.Exit (m_locks [i]);
}
}
///
/// Gets a collection containing the keys in the dictionary.
///
private ReadOnlyCollection GetKeys ()
{
int locksAcquired = 0;
try {
AcquireAllLocks (ref locksAcquired);
List keys = new List ();
for (int i = 0; i < m_buckets.Length; i++) {
Node current = m_buckets [i];
while (current != null) {
keys.Add (current.m_key);
current = current.m_next;
}
}
return new ReadOnlyCollection (keys);
} finally {
ReleaseLocks (0, locksAcquired);
}
}
///
/// Gets a collection containing the values in the dictionary.
///
private ReadOnlyCollection GetValues ()
{
int locksAcquired = 0;
try {
AcquireAllLocks (ref locksAcquired);
List values = new List ();
for (int i = 0; i < m_buckets.Length; i++) {
Node current = m_buckets [i];
while (current != null) {
values.Add (current.m_value);
current = current.m_next;
}
}
return new ReadOnlyCollection (values);
} finally {
ReleaseLocks (0, locksAcquired);
}
}
///
/// A helper method for asserts.
///
[Conditional ("DEBUG")]
private void Assert (bool condition)
{
if (!condition) {
throw new Exception ("Assertion failed.");
}
}
///
/// A helper function to obtain the string for a particular resource key.
///
///
///
private string GetResource (string key)
{
Assert (key != null);
return key;
}
///
/// A node in a singly-linked list representing a particular hash table bucket.
///
private class Node
{
internal TKey m_key;
internal TValue m_value;
internal volatile Node m_next;
internal int m_hashcode;
internal Node (TKey key, TValue value, int hashcode)
: this (key, value, hashcode, null)
{
}
internal Node (TKey key, TValue value, int hashcode, Node next)
{
m_key = key;
m_value = value;
m_next = next;
m_hashcode = hashcode;
}
}
///
/// A private class to represent enumeration over the dictionary that implements the
/// IDictionaryEnumerator interface.
///
private class DictionaryEnumerator : IDictionaryEnumerator
{
IEnumerator> m_enumerator;
// Enumerator over the dictionary.
internal DictionaryEnumerator (ConcurrentDictionary dictionary)
{
m_enumerator = dictionary.GetEnumerator ();
}
public DictionaryEntry Entry {
get { return new DictionaryEntry (m_enumerator.Current.Key, m_enumerator.Current.Value); }
}
public object Key {
get { return m_enumerator.Current.Key; }
}
public object Value {
get { return m_enumerator.Current.Value; }
}
public object Current {
get { return this.Entry; }
}
public bool MoveNext ()
{
return m_enumerator.MoveNext ();
}
public void Reset ()
{
m_enumerator.Reset ();
}
}
///
/// Get the data array to be serialized
///
[OnSerializing]
private void OnSerializing (StreamingContext context)
{
// save the data into the serialization array to be saved
m_serializationArray = ToArray ();
m_serializationConcurrencyLevel = m_locks.Length;
m_serializationCapacity = m_buckets.Length;
}
///
/// Construct the dictionary from a previously seiralized one
///
[OnDeserialized]
private void OnDeserialized (StreamingContext context)
{
KeyValuePair[] array = m_serializationArray;
m_buckets = new Node[m_serializationCapacity];
m_countPerLock = new int[m_serializationConcurrencyLevel];
m_locks = new object[m_serializationConcurrencyLevel];
for (int i = 0; i < m_locks.Length; i++) {
m_locks [i] = new object ();
}
InitializeFromCollection (array);
m_serializationArray = null;
}
}
}
#endif