[concurrency-interest] CAS using a MethodHandle

Rémi Forax forax at univ-mlv.fr
Mon Dec 19 13:28:15 EST 2011


Hi all,
Some time ago, I said that we should try to use a MethodHandle instead 
of an Atomic*FieldUpdater and let the VM inline the method handle so 
there should be no cost (or a little one) compared to directly calling 
unsafe.compareAndSwapObject.

The following code does exactly that, I have also include a small test 
that just demonstrates that it works.
Because I'm neither a benchmark expert nor an assembler expert, I've 
just checked that the method handle is fully inlined by the JIT and that 
the generated code seems to don't have more code than it should.

I know there is a lot of experts on this list, so experts am i wrong ?

cheers,
Rémi

----------------------------------------------------------------------------------------
package java.util.concurrent.atomic;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import sun.misc.Unsafe;

public class Volatiles {
   private static final Unsafe unsafe = Unsafe.getUnsafe();

   private static final MethodHandle CAS_OBJECT;
   static {
     try {
       CAS_OBJECT = MethodHandles.publicLookup().bind(unsafe, 
"compareAndSwapObject",
           MethodType.methodType(boolean.class, Object.class, 
long.class, Object.class, Object.class));
     } catch (NoSuchMethodException|IllegalAccessException e) {
       throw new AssertionError(e.getMessage(), e);
     }
   }

   public static MethodHandle compareAnSet(Class<?> declaringClass, 
String fieldName) {
       Field field;
       int modifiers;
       try {
         field = declaringClass.getDeclaredField(fieldName);
         Class<?> caller = sun.reflect.Reflection.getCallerClass(2);
         modifiers = field.getModifiers();
         sun.reflect.misc.ReflectUtil.ensureMemberAccess(
             caller, declaringClass, null, modifiers);
         sun.reflect.misc.ReflectUtil.checkPackageAccess(declaringClass);
       } catch (NoSuchFieldException|IllegalAccessException e) {
         throw new IllegalArgumentException(e);
       }

       if (Modifier.isStatic(modifiers)) {
         throw new IllegalArgumentException("Field must not be static");
       }
       if (!Modifier.isVolatile(modifiers)) {
         throw new IllegalArgumentException("Field must be volatile");
       }

       long offset = unsafe.objectFieldOffset(field);
       MethodHandle mh = MethodHandles.insertArguments(CAS_OBJECT, 1, 
offset);
       Class<?> fieldType = field.getType();
       return mh.asType(MethodType.methodType(boolean.class, 
declaringClass, fieldType, fieldType));
   }
}

-------------------------------------------------------------------
import java.lang.invoke.MethodHandle;
import java.util.concurrent.atomic.Volatiles;


public class CASTest {
   static class Link<E> {
     final E element;
     final Link<E> next;

     Link(E element, Link<E> next) {
       this.element = element;
       this.next = next;
     }
   }

   static class LinkedLink<E> {
     private volatile Link<E> head;

     private static final MethodHandle headCAS = 
Volatiles.compareAnSet(LinkedLink.class, "head");

     public void add(E element) {
       try {
         for(;;) {
           Link<E> head = this.head;
           Link<E> link = new Link<>(element, head);
           if ((boolean)headCAS.invokeExact(this, head, link)) {
             return;
           }
         }
       } catch (Throwable e) {
         throw new AssertionError(e.getMessage(), e);
       }
     }

     @Override
     public String toString() {
       StringBuilder builder = new StringBuilder().append('[');
       for(Link<?> l = head; l!= null; l = l.next) {
         builder.append(l.element).append(", ");
       }
       int length = builder.length();
       if (length != 0) {
         builder.setLength(length - 2);
       }
       return builder.append(']').toString();
     }
   }


   public static void main(String[] args) {
     LinkedLink<String> list = new LinkedLink<>();

     for(int i=0; i<100000; i++) {
       list.add(Integer.toString(i));
     }

     //System.out.println(list);
   }
}





More information about the Concurrency-interest mailing list