| Required Barriers | 2nd operation | |||||
| 1st operation | Normal Load | Normal Store | Volatile Load | Volatile Store | MonitorEnter | MonitorExit |
| Normal Load | LoadStore | LoadStore | ||||
| Normal Store | StoreStore | StoreExit | ||||
| Volatile Load | LoadLoad | LoadStore | LoadLoad | LoadStore | LoadEnter | LoadExit |
| Volatile Store | StoreLoad | StoreStore | StoreEnter | StoreExit | ||
| MonitorEnter | EnterLoad | EnterStore | EnterLoad | EnterStore | EnterEnter | EnterExit |
| MonitorExit | ExitLoad | ExitStore | ExitEnter | ExitExit | ||
In this table, "Enter" is the same as "Load" and "Exit" is the same as "Store", unless overridden by the use and nature of atomic instructions. In particular:
void LIRGenerator::do_StoreField(StoreField* x) {
...
if (is_volatile && os::is_MP()) {
__ membar_release();
}
...
if (is_volatile) {
volatile_field_store(value.result(), address, info);
...
if (is_volatile && os::is_MP()) {
__ membar();
}
}
void LIRGenerator::do_LoadField(LoadField* x) {
...
if (is_volatile) {
volatile_field_load(address, reg, info);
...
if (is_volatile && os::is_MP()) {
__ membar_acquire();
}
}
First note that the barriers here are expressed in the acquire/release/membar form not the loadload/loadstore etc form used by the JSR-133 Cookbook. This impedance mismatch is unfortunate and we should really fix it. Each architecture is responsible for defining what needs to be done for each of the membar forms.
| First Instruction | Second Instruction | Generated Sequence | Required Barrier | Actual Barrier |
|---|---|---|---|---|
| Normal load | Volatile load | load ; volatile_load; membar_acquire; | Nil | Nil |
| Normal store | Volatile load | store ; volatile_load; membar_acquire; | Nil | Nil |
| Volatile load | Volatile load | volatile_load; membar_acquire; volatile_load; membar_acquire; | LoadLoad | membar_acquire |
| Volatile store | Volatile load | membar_release; volatile_store; membar; volatile_load; membar_acquire; | StoreLoad | membar |
| Volatile load | Normal load | volatile_load; membar_acquire; load; | LoadLoad | membar_acquire |
| Volatile load | Normal store | volatile_load; membar_acquire; store; | LoadStore | membar_acquire |
| Volatile load | Volatile store | volatile_load; membar_acquire; membar_release; volatile_store; membar; | LoadStore | membar_acquire + membar_release |
| Normal load | Volatile store | load ; membar_release; volatile_store; membar; | LoadStore | membar_release |
| Normal store | Volatile store | store ; membar_release; volatile_store; membar; | StoreStore | membar_release |
| Volatile load | Volatile store | volatile_load; membar_acquire; membar_release; volatile_store; membar; | LoadStore | membar_acquire + membar_release |
| Volatile store | Volatile store | membar_release; volatile_store; membar; membar_release; volatile_store; membar; | StoreStore | membar + membar_release |
| Volatile store | Normal load | membar_release; volatile_store; membar; load; | Nil | membar |
| Volatile store | Normal store | membar_release; volatile_store; membar; store; | Nil | membar |
membar_acquire := LoadLoad + LoadStore membar_release := LoadStore + StoreStore membar := StoreLoadNote these are not describing summative operations but merely saying that the barrier on the left must perform the role of each barrier on the right. It is interesting to note that the most expensive barrier, StoreLoad, maps to membar, and that it is redundant in all cases except for a volatile store followed by a volatile load!
sun.misc.Unsafe Unsafe class provides a number of methods (mainly used by the java.util.concurrent utilities) that allow for atomic operations on variables with the same memory model semantics as used for Java volatile variables (in some cases there are checks that the field in question was in fact declared as volatile).
| Method | Description |
| Unsafe_GetObjectVolatile | Loads an object reference field |
| Unsafe_PutObjectVolatile | Stores an object reference field |
| Unsafe_GetLongVolatile | Loads a Java long field |
| Unsafe_PutLongVolatile | Stores a Java long field |
| Unsafe_Get<type>Volatile | Loads a Java <type> field (everything except long and object reference) |
| Unsafe_Put<type>Volatile | Stores a Java <type> field (everything except long and object reference) |
unsafe.cpp) most of the above are implemented in terms of two macros: GET_FIELD_VOLATILE and SET_FIELD_VOLATILE. For the long case there is further code to check if the VM supports_cx8 which identifies whether the platform supports 64-bit atomic operations (even if a 32-bit platform), and if not, object-locking is used to make the operation atomic - these cases need not be considered further. The Object version uses the macro GET_OOP_FIELD_VOLATILE for "get" and a direct implementation for "put"/"set".
The barriers used by these actions (at least as of fix 7009756 - Jan 8, 2011) are:
| Action | Generated Sequence |
| GET_FIELD_VOLATILE | OrderAccess::loadAcquire() |
| PUT_FIELD_VOLATILE | OrderAccess::release_store_fence() |
| GET_OOP_FIELD_VOLATILE | load; OrderAccess::acquire() |
| Unsafe_SetObjectVolatile | OrderAccess::release(); store; OrderAccess::fence() |
vmSymbols.hpp which maps each Unsafe method to a label:
put<type>Volatile maps to _put<type>Volatile
gett<type>Volatile maps to _gett<type>Volatile
c1_GraphBuilder.cpp these are then mapped to a single operation that is passed the type of the field being operated upon
_put<type>Volatile maps to UnsafePutObject(<type>, ..., true)
_gett<type>Volatile maps to UnsafeGetObject(<type>, ..., true)
LIRGenerator::do_UnsafeGetObject and LIRGenerator::do_UnsafePutObject. Note that the use of the term "Object" is misleading here as it should really be "field".
Those methods, in turn, for the volatile case generate the following code sequences (as of CR 7010665 - Jan 9, 2011):
| Action | Generated Sequence |
do_UnsafeGetObject | load; membar_acquire() |
do_UnsafePUtObject | membar_release(); store; membar() |
Unsafe class also supports three more weakly defined operations, but still with ordering constraints:
putOrderedObject for reference fields
putOrderedInt for int fields
putOrderedLong for long fields
unsafe.cpp and the C1 intrinsic versions, implement these identically to the volatile versions, with no relaxing of the memory barriers. Only the C2 intrinsics actually elide the trailing barrier.