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