Unasafe.putOdered -> Unsafe.releasePut
David Holmes
david.holmes at oracle.com
Thu Mar 5 00:49:59 UTC 2015
On 2/03/2015 9:54 PM, Paul Sandoz wrote:
>
> On Mar 2, 2015, at 12:07 PM, David Holmes <david.holmes at oracle.com> wrote:
>
>> Hi Paul,
>>
>> This is more a question for core-libs and the users of this Unsafe API.
>
> I wanted to check here first because of work done in [1] and because this is where there is most experience with the Unsafe implementation. I don't want to change the semantics of existing Unsafe methods.
>
> Note that the VarHandle work will expose using release/acquire names, where the former implementation defers to Unsafe.putOrdered*, and the later to relaxed plus explicit load fence.
The Unsafe API naming tends to be done by whomever is defining the
public API that uses it (eg. Doug Lea for j.u.c). The implementation of
the Unsafe API then has to map through to whatever the VM provides. So
given there are three layers you either match the first two or the last
two but you can rarely make all three the same.
So for example, j.u.c.atomic lazySet is defined as providing:
* <li> {@code lazySet} has the memory effects of writing (assigning)
* a {@code volatile} variable except that it permits reorderings with
* subsequent (but not previous) memory actions that do not themselves
* impose reordering constraints with ordinary non-{@code volatile}
* writes.
and is implemented by a call to Unsafe.putOrdered, which in turn is
implemented by doing a full volatile write, which is
OrderAccess::release_store_fence. The implementation provides a stronger
barrier than what is required. Depending on your perspective either
putOrdered captures the right "lazySet" semantics but happens to be
implemented using stronger ones (and that implementation might be
relaxed in the future); or putOrdered is really intended to be
release_store_fence (and so could be renamed accordingly) and if you
want to implement true lazySet semantics then you have to define a new
Unsafe API to capture that. Conversely if you need release_store_fence
then you either define an API that always provide it, or use an existing
one (in this case it is a normal volatile field write).
I'm in the former camp where putOrdered is weaker than a putRelease
would be. I think the VarHandle work would be wrong to assume putOrdered
must be as strong as it is, and should instead define a stronger Unsafe API.
But that is why I think this needs to be discussed with the folks at the
front-end API's not the backend ones :)
Cheers,
David
-----
>
>> Sometimes the implementation of unsafe doesn't exactly match the API, so we need to check what the intended API semantics are.
>>
>
> I believe things align naming-wise at least from eyeballing C2 code i.e. Unsafe.putOrdered* is a form of release-store like that of OrderAccess::release_store and Unsafe.storeFence is a form of release-fence like that of OrderAccess::release.
>
> src/share/vm/opto/library_call.cpp:
>
> bool LibraryCallKit::inline_unsafe_ordered_store(BasicType type) {
> ...
> insert_mem_bar(Op_MemBarRelease);
> insert_mem_bar(Op_MemBarCPUOrder);
> // Ensure that the store is atomic for longs:
> const bool require_atomic_access = true;
> Node* store;
> if (type == T_OBJECT) // reference stores need a store barrier.
> store = store_oop_to_unknown(control(), base, adr, adr_type, val, type, MemNode::release);
> else {
> store = store_to_memory(control(), adr, val, type, adr_type, MemNode::release, require_atomic_access);
> }
> insert_mem_bar(Op_MemBarCPUOrder);
> return true;
> }
>
> bool LibraryCallKit::inline_unsafe_fence(vmIntrinsics::ID id) {
> // Regardless of form, don't allow previous ld/st to move down,
> // then issue acquire, release, or volatile mem_bar.
> insert_mem_bar(Op_MemBarCPUOrder);
> switch(id) {
> case vmIntrinsics::_loadFence:
> insert_mem_bar(Op_LoadFence);
> return true;
> case vmIntrinsics::_storeFence:
> insert_mem_bar(Op_StoreFence);
> return true;
> case vmIntrinsics::_fullFence:
> insert_mem_bar(Op_MemBarVolatile);
> return true;
> default:
> fatal_unexpected_iid(id);
> return false;
> }
> }
>
>
> src/cpu/x86/vm/x86_32.ad:
>
> instruct membar_release() %{
> match(MemBarRelease);
> match(StoreFence);
> ins_cost(400);
>
> size(0);
> format %{ "MEMBAR-release ! (empty encoding)" %}
> ins_encode( );
> ins_pipe(empty);
> %}
>
> ...
>
> instruct membar_acquire() %{
> match(MemBarAcquire);
> match(LoadFence);
> ins_cost(400);
>
> size(0);
> format %{ "MEMBAR-acquire ! (empty encoding)" %}
> ins_encode();
> ins_pipe(empty);
> %}
>
> ...
>
> instruct membar_volatile(eFlagsReg cr) %{
> match(MemBarVolatile);
> effect(KILL cr);
> ins_cost(400);
>
> format %{
> $$template
> if (os::is_MP()) {
> $$emit$$"LOCK ADDL [ESP + #0], 0\t! membar_volatile"
> } else {
> $$emit$$"MEMBAR-volatile ! (empty encoding)"
> }
> %}
> ins_encode %{
> __ membar(Assembler::StoreLoad);
> %}
> ins_pipe(pipe_slow);
> %}
>
>
> Paul.
>
More information about the hotspot-dev
mailing list