[concurrency-interest] AtomicReference.updateAndGet() mandatory updating

Gil Tene gil at azul.com
Wed May 24 18:33:27 EDT 2017


The memory semantics of updateAndGet() need to be captured in its JavaDoc. In this sense it is no different from compareAndSet(), which currently defines the memory semantics:

http://download.java.net/java/jdk9/docs/api/java/util/concurrent/atomic/AtomicReference.html#compareAndSet-V-V- says:
"Atomically sets the value to newValue if the current value == expectedValue, with memory effects as specified by VarHandle.compareAndSet(java.lang.Object…)."

and under compareAndSet in VarHandle (http://download.java.net/java/jdk9/docs/api/java/lang/invoke/VarHandle.html#compareAndSet-java.lang.Object…- ) it currently says:
"Atomically sets the value of a variable to the newValue with the memory semantics of setVolatile(java.lang.Object...) if the variable's current value, referred to as the witness value, == the expectedValue, as accessed with the memory semantics of getVolatile(java.lang.Object…)."

However, 
http://download.java.net/java/jdk9/docs/api/java/util/concurrent/atomic/AtomicReference.html#updateAndGet-java.util.function.UnaryOperator- says nothing about the memory semantics. It should. And it should probably say it either like this:

"Atomically updates the current value with the results of applying the given function with memory semntics of VarHandle.setVolatile(java.lang.Object..), returning the updated value, as accessed with the memory semantics of getVolatile(java.lang.Object…). The function should be side-effect-free, since it may be re-applied when attempted updates fail due to contention among threads."

or like this:

"Atomically updates the current value with the results of applying the given function with memory semntics of VarHandle.setVolatile(java.lang.Object..) if the variable's current value, referred to as the witness value, != the existing value, as accessed with the memory semantics of getVolatile(java.lang.Object…), returning the updated value. The function should be side-effect-free, since it may be re-applied when attempted updates fail due to contention among threads."

The choice between the two above definitions will determine whether or not the suggested optimization is allowed...

Once could argue that the backwards compatible (with java 8) version would be the first form (i.e. always equivalent to both a volatile write and a volatile read), since the Java 8 semantics are defined that way for all of of j.u.c.atomic ((https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html): "… 	• compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables. ". However, since Java 9 seems to be relaxing the semantics for compareAndSet() [in an arguably not-fully-compatible way, since the new Java 9 compareAndSet semantics are somewhat weaker], we can/should do that consistently across the board.

In any case, all of the various atomic operations *should* have defined memory semantics. These used to be globally captured in the j.u.c.atomic JavaDoc in Java 8. But with Java 9, the memory semantics notes were removed from j.u.c.atomic and are "punted" by most individual method definitions to VarHandle. However, this punting is not universal, and arguably leaves the memory semantics of several operations in j.u.c.atomic classes unspecified, including getAndUpdate, upodateAndGet, getAndAccumulate, accumulateAndGet [all of which did have a defined memory semantics behavior in Java 8, since they all fit under the "...all other read-and-update.." catchall in j.u.c.atomic].

— Gil.

> On May 24, 2017, at 2:50 PM, Mike Duigou <openjdk at duigou.org> wrote:
> 
> That's my view of this optimization as well; the writes that occur do change but only in ways that nobody should be able to rely upon. Assuming the proposed change to getAndUpdate seemed reasonable to folks I planned to next suggest:
> 
> replace:
> 
> public final boolean compareAndSet(V expect, V update) {
>   return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
> }
> 
> with:
> 
> public final boolean compareAndSet(V expect, V update) {
>    return expect != update
>       ? unsafe.compareAndSwapObject(this, valueOffset, expect, update)
>       : expect != value ? false : true;
> }
> 
> For both the proposed updateAndGet and compareAndSet the goal is to avoid expensive volatile writes when the value is not actually changing.
> 
> Mike
> 
> On 2017-05-24 12:38, Justin Sampson wrote:
>> Andrew Haley wrote:
>>> Mike Duigou wrote:
>>> > I find that I write a lot of update functions which only occasionally
>>> > change the value. For these cases I wonder if it would be reasonable to
>>> > skip the update if the value of next is unchanged from previous.
>>> I don't think so, because the update has the effect of a volatile
>>> write. If you skip the update you lose the happens-before ordering
>>> of that write.
>> That's strictly true (the memory barriers come out different), but no
>> algorithm could actually rely on the difference. The reading thread can't
>> tell if it's reading after the write (and therefore can depend on the
>> happens-before) or reading before the write (and therefore cannot), since
>> it sees the same value in either case.
>> This kind of optimization is already done in some places in the JDK, such
>> as AtomicStampedReference and AtomicMarkableReference, both of which skip
>> the write if the current value is already equal to the new value in set(),
>> compareAndSet(), etc.
>> Cheers,
>> Justin
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest



More information about the Concurrency-interest mailing list