Esempio n. 1
0
 protected DictionaryImpl(int capacity, DictionaryImpl <TKey, TKeyStore, TValue> other)
 {
     capacity          = AlignToPowerOfTwo(capacity);
     this._entries     = new Entry[capacity];
     this._size        = other._size;
     this._topDict     = other._topDict;
     this._keyComparer = other._keyComparer;
 }
Esempio n. 2
0
            public Snapshot(DictionaryImpl <TKey, TKeyStore, TValue> dict)
            {
                this._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(copy_all: true);
                    this._table = (DictionaryImpl <TKey, TKeyStore, TValue>)(this._table._topDict._table);
                }

                // Warm-up the iterator
                MoveNext();
            }
Esempio n. 3
0
 public SnapshotIDict(DictionaryImpl <TKey, TKeyStore, TValue> dict) : base(dict)
 {
 }
Esempio n. 4
0
        // Copy one K/V pair from old table to new table.
        // Returns true if we actually did the copy.
        // Regardless, once this returns, the copy is available in the new table and
        // slot in the old table is no longer usable.
        private static bool CopySlot(ref Entry oldEntry, DictionaryImpl <TKey, TKeyStore, TValue> 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)
                {
                    // slot was not claimed, copy is done here
                    return(true);
                }
            }

            if (hash == TOMBPRIMEHASH)
            {
                // slot was trivially copied, but not by us
                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.
            object oldval = oldEntry.value;

            // already boxed?
            Prime box = oldval as Prime;

            if (box != null)
            {
                // volatile read here since we need to make sure
                // that the key read below happens after we have read oldval above
                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
                    object 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)
            {
                // Copy already complete here, but not by us.
                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.
            object 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;
            bool 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);
        }