Search code examples
multithreadingjava-8java.util.concurrentatomic-long

LongAdder Striped64 wasUncontended implementation detail


This is a question not about how LongAdder works, it's about an intriguing implementation detail that I can't figure out.

Here is the code from Striped64 (I've cut out some parts and left the relevant parts for the question):

    final void longAccumulate(long x, LongBinaryOperator fn,
                          boolean wasUncontended) {
    int h;
    if ((h = getProbe()) == 0) {
        ThreadLocalRandom.current(); // force initialization
        h = getProbe();
        wasUncontended = true;
    }
    boolean collide = false;  // True if last slot nonempty
    for (;;) {
        Cell[] as; Cell a; int n; long v;
        if ((as = cells) != null && (n = as.length) > 0) {
            if ((a = as[(n - 1) & h]) == null) {
                //logic to insert the Cell in the array
            }
            // CAS already known to fail
            else if (!wasUncontended)   {
                wasUncontended = true;      // Continue after rehash
            }
            else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))){
                break;
            }

A lot of things from code are clear to me, except for the :

        // CAS already known to fail
        else if (!wasUncontended)   {
            wasUncontended = true;      // Continue after rehash
        }

Where does this certainty that the following CAS will fail? This is really confusing for me at least, because this check only makes sense for a single case : when some Thread enters the longAccumulate method for the n-th time (n > 1) and the busy spin is at it's first cycle.

It's like this code is saying : if you (some Thread) have been here before and you have some contention on a particular Cell slot, don't try to CAS your value to the already existing one, but instead rehash the probe.

I honestly hope I will make some sense for someone.


Solution

  • It's not that it will fail, it's more that it has failed. The call to this method is done by the LongAdder add method.

    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
                longAccumulate(x, null, uncontended);
        }
    }
    
    1. The first set of conditionals is related to existence of the long Cells. If the necessary cell doesn't exist, then it will try to accumulate uncontended (as there was no attempt to add) by atomically adding the necessary cell and then adding.
    2. If the cell does exist, try to add (v + x). If the add failed then there was some form of contention, in that case try to do the accumulating optimistically/atomically (spin until successful)

    So why does it have

    wasUncontended = true;      // Continue after rehash
    

    My best guess is that with heavy contention, it will try to give the running thread time to catch up and will force a retry of the existing cells.