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.