JDK-8180620 : Clarify VarHandle mixed-access subtleties
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2017-05-18
  • Updated: 2023-11-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
The VarHandle spec should make the semantics of "mixed" access clearer.

Suppose two accesses to the same memory location use both VarHandle and non-VarHandle access:

volatile long x;
VarHandle X = ..."x"
...
x = 2;
...
X.compareAndSet(1, 3);

On a 32-bit platform with atomic 64-bit loads and stores but no 64-bit CAS, ordinary accesses are likely to be compiled to a single instruction, whereas the CAS must use a lock.  And the assignment must use the same lock to preserve CAS atomicity.

Perhaps it's simply time to de-support platforms without 64-bit atomics. 
(Are there any platforms currently running java with this problem?)
(currently openjdk apparently uses a global lock to implement VarHandles on such platforms, which is unsatisfactory even when correct).

(See VMSupportsCS8())

Current specs say,
"""When mixed access is performed extreme care should be taken since the Java Memory Model may permit surprising results."""
but it's not clear what kind of mixed mode access is meant: VarHandle vs. non-VarHandle, or sequentially consistent vs. relaxed (but the relaxed access is only possible via VarHandle).

It seems like C++ avoids this sort of issue by requiring an atomic<T> declaration, which can efficiently gate the use of a lock only where required.  That seems like a better design, but too late to adopt for java?

Java may have this problem in a bigger way when Valhalla introduces value types larger than 64 bits.
Comments
Note that it is the OpenJDK implementation that no longer supports platforms without 64-bit atomic ops, but the Java specifications, including those for the VarHandle API must still allow for 32-bit.
24-11-2023

> Perhaps it's simply time to de-support platforms without 64-bit atomics. This is being done with JDK-8318776.
23-11-2023

Consider requiring a field target of a VarHandle that is used with atomic operations be declared "volatile". This would allow the VM to (if necessary) align the field and allocate a lock adjacent to the field. This may not be necessary today with any common cpu but may become necessary when value types larger than 64 bits are introduced in a future release. Declaring such fields volatile also seems to be current practice ... except with array elements (oops!) where we don't yet have any syntax for that ...
22-05-2017

So I think we can ignore the fact that on some pre-jdk9 (and pre-VH) 32bit platforms, volatile long assignment was implemented differently than Unsafe.putVolatile (and only the latter was compatible with lock-based CAS). Good thing, because this oddity is not easy to explain away in a spec. (Internal jdk/j.u.c usages remain compliant with old constraints.) Also note that these specs seem to force upcoming value types to implement volatile assignment and VH.setVolatile in the same way (as I think they are planned to do). As a separate issue, it might not hurt to clarify "mixed access" in that sentence by adding parenthetical: When mixed access (i.e., combining direct field access with access using VarHandle methods) is performed extreme care should be ...
19-05-2017

None of the mainline OpenJDK 9 platforms (x86, sparcv9, ppc64, armv7, armv8/aarch64, S390) have a problem here.
19-05-2017

The OpenJDK mobile-dev project still seems to allow for a range of possibilities on ARM: ./cpu/arm/vm/vm_version_arm_32.cpp: _supports_cx8 = (supports_ldrexd() || supports_kuser_cmpxchg64()); ldrexd is available from ARMv7 upwards. ./cpu/arm/vm/vm_version_arm.hpp: static bool supports_kuser_cmpxchg64() { return _kuser_helper_version >= KUSER_VERSION_CMPXCHG64; } ./cpu/arm/vm/vm_version_arm.hpp:#define KUSER_VERSION_CMPXCHG64 5 and from the kernel txt file: - Valid only if __kuser_helper_version >= 5 (from kernel version 3.1). So mobile ARM < v7 running on Kernel < 3.1 will still use the global lock. I have no idea whether that is a likely scenario.
19-05-2017