[jmm-dev] Specifying VarHandle acquire/release without ill effects

Aleksey Shipilev aleksey.shipilev at oracle.com
Thu Jun 16 20:28:05 UTC 2016


Hi,

Our current VarHandle.{getAcquire|setRelease} are oddly specified:

  /*
   * Returns the value of a variable, and ensures that subsequent
   * loads and stores are not reordered before this access.
   * ...
   */
  Object getAcquire(Object... args);


  /**
   * Sets the value of a variable to the {@code newValue}, and ensures
   * that prior loads and stores are not reordered after this access.
   * ...
   */
   void setRelease(Object... args);


The clause about reorderings trips users into believing that getAcquire
and putRelease have fence-like semantics. But in reality, they are
weaker than fences, because optimizers are not obliged to introduce
barriers around the operation to get the needed semantics. This opens up
optimization opportunities that we would not like to miss with
over-specifying VarHandles.

For example, we do not specify volatile semantics in terms of
reorderings, because volatile accesses and associated barriers should be
completely removable here:

@JCStressTest
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE,
  desc = "No happens-before here.")
@Outcome(             expect = Expect.ACCEPTABLE,
  desc = "All other cases are acceptable.")
@State
public class VolatilesAreNotBarriers {

    static class Holder {
        volatile int GREAT_BARRIER_REEF;
    }

    int x;
    int y;

    @Actor
    public void actor1() {
        Holder h = new Holder();
        x = 1;
        h.GREAT_BARRIER_REEF = h.GREAT_BARRIER_REEF;
        y = 1;
    }

    @Actor
    public void actor2(IntResult2 r) {
        Holder h = new Holder();
        r.r1 = y;
        h.GREAT_BARRIER_REEF = h.GREAT_BARRIER_REEF;
        r.r2 = x;
    }
}

(A similar example can be built with setRelease/getAcquire)

Current C2 does leave barriers behind, but that seem to be an
implementation inefficiency: while it purges both Holder instances and
volatile ops, it loses the association between the actual store and the
relevant barrier shortly after parsing.

My duct-taped Graal runs show that Graal seems to eliminate both
instances and associated barriers on x86 (disassembly shows no barriers,
and performance is 10x faster in actor methods). We certainly would not
like to throw compilers under the bus and say this is forbidden.

Therefore, I wonder if this is a better getAcquire specification
(setRelease is symmetric to that):

    /**
     * Returns the value of a variable to the {@code newValue}, with
     * memory semantics similar to {@code volatile} variable load,
     * but without total ordering.
     *
     * <p>Reads with this access mode are access atomic.
     *
     * <p>Previous writes to the same variable synchronize-with (and
     * therefore happen-before) reads with this access mode, if writes
     * are performed with
     * {@code setVolatile}, {@code setRelease},
     * {@code compareAndExchangeVolatile},
     * {@code compareAndExchangeRelease},
     * {@code compareAndSet},
     * {@code weakCompareAndSetVolatile} or
     * {@code weakCompareAndSetRelease} access modes.
     *
     * <p>Reads with this access mode are not part of total
     * synchronization order. This makes {@code getAcquire} access
     * mode weaker than {@code getVolatile} access mode.
     *
     * ...
     */
    Object getAcquire(Object... args);

Or is there some hazard I am not seeing with spec like that? Apart from
a creepy disconnect with acq/rel participating in SW, but not in SO,
while SW is spec-ed as the suborder of SO.

Thanks,
-Aleksey



More information about the jmm-dev mailing list