Beispiel #1
0
        internal async Task <bool> SetOrAddUnchecked(TKey key, TValue value, bool overwrite)
        {
            // TODO happy path when adding last without TryFindAt, we should keep track of last bucket and || on the last condition
            if (!(_prevWBucket is null) &&
                comparer.Compare(key, _prevWHash) >= 0 &&
                (_prevWBucket.CompareToLast(key) <= 0)
                )
            {
                // We are inside previous bucket, setter has no choice but to set to this
                // bucket regardless of its size

                // We know that SM is synchronous
                var res = _prevWBucket.SetOrAdd(key, value, overwrite).Result;
                NotifyUpdate(false);
                return(res);
            }

            if (outerMap.TryFindAt(key, Lookup.LE, out var kvp)
                // the second condition here is for the case when we add inside existing bucket, overflow above chunkUpperLimit is inevitable without a separate split logic
                && (kvp.Value.size < _chunkUpperLimit || kvp.Value.CompareToLast(key) <= 0)
                )
            {
                if (comparer.Compare(_prevWHash, kvp.Key) != 0)
                {
                    // switched active bucket
                    await FlushUnchecked();

                    // if add fails later, it is ok to update the stale version to this._version
                    kvp.Value._version     = _version;
                    kvp.Value._nextVersion = _version;
                    _prevWHash             = kvp.Key;
                    _prevWBucket           = kvp.Value;
                }

                Debug.Assert(kvp.Value._version == _version);
                var res = await kvp.Value.SetOrAdd(key, value, overwrite);

                NotifyUpdate(false);
                return(res);
            }
            else
            {
                if (!(_prevWBucket is null))
                {
                    await FlushUnchecked();
                }
                // create a new bucket at key
                var newSm = innerFactory.Invoke(0, comparer);
                newSm._version     = _version;
                newSm._nextVersion = _version;
                // Set and Add are the same here, we use new SM
                var _ = await newSm.SetOrAdd(key, value, overwrite); // we know that SM is syncronous

                var outerSet =
                    await outerMap.Set(key,
                                       newSm); // outerMap.Version is incremented here, set non-empty bucket only

                _prevWHash   = key;
                _prevWBucket = newSm;
                NotifyUpdate(false);
                return(outerSet);
            }
        }