|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace Microsoft.CodeAnalysis.Collections
{
internal static class RoslynImmutableInterlocked
{
/// <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="T">The type of value stored by the list.</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<T>(ref ImmutableSegmentedList<T> location, Func<ImmutableSegmentedList<T>, ImmutableSegmentedList<T>> transformer)
{
if (transformer is null)
throw new ArgumentNullException(nameof(transformer));
var oldValue = ImmutableSegmentedList<T>.PrivateMarshal.VolatileRead(in location);
while (true)
{
var newValue = transformer(oldValue);
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
}
}
/// <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="T">The type of value stored by the list.</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<T, TArg>(ref ImmutableSegmentedList<T> location, Func<ImmutableSegmentedList<T>, TArg, ImmutableSegmentedList<T>> transformer, TArg transformerArgument)
{
if (transformer is null)
throw new ArgumentNullException(nameof(transformer));
var oldValue = ImmutableSegmentedList<T>.PrivateMarshal.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
}
}
/// <summary>
/// Assigns a field or variable containing an immutable list to the specified value and returns the previous
/// value.
/// </summary>
/// <typeparam name="T">The type of value stored by the list.</typeparam>
/// <param name="location">The field or local variable to change.</param>
/// <param name="value">The new value to assign.</param>
/// <returns>The prior value at the specified <paramref name="location"/>.</returns>
public static ImmutableSegmentedList<T> InterlockedExchange<T>(ref ImmutableSegmentedList<T> location, ImmutableSegmentedList<T> value)
{
return ImmutableSegmentedList<T>.PrivateMarshal.InterlockedExchange(ref location, value);
}
/// <summary>
/// Assigns a field or variable containing an immutable list to the specified value if it is currently equal to
/// another specified value. Returns the previous value.
/// </summary>
/// <typeparam name="T">The type of value stored by the list.</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 ImmutableSegmentedList<T> InterlockedCompareExchange<T>(ref ImmutableSegmentedList<T> location, ImmutableSegmentedList<T> value, ImmutableSegmentedList<T> comparand)
{
return ImmutableSegmentedList<T>.PrivateMarshal.InterlockedCompareExchange(ref location, value, comparand);
}
/// <summary>
/// Assigns a field or variable containing an immutable list to the specified value if it is has not yet been
/// initialized.
/// </summary>
/// <typeparam name="T">The type of value stored by the list.</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<T>(ref ImmutableSegmentedList<T> location, ImmutableSegmentedList<T> value)
{
return InterlockedCompareExchange(ref location, value, default(ImmutableSegmentedList<T>)).IsDefault;
}
/// <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="T">The type of value stored by the set.</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<T>(ref ImmutableSegmentedHashSet<T> location, Func<ImmutableSegmentedHashSet<T>, ImmutableSegmentedHashSet<T>> transformer)
{
if (transformer is null)
throw new ArgumentNullException(nameof(transformer));
var oldValue = ImmutableSegmentedHashSet<T>.PrivateMarshal.VolatileRead(in location);
while (true)
{
var newValue = transformer(oldValue);
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
}
}
/// <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="T">The type of value stored by the set.</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<T, TArg>(ref ImmutableSegmentedHashSet<T> location, Func<ImmutableSegmentedHashSet<T>, TArg, ImmutableSegmentedHashSet<T>> transformer, TArg transformerArgument)
{
if (transformer is null)
throw new ArgumentNullException(nameof(transformer));
var oldValue = ImmutableSegmentedHashSet<T>.PrivateMarshal.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
}
}
/// <summary>
/// Assigns a field or variable containing an immutable set to the specified value and returns the
/// previous value.
/// </summary>
/// <typeparam name="T">The type of value stored by the set.</typeparam>
/// <param name="location">The field or local variable to change.</param>
/// <param name="value">The new value to assign.</param>
/// <returns>The prior value at the specified <paramref name="location"/>.</returns>
public static ImmutableSegmentedHashSet<T> InterlockedExchange<T>(ref ImmutableSegmentedHashSet<T> location, ImmutableSegmentedHashSet<T> value)
{
return ImmutableSegmentedHashSet<T>.PrivateMarshal.InterlockedExchange(ref location, value);
}
/// <summary>
/// Assigns a field or variable containing an immutable set to the specified value if it is currently
/// equal to another specified value. Returns the previous value.
/// </summary>
/// <typeparam name="T">The type of value stored by the set.</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 ImmutableSegmentedHashSet<T> InterlockedCompareExchange<T>(ref ImmutableSegmentedHashSet<T> location, ImmutableSegmentedHashSet<T> value, ImmutableSegmentedHashSet<T> comparand)
{
return ImmutableSegmentedHashSet<T>.PrivateMarshal.InterlockedCompareExchange(ref location, value, comparand);
}
/// <summary>
/// Assigns a field or variable containing an immutable set to the specified value if it is has not yet
/// been initialized.
/// </summary>
/// <typeparam name="T">The type of value stored by the set.</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<T>(ref ImmutableSegmentedHashSet<T> location, ImmutableSegmentedHashSet<T> value)
{
return InterlockedCompareExchange(ref location, value, default(ImmutableSegmentedHashSet<T>)).IsDefault;
}
/// <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)
where TKey : notnull
{
if (transformer is null)
throw new ArgumentNullException(nameof(transformer));
var oldValue = ImmutableSegmentedDictionary<TKey, TValue>.PrivateMarshal.VolatileRead(in location);
while (true)
{
var newValue = transformer(oldValue);
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
}
}
/// <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>.PrivateMarshal.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
}
}
/// <summary>
/// Assigns a field or variable containing an immutable dictionary to the specified value and 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>
/// <returns>The prior value at the specified <paramref name="location"/>.</returns>
public static ImmutableSegmentedDictionary<TKey, TValue> InterlockedExchange<TKey, TValue>(ref ImmutableSegmentedDictionary<TKey, TValue> location, ImmutableSegmentedDictionary<TKey, TValue> value)
where TKey : notnull
{
return ImmutableSegmentedDictionary<TKey, TValue>.PrivateMarshal.InterlockedExchange(ref location, value);
}
/// <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>.PrivateMarshal.InterlockedCompareExchange(ref location, value, comparand);
}
/// <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;
}
/// <inheritdoc cref="ImmutableInterlocked.GetOrAdd{TKey, TValue, TArg}(ref ImmutableDictionary{TKey, TValue}, TKey, Func{TKey, TArg, TValue}, TArg)"/>
public static TValue GetOrAdd<TKey, TValue, TArg>(ref ImmutableSegmentedDictionary<TKey, TValue> location, TKey key, Func<TKey, TArg, TValue> valueFactory, TArg factoryArgument)
where TKey : notnull
{
if (valueFactory is null)
throw new ArgumentNullException(nameof(valueFactory));
var map = ImmutableSegmentedDictionary<TKey, TValue>.PrivateMarshal.VolatileRead(in location);
if (map.IsDefault)
throw new ArgumentNullException(nameof(location));
if (map.TryGetValue(key, out var value))
{
return value;
}
value = valueFactory(key, factoryArgument);
return GetOrAdd(ref location, key, value);
}
/// <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>.PrivateMarshal.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);
}
/// <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>.PrivateMarshal.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>.PrivateMarshal.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.AddOrUpdate{TKey, TValue}(ref ImmutableDictionary{TKey, TValue}, TKey, TValue, Func{TKey, TValue, TValue})"/>
public static TValue AddOrUpdate<TKey, TValue>(ref ImmutableSegmentedDictionary<TKey, TValue> location, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
where TKey : notnull
{
if (updateValueFactory is null)
throw new ArgumentNullException(nameof(updateValueFactory));
TValue newValue;
var priorCollection = ImmutableSegmentedDictionary<TKey, TValue>.PrivateMarshal.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 = addValue;
}
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.TryAdd{TKey, TValue}(ref ImmutableDictionary{TKey, TValue}, TKey, TValue)"/>
public static bool TryAdd<TKey, TValue>(ref ImmutableSegmentedDictionary<TKey, TValue> location, TKey key, TValue value)
where TKey : notnull
{
var priorCollection = ImmutableSegmentedDictionary<TKey, TValue>.PrivateMarshal.VolatileRead(in location);
while (true)
{
if (priorCollection.IsDefault)
throw new ArgumentNullException(nameof(location));
if (priorCollection.ContainsKey(key))
{
return false;
}
var updatedCollection = priorCollection.Add(key, value);
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.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>.PrivateMarshal.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>.PrivateMarshal.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
}
}
/// <summary>
/// Reads from an ImmutableArray location, ensuring that a read barrier is inserted to prevent any subsequent reads from being reordered before this read.
/// </summary>
/// <remarks>
/// This method is not intended to be used to provide write barriers.
/// </remarks>
public static ImmutableArray<T> VolatileRead<T>(ref readonly ImmutableArray<T> location)
{
var value = location;
// When Volatile.ReadBarrier() is available in .NET 10, it can be used here.
Interlocked.MemoryBarrier();
return value;
}
/// <summary>
/// Writes to an ImmutableArray location, ensuring that a write barrier is inserted to prevent any prior writes from being reordered after this write.
/// </summary>
/// <remarks>
/// This method is not intended to be used to provide read barriers.
/// </remarks>
public static void VolatileWrite<T>(ref ImmutableArray<T> location, ImmutableArray<T> value)
{
// When Volatile.WriteBarrier() is available in .NET 10, it can be used here.
Interlocked.MemoryBarrier();
location = value;
}
}
}
|