`weakCompareAndSetPlain` methods are not atomic
The weakCompareAndSetPlain methods are not atomic. For example:
https://github.com/scala-native/scala-native/blob/1ab30535648dbab6358bc1d8630cf4a9453bb29c/javalib/src/main/scala/java/util/concurrent/atomic/AtomicReference.scala#L112-L117
At least AtomicReference, AtomicBoolean and AtomicInteger are affected (but I'd guess the others are too).
I this this one needs an umpire call.
Wojciech, from PR #3129 you appear to have some history with this code. when you come up from air and have some time, could you take a look at this?
It reads like JSR-166 code but I could not find any such thing in the various Doug Lea & Co. Oswego JSR-166 repositories I could find on my phone.
The closest I found was a Java 1.5 version where the Weak version called into the strong CAS version
with a note saying 'for now'. Because that was well before Java 9, there was no Plain variant.
Access modes control atomicity and consistency properties. Plain read (get) and write (set) accesses are guaranteed to be bitwise atomic only for references and for primitive values of at most 32 bits, and impose no observable ordering constraints with respect to threads other than the executing thread.
Scala.js has related but not directly relevant methods.
Is there a bug or deficiency here other than the underlying Java definition, which is canon?
Durban,
At the risk of being an apologist for a methods I would be extremely reluctant to use, let me suggest that the cited code may not be as obviously wrong as it appears.
As well as I could do on my phone, I chased the JDK 24 VarHandle and weakCompareAndSetPlain descriptions.
The latter says:
Possibly atomically sets the value to newValue if the current value == expectedValue, with memory effects as specified by VarHandle.weakCompareAndSetPlain(java.lang.Object...).
It later talks about:
Access modes control atomicity and consistency properties. Plain read (get) and write (set) accesses are guaranteed to be bitwise atomic only for references and for primitive values of at most 32 bits, and impose no observable ordering constraints with respect to threads other than the executing thread.
It emphasizes that atomic means setting all 64 bits at once. Then it says
atomically compare and set the value of a variable under specified memory ordering effects.
Now is the "atomically compare" in the last snippet the "atomically compare-and-set" that most non-Java discussions of CAS, use or is it "compare 64 bits" then possibly "set 64 bits", i.e. the current implementation?
Notes:
-
The C and C++ 'atomic' headers seem to have no equivalent function. Given that I would expect to no equivalent LLVM intrinsics.
-
"Atomically compare & set(exchange)" and "Plain" memory semantics seem to be somewhat at odds. How does one get any correctness or predictability out of this?