Example #1
0
            public AsyncLock GetLock(TK key)
            {
                if (key == null)
                {
                    throw new ArgumentNullException(nameof(key));
                }
                int index = Math.Abs(key.GetHashCode() % this.keyShardCount);

                return(this.locks[index]);
            }
Example #2
0
            public bool TryGetValue(TK key, out TV value)
            {
                var hash = key.GetHashCode();

                // First, try the lock-free approach. It avoids locks when no writes are
                // underway and all readers (possibly more than one) are hitting close to the
                // current root (thus requiring no flattening).

                Interlocked.MemoryBarrier();
                var oldToken = Data.FastLock;

                Interlocked.MemoryBarrier();

                // An even lock token means no structure update is underway
                if (oldToken % 2 == 0)
                {
                    // Are we on the root, or close enough ?
                    var node  = this;
                    var isset = false;
                    var found = false;

                    value = default(TV);

                    for (var n = 0; !found && node.Parent != null && n < MaxSearchDepth; ++n)
                    {
                        if (node.Hash == hash && node.SetKey.Equals(key))
                        {
                            value = node.SetValue;
                            isset = !node.UnsetValue;
                            found = true;
                        }

                        node = node.Parent;
                    }

                    if (!found && node.Parent == null)
                    {
                        found = true;
                        var entry = Data.FindEntry(hash, key);
                        isset = entry >= 0;
                        if (isset)
                        {
                            value = Data.Entries[entry].Value;
                        }
                    }

                    if (found)
                    {
                        Interlocked.MemoryBarrier();
                        var newToken = Data.FastLock;
                        Interlocked.MemoryBarrier();

                        // No changes occurred while we were reading, so just return the value.
                        if (oldToken == newToken)
                        {
                            return(isset);
                        }
                    }
                }

                // Our optimistic attempt failed (either because someone tried a
                // write, or the value was too deep).
                Data.HardLock?.Wait();
                try
                {
                    Flatten();

                    var entry = Data.FindEntry(hash, key);
                    value = entry >= 0 ? Data.Entries[entry].Value : default(TV);
                    return(entry >= 0);
                }
                finally
                {
                    Data.HardLock?.Release();
                }
            }
Example #3
0
            /// <summary> Bind a value to a key, throw if adding and the key already exists. </summary>
            public Node SetItem(TK key, TV value, bool isAdd = false)
            {
                var node = new Node(Data);

                var hash = key.GetHashCode();

                var lockAcquired = Data.HardLock == null || Data.HardLock.Wait(0);

                if (lockAcquired || isAdd)
                {
                    // 'isAdd' requires that we check whether the key already exists, so we
                    // accept the performance hit of waiting for the lock.
                    if (!lockAcquired)
                    {
                        Data.HardLock.Wait();
                    }

                    // We have a lock, so just mutate the underlying dictionary
                    try
                    {
                        Flatten();

                        var entry  = Data.FindEntry(hash, key);
                        var exists = entry >= 0;
                        if (exists && isAdd)
                        {
                            throw new InvalidOperationException("Cannot add, key already exists in dictionary");
                        }

                        // Not enough data...
                        if (!exists && Data.Free < 0)
                        {
                            return(Enlarge().SetItem(key, value, isAdd));
                        }

                        var current = exists ? Data.Entries[entry].Value : default(TV);

                        Interlocked.Increment(ref Data.FastLock);

                        Parent     = node;
                        SetKey     = key;
                        SetValue   = current;
                        UnsetValue = !exists;

                        if (exists)
                        {
                            Data.Entries[entry].Value = value;
                        }
                        else
                        {
                            Data.CreateEntry(hash, key, value);
                        }

                        Interlocked.Increment(ref Data.FastLock);
                    }
                    finally
                    {
                        Data.HardLock?.Release();
                    }
                }
                else
                {
                    // The lock was unavailable, so create a pending modification
                    // without touching the source.
                    node.Parent   = this;
                    node.Hash     = hash;
                    node.SetKey   = key;
                    node.SetValue = value;
                }

                return(node);
            }