I am working on small Ref<T>
abstraction with lock-free optimistic Update method (supposed to work with immutable T
data structures, immutable trees in particular).
It is based on Albahari's "Threading in C#" LockFreeUpdate
method in SpinWait usage section:
static void LockFreeUpdate<T> (ref T field, Func <T, T> updateFunction) where T : class
{
var spinWait = new SpinWait();
while (true)
{
T snapshot1 = field;
T calc = updateFunction (snapshot1);
T snapshot2 = Interlocked.CompareExchange (ref field, calc, snapshot1);
if (snapshot1 == snapshot2) return;
spinWait.SpinOnce();
}
}
My question is Why do we need SpinWait here? Because if field is already updated then another concurrent code can immediately retry to Update from new value.
Full Ref code without SpinWait is provided below (check Update
method for comparison):
public sealed class Ref<T> where T : class
{
public T Value { get { return _value; } }
public Ref(T initialValue = default(T))
{
_value = initialValue;
}
public T Update(Func<T, T> update)
{
var retryCount = 0;
while (true)
{
var oldValue = _value;
var newValue = update(oldValue);
if (Interlocked.CompareExchange(ref _value, newValue, oldValue) == oldValue)
return oldValue;
if (++retryCount > RETRY_COUNT_UNTIL_THROW)
throw new InvalidOperationException(ERROR_EXCEEDED_RETRY_COUNT);
}
}
private T _value;
private const int RETRY_COUNT_UNTIL_THROW = 10;
private static readonly string ERROR_EXCEEDED_RETRY_COUNT =
"Ref retried to Update for " + RETRY_COUNT_UNTIL_THROW + " times But there is always someone else intervened.";
}