public bool TryUpdate(TKey key, TValue value, TValue comparisonValue) { var oldValObj = DictionaryImpl.ToObjectValue(comparisonValue); var newValObj = DictionaryImpl.ToObjectValue(value); return(_table.PutIfMatch(key, newValObj, ref oldValObj, DictionaryImpl.ValueMatch.OldValue)); }
public bool Remove(KeyValuePair <TKey, TValue> item) { var oldValObj = DictionaryImpl.ToObjectValue(item.Value); return(_table.PutIfMatch(item.Key, DictionaryImpl.TOMBSTONE, ref oldValObj, DictionaryImpl.ValueMatch.OldValue)); }
public bool TryAdd(TKey key, TValue value) { object oldValObj = null; var newValObj = DictionaryImpl.ToObjectValue(value); return(_table.PutIfMatch(key, newValObj, ref oldValObj, DictionaryImpl.ValueMatch.NullOrDead)); }
protected DictionaryImpl(int capacity, DictionaryImpl <TKey, TKeyStore, TValue, TComparer> other) { capacity = AlignToPowerOfTwo(capacity); _entries = new Entry[capacity]; _size = other._size; _topDict = other._topDict; }
public TValue GetOrAdd(TKey key, TValue value) { object oldValObj = null; var newValObj = DictionaryImpl.ToObjectValue(value); if (_table.PutIfMatch(key, newValObj, ref oldValObj, DictionaryImpl.ValueMatch.NullOrDead)) { return(value); } // PERF: this would be nice to have as a helper, // but it does not get inlined if (default(TValue) == null && oldValObj == DictionaryImpl.NULLVALUE) { oldValObj = null; } return((TValue)oldValObj); }
// System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue> /// <param name="capacity"> /// The initial number of elements that the /// <see cref="T:System.Collections.Concurrent.ConcurrentDictionary`2" /> can contain. /// </param> /// <summary> /// Initializes a new instance of the <see cref="T:System.Collections.Concurrent.ConcurrentDictionary`2" /> class /// that is empty, has the default concurrency level and uses the default comparer for the key type. /// </summary> public ConcurrentDictionary(int capacity) { if (capacity < 0) { throw new ArgumentOutOfRangeException(nameof(capacity)); } if (default(TKey) == null) { if (typeof(TKey) == typeof(ValueType) || !(default(TKey) is ValueType)) { _table = DictionaryImpl <TKey, TValue, TComparer> .CreateRefUnsafe(this, capacity); return; } } else { if (typeof(TKey) == typeof(uint) || typeof(TKey) == typeof(ulong)) { throw new NotSupportedException( "Unsupported until we have confirmation of how to by-pass the code-gen issue with the casting of Boxed<TKey>. Use int or long instead."); } if (typeof(TKey) == typeof(int)) { _table = DictionaryImpl <TKey, TValue, TComparer> .CreateIntUnsafe(this, capacity); return; } if (typeof(TKey) == typeof(long)) { _table = DictionaryImpl <TKey, TValue, TComparer> .CreateLongUnsafe(this, capacity); return; } } _table = new DictionaryImplBoxed <TKey, TValue, TComparer>(capacity, this); }
public Snapshot(DictionaryImpl <TKey, TKeyStore, TValue, TComparer> dict) { _table = dict; // linearization point. // if table is quiescent and has no copy in progress, // we can simply iterate over its table. while (true) { if (_table._newTable == null) { break; } // there is a copy in progress, finish it and try again _table.HelpCopyImpl(true); _table = (DictionaryImpl <TKey, TKeyStore, TValue, TComparer>)_table._topDict._table; } // Warm-up the iterator MoveNext(); }
public TValue this[TKey key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { var oldValObj = _table.TryGetValue(key); Debug.Assert(!(oldValObj is DictionaryImpl.Prime)); if (oldValObj != null) { // PERF: this would be nice to have as a helper, // but it does not get inlined TValue value; if (default(TValue) == null && oldValObj == DictionaryImpl.NULLVALUE) { value = default(TValue); } else { value = (TValue)oldValObj; } return(value); } return(ThrowKeyNotFound()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { object oldValObj = null; var newValObj = DictionaryImpl.ToObjectValue(value); _table.PutIfMatch(key, newValObj, ref oldValObj, DictionaryImpl.ValueMatch.Any); } }
public SnapshotKV(DictionaryImpl <TKey, TKeyStore, TValue, TComparer> dict) : base(dict) { }
private static bool CopySlot(ref Entry oldEntry, DictionaryImpl <TKey, TKeyStore, TValue, TComparer> newTable) { Debug.Assert(newTable != null); // Blindly set the hash from 0 to TOMBPRIMEHASH, to eagerly stop // fresh put's from claiming new slots in the old table when the old // table is mid-resize. var hash = oldEntry.hash; if (hash == 0) { hash = Interlocked.CompareExchange(ref oldEntry.hash, TOMBPRIMEHASH, 0); if (hash == 0) { return(true); } } if (hash == TOMBPRIMEHASH) { return(false); } // Prevent new values from appearing in the old table. // Box what we see in the old table, to prevent further updates. // NOTE: Read of the value below must happen before reading of the key, // however this read does not need to be volatile since we will have // some fences in between reads. var oldval = oldEntry.value; // already boxed? var box = oldval as Prime; if (box != null) { Volatile.Read(ref box.originalValue); } else { do { box = EntryValueNullOrDead(oldval) ? TOMBPRIME : new Prime(oldval); // CAS down a box'd version of oldval // also works as a complete fence between reading the value and the key var prev = Interlocked.CompareExchange(ref oldEntry.value, box, oldval); if (prev == oldval) { // If we made the Value slot hold a TOMBPRIME, then we both // prevented further updates here but also the (absent) // oldval is vacuously available in the new table. We // return with true here: any thread looking for a value for // this key can correctly go straight to the new table and // skip looking in the old table. if (box == TOMBPRIME) { return(true); } // Break loop; oldval is now boxed by us // it still needs to be copied into the new table. break; } oldval = prev; box = oldval as Prime; } while (box == null); } if (box == TOMBPRIME) { return(false); } // Copy the value into the new table, but only if we overwrite a null. // If another value is already in the new table, then somebody else // wrote something there and that write is happens-after any value that // appears in the old table. If putIfMatch does not find a null in the // new table - somebody else should have recorded the null-not_null // transition in this copy. var originalValue = box.originalValue; Debug.Assert(originalValue != TOMBSTONE); // since we have a real value, there must be a nontrivial key in the table // regular read is ok because value is always CASed down after the key // and we ensured that we read the key after the value with fences above var key = oldEntry.key; var copiedIntoNew = newTable.PutSlotCopy(key, originalValue, hash); // Finally, now that any old value is exposed in the new table, we can // forever hide the old-table value by gently inserting TOMBPRIME value. // This will stop other threads from uselessly attempting to copy this slot // (i.e., it's a speed optimization not a correctness issue). // Check if we are not too late though, to not pay for MESI RFO and // GC fence needlessly. if (oldEntry.value != TOMBPRIME) { oldEntry.value = TOMBPRIME; } // if we failed to copy, it means something has already appeared in // the new table and old value should have been copied before that (not by us). return(copiedIntoNew); }