예제 #1
0
파일: UnitTest1.cs 프로젝트: Oprych22/Match
        public void TestValueMatch()
        {
            var valueMatch = new ValueMatch();

            Assert.IsTrue(valueMatch.IsMatch(_card1, _card3));
            Assert.IsFalse(valueMatch.IsMatch(_card2, _card3));
        }
예제 #2
0
 internal abstract bool PutIfMatch(TKey key, object newVal, ref object oldValue, ValueMatch match);
예제 #3
0
 internal abstract bool RemoveIfMatch(TKey key, ref TValue oldValue, ValueMatch match);
예제 #4
0
        // 1) finds or creates a slot for the key
        // 2) sets the slot value to the putval if original value meets expVal condition
        // 3) returns true if the value was actually changed
        // Note that pre-existence of the slot is irrelevant
        // since slot without a value is as good as no slot at all
        internal sealed override bool PutIfMatch(TKey key, object newVal, ref object oldVal, ValueMatch match)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            var curTable = this;
            int fullHash = curTable.hash(key);

TRY_WITH_NEW_TABLE:

            Debug.Assert(newVal != null);
            Debug.Assert(!(newVal is Prime));

            var curEntries = curTable._entries;
            int lenMask    = curEntries.Length - 1;
            int idx        = ReduceHashToIndex(fullHash, lenMask);

            // Spin till we get a slot for the key or force a resizing.
            int reprobeCnt = 0;

            while (true)
            {
                // hash, key and value are all CAS-ed down and follow a specific sequence of states.
                // hence the order of their reads is irrelevant and they do not need to be volatile
                var entryHash = curEntries[idx].hash;
                if (entryHash == 0)
                {
                    // Found an unassigned slot - which means this
                    // key has never been in this table.
                    if (newVal == TOMBSTONE)
                    {
                        Debug.Assert(match == ValueMatch.NotNullOrDead || match == ValueMatch.OldValue);
                        oldVal = null;
                        goto FAILED;
                    }
                    else
                    {
                        // Slot is completely clean, claim the hash first
                        Debug.Assert(fullHash != 0);
                        entryHash = Interlocked.CompareExchange(ref curEntries[idx].hash, fullHash, 0);
                        if (entryHash == 0)
                        {
                            entryHash = fullHash;
                            if (entryHash == ZEROHASH)
                            {
                                // "added" entry for zero key
                                curTable.allocatedSlotCount.Increment();
                                break;
                            }
                        }
                    }
                }

                if (entryHash == fullHash)
                {
                    // hash is good, one way or another,
                    // try claiming the slot for the key
                    if (curTable.TryClaimSlotForPut(ref curEntries[idx].key, key))
                    {
                        break;
                    }
                }

                // here we know that this slot does not map to our key
                // and must reprobe or resize
                // hitting reprobe limit or finding TOMBPRIMEHASH here means that the key is not in this table,
                // but there could be more in the new table
                if (++reprobeCnt >= ReprobeLimit(lenMask) |
                    entryHash == TOMBPRIMEHASH)
                {
                    // start resize or get new table if resize is already in progress
                    var newTable1 = curTable.Resize();
                    // help along an existing copy
                    curTable.HelpCopy();
                    curTable = newTable1;
                    goto TRY_WITH_NEW_TABLE;
                }

                curTable.ReprobeResizeCheck(reprobeCnt, lenMask);

                // quadratic reprobing
                idx = (idx + reprobeCnt) & lenMask;
            }

            // Found the proper Key slot, now update the Value.
            // We never put a null, so Value slots monotonically move from null to
            // not-null (deleted Values use Tombstone).

            // volatile read to make sure we read the element before we read the _newTable
            // that would guarantee that as long as _newTable == null, entryValue cannot be a Prime.
            var entryValue = Volatile.Read(ref curEntries[idx].value);

            // See if we want to move to a new table (to avoid high average re-probe counts).
            // We only check on the initial set of a Value from null to
            // not-null (i.e., once per key-insert).
            var newTable = curTable._newTable;

            // newTable == entryValue only when both are nulls
            if ((object)newTable == (object)entryValue &&
                curTable.TableIsCrowded(lenMask))
            {
                // Force the new table copy to start
                newTable = curTable.Resize();
                Debug.Assert(curTable._newTable != null && newTable == curTable._newTable);
            }

            // See if we are moving to a new table.
            // If so, copy our slot and retry in the new table.
            if (newTable != null)
            {
                var newTable1 = curTable.CopySlotAndGetNewTable(idx, shouldHelp: true);
                Debug.Assert(newTable == newTable1);
                curTable = newTable;
                goto TRY_WITH_NEW_TABLE;
            }

            // We are finally prepared to update the existing table
            while (true)
            {
                Debug.Assert(!(entryValue is Prime));
                var entryValueNullOrDead = EntryValueNullOrDead(entryValue);

                switch (match)
                {
                case ValueMatch.Any:
                    if (newVal == entryValue)
                    {
                        // Do not update!
                        goto FAILED;
                    }
                    break;

                case ValueMatch.NullOrDead:
                    if (entryValueNullOrDead)
                    {
                        break;
                    }

                    oldVal = entryValue;
                    goto FAILED;

                case ValueMatch.NotNullOrDead:
                    if (entryValueNullOrDead)
                    {
                        goto FAILED;
                    }
                    break;

                case ValueMatch.OldValue:
                    Debug.Assert(oldVal != null);
                    if (!oldVal.Equals(entryValue))
                    {
                        oldVal = entryValue;
                        goto FAILED;
                    }
                    break;
                }

                // Actually change the Value
                var prev = Interlocked.CompareExchange(ref curEntries[idx].value, newVal, entryValue);
                if (prev == entryValue)
                {
                    // CAS succeeded - we did the update!
                    // Adjust sizes
                    if (entryValueNullOrDead)
                    {
                        oldVal = null;
                        if (newVal != TOMBSTONE)
                        {
                            curTable._size.Increment();
                        }
                    }
                    else
                    {
                        oldVal = prev;
                        if (newVal == TOMBSTONE)
                        {
                            curTable._size.Decrement();
                        }
                    }

                    return(true);
                }
                // Else CAS failed

                // If a Prime'd value got installed, we need to re-run the put on the new table.
                if (prev is Prime)
                {
                    curTable = curTable.CopySlotAndGetNewTable(idx, shouldHelp: true);
                    goto TRY_WITH_NEW_TABLE;
                }

                // Otherwise we lost the CAS to another racing put.
                // Simply retry from the start.
                entryValue = prev;
            }

FAILED:
            return(false);
        }
예제 #5
0
 internal abstract bool PutIfMatch(TKey key, TValue newVal, ref TValue oldValue, ValueMatch match);