/// <summary> /// Mutates a value in-place with optimistic locking transaction semantics via a specified transformation /// function. The transformation is retried as many times as necessary to win the optimistic locking race. /// </summary> /// <typeparam name="TKey">The type of key stored by the dictionary.</typeparam> /// <typeparam name="TValue">The type of value stored by the dictionary.</typeparam> /// <typeparam name="TArg">The type of argument passed to the <paramref name="transformer"/>.</typeparam> /// <param name="location"> /// The variable or field to be changed, which may be accessed by multiple threads. /// </param> /// <param name="transformer"> /// A function that mutates the value. This function should be side-effect free, as it may run multiple times /// when races occur with other threads.</param> /// <param name="transformerArgument">The argument to pass to <paramref name="transformer"/>.</param> /// <returns> /// <see langword="true"/> if the location's value is changed by applying the result of the /// <paramref name="transformer"/> function; otherwise, <see langword="false"/> if the location's value remained /// the same because the last invocation of <paramref name="transformer"/> returned the existing value. /// </returns> public static bool Update <TKey, TValue, TArg>(ref ImmutableSegmentedDictionary <TKey, TValue> location, Func <ImmutableSegmentedDictionary <TKey, TValue>, TArg, ImmutableSegmentedDictionary <TKey, TValue> > transformer, TArg transformerArgument) where TKey : notnull { if (transformer is null) { throw new ArgumentNullException(nameof(transformer)); } var oldValue = ImmutableSegmentedDictionary <TKey, TValue> .PrivateInterlocked.VolatileRead(in location); while (true) { var newValue = transformer(oldValue, transformerArgument); if (oldValue == newValue) { // No change was actually required. return(false); } var interlockedResult = InterlockedCompareExchange(ref location, newValue, oldValue); if (oldValue == interlockedResult) { return(true); } oldValue = interlockedResult; // we already have a volatile read that we can reuse for the next loop } }
/// <inheritdoc cref="ImmutableInterlocked.TryUpdate{TKey, TValue}(ref ImmutableDictionary{TKey, TValue}, TKey, TValue, TValue)"/> public static bool TryUpdate <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, TKey key, TValue newValue, TValue comparisonValue) where TKey : notnull { var valueComparer = EqualityComparer <TValue> .Default; var priorCollection = ImmutableSegmentedDictionary <TKey, TValue> .PrivateInterlocked.VolatileRead(in location); while (true) { if (priorCollection.IsDefault) { throw new ArgumentNullException(nameof(location)); } if (!priorCollection.TryGetValue(key, out var priorValue) || !valueComparer.Equals(priorValue, comparisonValue)) { // The key isn't in the dictionary, or its current value doesn't match what the caller expected. return(false); } var updatedCollection = priorCollection.SetItem(key, newValue); var interlockedResult = InterlockedCompareExchange(ref location, updatedCollection, priorCollection); if (priorCollection == interlockedResult) { return(true); } priorCollection = interlockedResult; // we already have a volatile read that we can reuse for the next loop } }
/// <inheritdoc cref="ImmutableInterlocked.TryRemove{TKey, TValue}(ref ImmutableDictionary{TKey, TValue}, TKey, out TValue)"/> public static bool TryRemove <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, TKey key, [MaybeNullWhen(false)] out TValue value) where TKey : notnull { var priorCollection = ImmutableSegmentedDictionary <TKey, TValue> .PrivateInterlocked.VolatileRead(in location); while (true) { if (priorCollection.IsDefault) { throw new ArgumentNullException(nameof(location)); } if (!priorCollection.TryGetValue(key, out value)) { return(false); } var updatedCollection = priorCollection.Remove(key); var interlockedResult = InterlockedCompareExchange(ref location, updatedCollection, priorCollection); if (priorCollection == interlockedResult) { return(true); } priorCollection = interlockedResult; // we already have a volatile read that we can reuse for the next loop } }
/// <inheritdoc cref="ImmutableInterlocked.GetOrAdd{TKey, TValue}(ref ImmutableDictionary{TKey, TValue}, TKey, TValue)"/> public static TValue GetOrAdd <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, TKey key, TValue value) where TKey : notnull { var priorCollection = ImmutableSegmentedDictionary <TKey, TValue> .PrivateInterlocked.VolatileRead(in location); while (true) { if (priorCollection.IsDefault) { throw new ArgumentNullException(nameof(location)); } if (priorCollection.TryGetValue(key, out var oldValue)) { return(oldValue); } var updatedCollection = priorCollection.Add(key, value); var interlockedResult = InterlockedCompareExchange(ref location, updatedCollection, priorCollection); if (priorCollection == interlockedResult) { // We won the race-condition and have updated the collection. // Return the value that is in the collection (as of the Interlocked operation). return(value); } priorCollection = interlockedResult; // we already have a volatile read that we can reuse for the next loop } }
/// <inheritdoc cref="ImmutableInterlocked.AddOrUpdate{TKey, TValue}(ref ImmutableDictionary{TKey, TValue}, TKey, Func{TKey, TValue}, Func{TKey, TValue, TValue})"/> public static TValue AddOrUpdate <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, TKey key, Func <TKey, TValue> addValueFactory, Func <TKey, TValue, TValue> updateValueFactory) where TKey : notnull { if (addValueFactory is null) { throw new ArgumentNullException(nameof(addValueFactory)); } if (updateValueFactory is null) { throw new ArgumentNullException(nameof(updateValueFactory)); } TValue newValue; var priorCollection = ImmutableSegmentedDictionary <TKey, TValue> .PrivateInterlocked.VolatileRead(in location); while (true) { if (priorCollection.IsDefault) { throw new ArgumentNullException(nameof(location)); } if (priorCollection.TryGetValue(key, out var oldValue)) { newValue = updateValueFactory(key, oldValue); } else { newValue = addValueFactory(key); } var updatedCollection = priorCollection.SetItem(key, newValue); var interlockedResult = InterlockedCompareExchange(ref location, updatedCollection, priorCollection); if (priorCollection == interlockedResult) { // We won the race-condition and have updated the collection. // Return the value that is in the collection (as of the Interlocked operation). return(newValue); } priorCollection = interlockedResult; // we already have a volatile read that we can reuse for the next loop } }
/// <inheritdoc cref="ImmutableInterlocked.GetOrAdd{TKey, TValue}(ref ImmutableDictionary{TKey, TValue}, TKey, Func{TKey, TValue})"/> public static TValue GetOrAdd <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, TKey key, Func <TKey, TValue> valueFactory) where TKey : notnull { if (valueFactory is null) { throw new ArgumentNullException(nameof(valueFactory)); } var map = ImmutableSegmentedDictionary <TKey, TValue> .PrivateInterlocked.VolatileRead(in location); if (map.IsDefault) { throw new ArgumentNullException(nameof(location)); } if (map.TryGetValue(key, out var value)) { return(value); } value = valueFactory(key); return(GetOrAdd(ref location, key, value)); }
internal KeyCollection(ImmutableSegmentedDictionary <TKey, TValue> dictionary) { _dictionary = dictionary; }
internal KeyCollection(ImmutableSegmentedDictionary <TKey, TValue> .Builder dictionary) { LorettaDebug.Assert(dictionary is not null); _dictionary = dictionary !; }
internal Builder(ImmutableSegmentedDictionary <TKey, TValue> dictionary) { _dictionary = dictionary; }
internal Enumerator(ImmutableSegmentedDictionary <TKey, TValue> .Enumerator enumerator) { _enumerator = enumerator; }
public static ImmutableSegmentedDictionary <TKey, TValue> ToImmutableSegmentedDictionary <TKey, TValue>(this ImmutableSegmentedDictionary <TKey, TValue> .Builder builder) where TKey : notnull { if (builder is null) { throw new ArgumentNullException(nameof(builder)); } return(builder.ToImmutable()); }
/// <summary> /// Mutates a value in-place with optimistic locking transaction semantics via a specified transformation /// function. The transformation is retried as many times as necessary to win the optimistic locking race. /// </summary> /// <typeparam name="TKey">The type of key stored by the dictionary.</typeparam> /// <typeparam name="TValue">The type of value stored by the dictionary.</typeparam> /// <param name="location"> /// The variable or field to be changed, which may be accessed by multiple threads. /// </param> /// <param name="transformer"> /// A function that mutates the value. This function should be side-effect free, /// as it may run multiple times when races occur with other threads.</param> /// <returns> /// <see langword="true"/> if the location's value is changed by applying the result of the /// <paramref name="transformer"/> function; otherwise, <see langword="false"/> if the location's value remained /// the same because the last invocation of <paramref name="transformer"/> returned the existing value. /// </returns> public static bool Update <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, Func <ImmutableSegmentedDictionary <TKey, TValue>, ImmutableSegmentedDictionary <TKey, TValue> > transformer !!)
internal ValueCollection(ImmutableSegmentedDictionary <TKey, TValue> .Builder dictionary) { RoslynDebug.Assert(dictionary is not null); _dictionary = dictionary !; }
/// <summary> /// Assigns a field or variable containing an immutable dictionary to the specified value if it is has not yet /// been initialized. /// </summary> /// <typeparam name="TKey">The type of key stored by the dictionary.</typeparam> /// <typeparam name="TValue">The type of value stored by the dictionary.</typeparam> /// <param name="location">The field or local variable to change.</param> /// <param name="value">The new value to assign.</param> /// <returns><see langword="true"/> if the field was assigned the specified value; otherwise, /// <see langword="false"/> if it was previously initialized.</returns> public static bool InterlockedInitialize <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, ImmutableSegmentedDictionary <TKey, TValue> value) where TKey : notnull { return(InterlockedCompareExchange(ref location, value, default(ImmutableSegmentedDictionary <TKey, TValue>)).IsDefault); }
/// <summary> /// Assigns a field or variable containing an immutable dictionary to the specified value if it is currently /// equal to another specified value. Returns the previous value. /// </summary> /// <typeparam name="TKey">The type of key stored by the dictionary.</typeparam> /// <typeparam name="TValue">The type of value stored by the dictionary.</typeparam> /// <param name="location">The field or local variable to change.</param> /// <param name="value">The new value to assign.</param> /// <param name="comparand">The value to check equality for before assigning.</param> /// <returns>The prior value at the specified <paramref name="location"/>.</returns> public static ImmutableSegmentedDictionary <TKey, TValue> InterlockedCompareExchange <TKey, TValue>(ref ImmutableSegmentedDictionary <TKey, TValue> location, ImmutableSegmentedDictionary <TKey, TValue> value, ImmutableSegmentedDictionary <TKey, TValue> comparand) where TKey : notnull { return(ImmutableSegmentedDictionary <TKey, TValue> .PrivateInterlocked.InterlockedCompareExchange(ref location, value, comparand)); }