6

Here is a code snippet from AtomicInteger:

public final int accumulateAndGet(int x,
                                  IntBinaryOperator accumulatorFunction) {
    int prev = get(), next = 0;
    for (boolean haveNext = false;;) {
        if (!haveNext)
            next = accumulatorFunction.applyAsInt(prev, x);
        if (weakCompareAndSetVolatile(prev, next))
            return next;
        haveNext = (prev == (prev = get()));
    }
}

Is there any specific reason of using for(... instead of while(... or it's just a programming choice?

Is it because of the reordering done by the jvm?

3 Answers3

1

This is because the loop is left in the middle, not at the start (then programmer could use "while"), or end (then programmer could use "do ... while"). It is not connected with reordering.

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
1

TLDR.: This is done to prevent an unnecessary remapping of next.

This is because of the "weak" nature of the weakCompareAndSetVolatile:

/**
 * Possibly atomically sets the value to {@code newValue}
 * if the current value {@code == expectedValue},
 * with memory effects as specified by
 * {@link VarHandle#weakCompareAndSet}.
 *
 * @param expectedValue the expected value
 * @param newValue the new value
 * @return {@code true} if successful
 * @since 9
 */
public final boolean weakCompareAndSetVolatile(V expectedValue, V newValue) {
    return VALUE.weakCompareAndSet(this, expectedValue, newValue);
}

On the "weak" nature of the weakCompareAndSet's:

"... But on other platforms such as PowerPC or ARM (without LSE extension) CAS is implemented as a sequence of several instructions which provide LL/SC behavior and memory barriers as separate building blocks. This creates some wiggle room how strong your CAS can be both in terms of ordering and failure guarantees."

What the Javadoc defines as "May fail spuriously."

Because of the possibility of these false negatives, the line haveNext = (prev == (prev = get())); needs to reassure that the CAS failed because of a true miss (a change in the volatile value), and not because it "failed spuriously".

In reality, this would not be needed if the function accumulatorFunction.applyAsInt(prev, x); would not be required to redefine next each time prev received a volatile write, hence changing its value... on each loop, meaning the reason for this double check is to prevent an unnecessary remapping of next.

At first glance it seems as if a mismatched version of next could happen in between loops (when a wrong version of prev passes the function), and that is correct. BUT the mismatched version will never be set because by the time the loop reaches the weakCompareAndSetVolatile, the expected value will not match, and so the entire process will retry, until everything matches.

Delark
  • 1,141
  • 2
  • 9
  • 15
0

In that specific use case for AtomicInteger the variable can be defined within the scope of usage once where as with a while-loop it would be defined out-side of the scope of the iteration. I would assume that played into the choice of iteration mechanism.

Jason
  • 5,154
  • 2
  • 12
  • 22
  • 1
    "it would be constantly redefined upon each iteration." only if you put it inside the loop. – Andy Turner Jul 15 '20 at 18:12
  • 1
    @AndyTurner yeah so I should have specifically indicated that the purpose would be to keep it to the scope of the block its defined in instead of created outside of the scope. – Jason Jul 15 '20 at 18:13
  • @ConcurrentBhai Yeah you're right that is a really good point that I overlooked. I shouldn't have rushed to answer. – Jason Jul 15 '20 at 18:16