// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace FASTER.core { /// /// A dictionary that supports concurrency with similar interface to .NET's ConcurrentDictionary. /// However, this dictionary changes the implementation of AddOrUpdate and GetOrAdd functions to /// guarantee atomicity per-key for factory lambdas. /// /// Type of keys in the dictionary /// Type of values in the dictionary internal sealed class SafeConcurrentDictionary : IEnumerable> { private readonly ConcurrentDictionary dictionary = new ConcurrentDictionary(); private readonly ConcurrentDictionary keyLocks = new ConcurrentDictionary(); /// /// Returns the count of the dictionary. /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return dictionary.Count; } } /// /// Returns whether or not the dictionary is empty. /// public bool IsEmpty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return dictionary.IsEmpty; } } /// /// Gets or sets the value associated with a key. /// public TValue this[TKey key] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return dictionary[key]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { dictionary[key] = value; } } /// /// Returns a collection of the keys in the dictionary. /// public ICollection Keys { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return dictionary.Keys; } } /// /// Returns a collection of the values in the dictionary. /// public ICollection Values { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return dictionary.Values; } } /// /// Adds or updates a key/value pair to the dictionary. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue AddOrUpdate(TKey key, Func addValueFactory, Func updateValueFactory) { lock (GetLock(key)) { return dictionary.AddOrUpdate(key, addValueFactory, updateValueFactory); } } /// /// Adds or updates a key/value pair to the dictionary. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue AddOrUpdate(TKey key, TValue addValue, Func updateValueFactory) { lock (GetLock(key)) { return dictionary.AddOrUpdate(key, addValue, updateValueFactory); } } /// /// Adds a key/value pair to the dictionary if it does not exist. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue GetOrAdd(TKey key, Func valueFactory) { if (dictionary.TryGetValue(key, out TValue value)) { return value; } lock (GetLock(key)) { return dictionary.GetOrAdd(key, valueFactory); } } /// /// Adds a key/value pair to the dictionary if it does not exist. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue GetOrAdd(TKey key, TValue value) { return dictionary.GetOrAdd(key, value); } /// /// Clears the dictionary. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { dictionary.Clear(); keyLocks.Clear(); } /// /// Returns whether or not the dictionary contains the specified key. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); } /// /// Returns an enumerator of the elements in the dictionary. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public IEnumerator> GetEnumerator() { return dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// Copies the key/value pairs to a new array. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyValuePair[] ToArray() { return dictionary.ToArray(); } /// /// Attempts to add the specified key/value to the dictionary if it does not exist. /// Returns true or false depending on if the value was added or not, respectively. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryAdd(TKey key, TValue value) { return dictionary.TryAdd(key, value); } /// /// Attempts to get the value for the specified key. /// Returns true if the key was in the dictionary or false otherwise. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryGetValue(TKey key, out TValue value) { return dictionary.TryGetValue(key, out value); } /// /// Attempts to remove the value for the specified key. /// Returns true if the key was in the dictionary or false otherwise. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryRemove(TKey key, out TValue value) { return dictionary.TryRemove(key, out value); } /// /// Compares the existing value for the specified key with a specified value, /// and updates it if and only if it is a match. Returns true is updated or /// false otherwise. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) { return dictionary.TryUpdate(key, newValue, comparisonValue); } /// /// Retrieves lock associated with a key (creating it if it does not exist). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private object GetLock(TKey key) { return keyLocks.GetOrAdd(key, v => new object()); } } }