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.