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]); }
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(); } }
/// <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); }