HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Try a speculative, concurrent, lock free, atomic update until abort condition matches

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
matchesupdateconditionuntilfreespeculativeabortatomicconcurrenttry

Problem

Please let me know if you see any performance improvements, bugs, or anything you'd change and why.

public static bool TrySpeculativeUpdate(ref int field, out int result,
        Func update, Func shouldAbort)
    {
        SpinWait spinWait = new SpinWait();
        while (true)
        {
            int snapshot = field;
            if (shouldAbort(field))
            {
                result = 0;
                return false;
            }
            else
            {
                int calc = update(snapshot);
                if (Interlocked.CompareExchange(ref field, calc, snapshot) == snapshot)
                {
                    result = calc;
                    return true;
                }
            }

            spinWait.SpinOnce();
        }
    }


Can be used like this

private bool TryIncreaseCapacity(out int newCapacity)
    {
        return TrySpeculativeUpdate(ref _currentCapacity, out newCapacity,
            (currentCapacity) => currentCapacity + 1,
            (currentCapacity) => currentCapacity == _maxCapacity);
    }

    if (this.TryIncreaseCapacity(out newCapacity))
    {
        ...
    }
    else
    {
        ...
    }


What I'm really trying to accomplish is the fastest thread safe version of Interlocked.Increment that will stop incrementing at a max value and I will have some way of detecting it stopped incrementing.

Solution

I'm not sure I like the method's signature:

public static bool TrySpeculativeUpdate(ref int, out int, Func, Func)


It's probably just me, but I like pushing ref and out parameters to the end of the parameters list, so I'd write it like this:

public static bool TrySpeculativeUpdate(Func, Func, ref int, out int)


I'd use var to ease reading, and FWIW it might be clearer to pass snapshot instead of field to shouldAbort:

public static bool TrySpeculativeUpdate(Func update, Func shouldAbort, ref int field, out int result)
{
    var spinWait = new SpinWait();
    while (true)
    {
        var snapshot = field;

        if (shouldAbort(snapshot))
        {
            result = 0;
            return false;
        }
        else
        {
            var calc = update(snapshot);
            if (Interlocked.CompareExchange(ref field, calc, snapshot) == snapshot)
            {
                result = calc;
                return true;
            }
        }

        spinWait.SpinOnce();
    }
}


That's all I could see. The naming looks about right, but I don't write enough multithreaded code to know for a fact whether this code is optimal or not - it does look good though, I don't see how nesting could be reduced, other than by extacting a tiny little specialized function responsible for this part:

if (Interlocked.CompareExchange(ref field, calc, snapshot) == snapshot)
{
    result = calc;
    return true;
}


But that clearly would be overkill IMO.

Code Snippets

public static bool TrySpeculativeUpdate(ref int, out int, Func<int, int>, Func<int, bool>)
public static bool TrySpeculativeUpdate(Func<int, int>, Func<int, bool>, ref int, out int)
public static bool TrySpeculativeUpdate(Func<int, int> update, Func<int, bool> shouldAbort, ref int field, out int result)
{
    var spinWait = new SpinWait();
    while (true)
    {
        var snapshot = field;

        if (shouldAbort(snapshot))
        {
            result = 0;
            return false;
        }
        else
        {
            var calc = update(snapshot);
            if (Interlocked.CompareExchange(ref field, calc, snapshot) == snapshot)
            {
                result = calc;
                return true;
            }
        }

        spinWait.SpinOnce();
    }
}
if (Interlocked.CompareExchange(ref field, calc, snapshot) == snapshot)
{
    result = calc;
    return true;
}

Context

StackExchange Code Review Q#51725, answer score: 2

Revisions (0)

No revisions yet.