scala-native icon indicating copy to clipboard operation
scala-native copied to clipboard

`weakCompareAndSetPlain` methods are not atomic

Open durban opened this issue 4 months ago • 1 comments

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).

durban avatar Aug 04 '25 23:08 durban

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?

It seems to me that the answer really does not matter, since the access to the underlying `value` is Wild West. The values seem to make sense only within the thread that sets them, and then for possibly only a very short window.
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?

LeeTibbert avatar Aug 22 '25 23:08 LeeTibbert