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