| Owner | Doug Lea |
| Type | Feature |
| Scope | JDK |
| Status | Closed / Delivered |
| Release | 8 |
| Component | hotspot / runtime |
| Discussion | hostspot dash dev at openjdk dot java dot net |
| Endorsed by | Mark Reinhold |
| Created | 2012/11/27 20:00 |
| Updated | 2017/06/14 19:19 |
| Issue | 8046161 |
Add three memory-ordering intrinsics to thesun.misc.Unsafe class.
JVMs don't have advertised mechanisms providing memory orderings thatwere not envisioned originally or in the JSR 133 memory model specs.(But are present for example in the recent C11/C++11 specs.) Theseinclude several constructions used injava.util.concurrent and otherlow-level libraries that currently rely on undocumented (and possiblytransient) properties of existing intrinsics.
Adding these methods at the VM level permits use by JDK libraries insupport of JDK 8 features, while also opening up the possibility oflater exporting the base functionality via newjava.util.concurrentAPIs. This may become essential to allow people developing non-JDKlow-level libraries if upcoming modularity support makes these methodsimpossible for others to access.
The three methods provide the three different kinds of memory fencesthat some compilers and processors need to ensure that particularaccesses (loads and stores) do not become reordered. In practice, theyare identical in effect to existinggetXXXVolatile,putXXXVolatile, andputOrderedXXX methods, except that they do not actually perform anaccess; they just ensure the ordering. However, they conceptuallydiffer in one way: According to the current JMM, some language-leveluses ofvolatile may be reordered with some uses of non-volatilevariables. But this would not be allowed here. (It is not allowed inthe current intrinsics either, but this is an undocumented differencebetween intrinsics-based vs language-based volatile access.)
The three methods are:
/** * Ensures lack of reordering of loads before the fence * with loads or stores after the fence. */void loadFence();/** * Ensures lack of reordering of stores before the fence * with loads or stores after the fence. */void storeFence();/** * Ensures lack of reordering of loads or stores before the fence * with loads or stores after the fence. */void fullFence();While there is nothing at all "unsafe" about the three methods, byconvention,Unsafe currently houses related methods for per-usevolatile and atomic operations, so seems to be the best home for thesemethods.
The javadoc could be made more rigorous, although cannot be specifiedin terms of the JMM because it doesn't cover "per-use volatileness".So, leaving them in this simple form probably best conveys intent totarget users. Also, it is probably impossible (because of likelyexisting usage dependencies) to at the same time make explicit andslightly weaken the minimally required reordering properties ofexisting volatile access intrinsics. However, the presence of fenceintrinsics allows users to unambiguously obtain these effects whenneeded.
The three methods can be implemented in terms of the existingacquire,release, andvolatile fence methods available across c1, c2, and theinterpreter/runtime, plus, when applicable, suppressing internal compilerreorderings and value reuse. This requires no new underlyingcapabailities, but still requires adding to and adapting the code strewnacross Hotspot necessary for new intrinsics. Omitting these, here is animplementation sketch:
For c2, implementation amounts to methods that omit all the access codein methods likeinline_unsafe_access, leaving only generation of thememory-based fences, plus an internalCPUOrder fence that disablesreorderings during optimization. (In the case offullFence, alsocautiously including an acquire fence along with the fullvolatilefence, to cover the possibility that existing c2 code relies on thepresence ofMemBarAcquire to detect volatile-ness with respect toloads.)
loadFence: { insert_mem_bar(Op_MemBarCPUOrder); insert_mem_bar(Op_MemBarAcquire);}storeFence: { insert_mem_bar(Op_MemBarCPUOrder); insert_mem_bar(Op_MemBarRelease);}fullFence: { insert_mem_bar(Op_MemBarCPUOrder); insert_mem_bar(Op_MemBarAcquire); insert_mem_bar(Op_MemBarVolatile);}For c1, new nodes can be defined withLIRGenerator actions of
loadFence: { if (os::is_MP()) __ membar_acquire(); }storeFence: { if (os::is_MP()) __ membar_release(); }fullFence: { if (os::is_MP()) __ membar(); }Plus, for all three, disabling GVN inValueMap:
xxxxxFence: { kill_memory(); }And for the C++ runtime versions (inprims/unsafe.cpp), implementingvia the existingOrderAccess methods:
loadFence: { OrderAccess::acquire(); }storeFence: { OrderAccess::release(); }fullFence: { OrderAccess::fence(); }The test infrastructure recently set up by Aleksey Shipilev fortorture-testing volatiles and atomics is simple to adapt to obtain thesame coverage for fence-separated access versus volatiles.
We assume that Oracle engineers will continue to assist integrating intoJDK 8.