[jmm-dev] jdk9 APIs [Fences specifically]

Hans Boehm boehm at acm.org
Wed Aug 12 22:33:13 UTC 2015


On Mon, Aug 10, 2015 at 11:22 AM, Doug Lea <dl at cs.oswego.edu> wrote:
>
> ...
>
> For Fences, adding plain StoreStore and LoadLoad fences to the set
> acquireFence, ReleaseFence, and fullFence seems wise.  The main
> arguments against LoadLoad and StoreStore in C11 were usability
> concerns. But they have been found to be useful enough internally to
> JVMs (mainly on ARM) to be semi-supported within hotspot. And we'd
> like to not keep making the same mistake of unnecessarily making
> things ARM/POWER hostile.

Let me argue once more against LoadLoad, and at least dampen the
enthusiasm for StoreStore.

I know of no hardware instructions, except on SPARC, that correspond
to a LoadLoad fence.  And my impression is that it's not very useful on
SPARC.  The ARM DMB xLD fence instruction, if I understand correctly,
is essentially a C++ acquire fence.  POWER lwsync is even stronger
than that. For TSO architectures, loadLoadFence and acquireFence are both
no-ops.

I believe that in all mainstream environments, loadloadFence will
generate the same code as acquireFence, but is much harder to specify
and much trickier to use correctly.  Why would you bother?

The StoreStore situation is slightly different for two reasons:

1) It does seem to correspond to the ARM DMB xST instruction.
2) It does seem to be useful in VM implementations, in that it seems to
    suffice as the end-of-constructor fence.

However, I think it difficult to specify correctly outside of that specific
essentially final-field-initialization scenario.  If I write

x++; // Increment zero initialized field
storeStoreFence();
x_init = true;

can the load for the x++ see a value that was assigned to x in
another thread after seeing x_init set to true?  The load can be reordered
with the fence.  The store can't, but the store "depends on" the load.
But we don't enforce dependency-based ordering, mostly because we
don't know how to define it.

Even if we could define it reasonably, the semantics strike me as thoroughly
weird, even by my warped standards:

Consider:

Thread 1:
x = ... ? 1 : 2;
assert (x < 3);
storeStoreFence();
x_init = true;

Thread 2:
if (x_init) {
    acquireFence();
    x = 17;
}

allows the assertion to fail.

In a more complex example, any method calls used to initialize x in thread 1
need to be careful not to read the value they just wrote.  You guys did
realize that you just volunteered to make a pass through the library
specification
to specify which calls are safe in such cases, right?

It may be more promising to somehow specify it only for use with write-once
variables.  But I don't really know how to make that work either.

And then there are important transitivity/cumulativity questions ...

I think LoadLoad actually has some analogous issues as well.  But unlike
StoreStore,
there seems to be no hardware/performance argument to even consider it.

Hans

>
>
> /**
>  * A set of methods providing fine-grained control of memory ordering.
>  *
>  * <p>The Java Language Specification permits operations to be
>  * executed in orders different than are apparent in program source
>  * code, subject to constraints mainly stemming from the use of locks
>  * and volatile fields. The methods of this class can also be used to
>  * impose constraints. Their specifications are phrased in terms of
>  * the lack of "reorderings" -- observable ordering effects that might
>  * otherwise occur if the fence were not present.
>  *
>  * @apiNote More precise phrasing of these specifications may
>  * accompany future updates of the Java Language Specification.
>  */
> public class Fences {
>
>     /**
>      * Ensures that loads and stores before the fence will not be
>      * reordered with loads and stores after the fence.
>      *
>      * @apiNote Ignoring the many semantics differences from C and
>      * C++, this method has memory ordering effects compatible with
>      * atomic_thread_fence(memory_order_seq_cst)
>      */
>     public static void fullFence() {}
>
>     /**
>      * Ensures that loads before the fence will not be reordered with
>      * loads and stores after the fence.
>      *
>      * @apiNote Ignoring the many semantics differences from C and
>      * C++, this method has memory ordering effects compatible with
>      * atomic_thread_fence(memory_order_acquire)
>      */
>     public static void acquireFence() {}
>
>     /**
>      * Ensures that loads and stores before the fence will not be
>      * reordered with stores after the fence.
>      *
>      * @apiNote Ignoring the many semantics differences from C and
>      * C++, this method has memory ordering effects compatible with
>      * atomic_thread_fence(memory_order_release)
>      */
>     public static void releaseFence() {}
>
>     /**
>      * Ensures that loads before the fence will not be reordered with
>      * loads after the fence.
>      */
>     public static void loadLoadFence() {}
>
>     /**
>      * Ensures that stores before the fence will not be reordered with
>      * stores after the fence.
>      */
>     public static void storeStoreFence() {}
>
>
> }
>


More information about the jmm-dev mailing list