FTR: JVM Lang Summit presentation

Remi Forax forax at univ-mlv.fr
Fri Aug 8 21:34:31 UTC 2014


On 08/08/2014 03:01 AM, John Rose wrote:
> On Aug 7, 2014, at 4:25 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
>
>>> slide 71:
>>> I still think VarHandle is not a good idea. Having two way to represent roughly same thing, MethodHandle and VarHandle, is not a good idea.
>> In a perfect world, yes.  MethodHandle tried to model fields, it was a good first attempt, but it didn’t cover all the cases.  It can’t model accesses with (or without) fences; it can’t model atomic updates.  We could of course try to add more MH types to cover this, but the combinatorial explosion is large.  So, having explored the “continue down the MH path”, we discovered that the VarHandle path is richer and more suited to the problem.  Live and learn.

or live and let die ...

I see two serious issues to the VarHandle prototype.
But first, I should say that I'm glad that we are moving away from a 
language feature to an API feature (even if the API require some 
compiler tweaks).
First, VarHandle use signature polymorphism method and I fail to see 
why. To take an example the method compareAnSet takes 3 parameters, the 
first one is
typed with the class that declare the field and the two other parameters 
are the type of the field. It should be typed using generics and the 
real issue is that generics are erased so what we need is a runtime 
check that guarantee that even if someone trick the java type system it 
will fail at runtime.
Using a signature polymorphism has its own issues, null is not typed 
correctly (BTW, the compiler should raise a compile time error if the 
type of null is used to match a polymorphic signature instead of using 
Void, my bad on that) and subtype fail to work properly (it's valid to 
have the type of the expected value and the type of the future value to 
be different in a compareAndSet).
Then I fail to see why it can not be built on top of MethodHandle 
instead of on top of LambdaForm.

> It seems to me there are serious usability problems with using MHs to build a VH API.  Given this:
>     VarHandle vh = ... reify ObjType.fld ...;
>     SomeType v0 = ..., v1 = ...;
>
> we have:
>     boolean z = vh.compareAndSet(obj, v0, v1);
>
> versus:
>     boolean z;
>     try { z = vh.compareAndSet().invokeExact(obj, v0, v1); }
>     catch (Exception e) { throw new AssertionError(e); }

Technically the compareAndSet implemented using a method handle can fail 
at runtime if obj is null or if a lambda form can be allocated,
so the code that use a method handle is even worst:

    boolean z;
    try { z = vh.compareAndSet().invokeExact(obj, v0, v1); }
    catch (Throwale e) {
      if (e instanceof RuntimeException) throw (RuntimeException)e;
      if (e instanceof Error) throw (Error)e;
      throw new AssertionError(e);
    }

here, I agree that we need some sugar, but neither necessarily some 
syntactic sugar nor a new kind of beast,
just a small wrapper on top of a method handle can be enough.
Something along :

   private static MethodHandle compareAndSet(Lookup lookup, Class<?> declaringClass, String fieldName, Class<?> fieldType)
         throws NoSuchFieldException, IllegalAccessException {
     MethodHandle getter = lookup.findGetter(declaringClass, fieldName, fieldType);
     MethodHandleInfo methodHandleInfo = lookup.revealDirect(getter);
     Field field = methodHandleInfo.reflectAs(Field.class, lookup);
     long offset = UNSAFE.objectFieldOffset(field);
     MethodHandle mh = MethodHandles.insertArguments(COMPARE_AND_SWAP_OBJECT, 1, offset);
     return mh.asType(MethodType.methodType(boolean.class, declaringClass, fieldType, fieldType));
   }

   public class VarHandle<T, U> {
     private final @Stable MethodHandle compareAndSet;

     VarHandle(MethodHandle compareAndSet) {
         this.compareAndSet = compareAndSet;
     }

     public boolean compareAndSet(T object, U expected, U value) {
       try {
         return (boolean)compareAndSet.invoke(object, expected, value);
       } catch (Throwable e) {
         if (e instanceof RuntimeException) throw (RuntimeException)e;
         if (e instanceof Error) throw (Error)e;
         throw new AssertionError(e);
       }
     }
   }

Notice the asType at the end of the method compareAndSet and that I use 
invoke and not invokeExact when calling the method handle.
And obviously, things can be a little better if VarHandle is a value type.

> And if we expose an intermediate MH, we would have to ensure that the MH would optimize away cleanly.

yes and the code above is worst because it suppose that invoke is 
optimized cleanly.

>
> The VH prototype allows signature polymorphism to leak into some new types.  If we adopt the prototype as is, we will have to specify that leakage in the JLS and JVMS.  My biggest problem with the prototype is that I don't see a clean way to do that yet.
>
> We can, of course, expose a MH at the bytecode level, under some suitable sugar.  And we can control its allocation cost if we are willing to use an invokedynamic instruction; then we would have a VH metafactory to produce a MH for each operation, for each signature.
>
> One cost of using indy is that we would need a JLS change to define the sugary stuff that calls for the instruction.  That leads us back toward some variation of "obj.fld.volatile.compareAndSet(v0, v1)" or "vh.volatile.compareAndSet(obj, v0, v1)".

No, indy doesn't mean automatically syntactic sugar, you can have indy 
on methods on an API.
It's better to teach javac a new annotation, let say @Invokedynamic, 
that you put on methods. javac will generate an invokedynamic with a 
constant method handle to the implementation of the method if it exist 
(non abstract) as bootstrap argument instead of an invoke* when calling 
those methods.
Unlike @PolymoprhicSignature, @Invokedynamic relies on the classical 
typechecking so no need to cast the result value and workaround the 
issues I have explained above.
Yet, because it use invokedynamic, you can do all the typecheck you want 
inside the bootstrap method and doesn't pay those cost for each calls at 
runtime.

>
> If we had fully polymorphic argument list abstraction, we could define vh.compareAndSet(...) as a method which called invokeExact internally.  That is a much bigger thing than ad hoc signature polymorphism, but possibly with a global payoff.  Hard to do.  Maybe method specialization would help, in a more modest way, to do similar things.
>
> It feels to me that we might go back for some sugar in the end, as a way of minimizing deep spec. complexity by targeting indy.

again, we don't need @PolymorphicSignature + indy, just classical 
typechecking + indy, this is far more simple.

>
> But for now we are learning the most, the fastest, by using the VarHandle prototype.

I beg to disagree because I don't think that playing with something that 
use @PolymorphicSignature is the right way to see the problem.

>
> — John

Rémi



More information about the valhalla-dev mailing list