/// <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));
        }
Example #7
0
 internal KeyCollection(ImmutableSegmentedDictionary <TKey, TValue> dictionary)
 {
     _dictionary = dictionary;
 }
Example #8
0
 internal KeyCollection(ImmutableSegmentedDictionary <TKey, TValue> .Builder dictionary)
 {
     LorettaDebug.Assert(dictionary is not null);
     _dictionary = dictionary !;
 }
Example #9
0
 internal Builder(ImmutableSegmentedDictionary <TKey, TValue> dictionary)
 {
     _dictionary = dictionary;
 }
Example #10
0
 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 !!)
Example #13
0
 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));
 }