Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8145371

ClassCastException thrown in LambdaFormEditor.getInCache

    XMLWordPrintable

Details

    • b01
    • x86_64
    • windows_7

    Description

      FULL PRODUCT VERSION :
      java version "1.8.0_66"
      Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
      Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      MethodHandle#customize() does not properly check for concurrent access to MethodHandle#form, which can lead to a ClassCastException ("java.lang.invoke.LambdaForm cannot be cast to [Ljava.lang.invoke.LambdaFormEditor$Transform;") in LambdaFormEditor#getInCache(). This is an intermittent issue (*) and probably only reliable to reproduce with debugger support. For the STR I've used the Eclipse debugger, but it should also be possible to use other debuggers.

      (*) It requires a context switch at a specific program point and only happened once for me in an actual program.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Add a break point in java.lang.invoke.MethodHandle#customize(), l. 1441 (`LambdaForm newForm = form.customize(this);`).
      2. Run the attached program with `-Djava.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD=0` (threshold only set to 0 for easier STR).
      3. When the break point is triggered and the current thread is suspended, execute `((java.lang.Thread)Thread.currentThread().getClass().getDeclaredField("thread2").get(Thread.currentThread())).start();` in the debugger to start the second thread.
      4. The new thread will also be suspended at the break point in MethodHandle#customize(), continue execution of "Thread 2" ("Thread 1" is still suspended at this point).
      5. When "Thread 2" has finished, resume execution of "Thread 1".
      6. The break point is hit again, now simply resume execution.


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The following output is printed to System.out:
      ---
      (A, C)
      (A, B)
      (A, D)
      ---
      ACTUAL -
      "(A, D)" is not printed to System.out, instead a ClassCastException is thrown:
      ---
      (A, C)
      (A, B)
      java.lang.ClassCastException: java.lang.invoke.LambdaForm cannot be cast to [Ljava.lang.invoke.LambdaFormEditor$Transform;
      ---

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      java.lang.ClassCastException: java.lang.invoke.LambdaForm cannot be cast to [Ljava.lang.invoke.LambdaFormEditor$Transform;
      at java.lang.invoke.LambdaFormEditor.getInCache(LambdaFormEditor.java:275)
      at java.lang.invoke.LambdaFormEditor.bindArgumentForm(LambdaFormEditor.java:450)
      at java.lang.invoke.LambdaFormEditor.bindArgumentL(LambdaFormEditor.java:403)
      at java.lang.invoke.BoundMethodHandle.bindArgumentL(BoundMethodHandle.java:97)
      at java.lang.invoke.MethodHandles.insertArguments(MethodHandles.java:2375)
      at es6draft.intltests.invoke.CustomizeConcurrentBug$Thread1.run(CustomizeConcurrentBug.java:39)

      REPRODUCIBILITY :
      This bug can be reproduced rarely.

      ---------- BEGIN SOURCE ----------
      import java.lang.invoke.MethodHandle;
      import java.lang.invoke.MethodHandles;
      import java.lang.invoke.MethodHandles.Lookup;
      import java.lang.invoke.MethodType;

      public class CustomizeConcurrentBug {
          public static void main(String[] args) throws Throwable {
              // Run with: -Djava.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD=0

              Lookup lookup = MethodHandles.publicLookup();
              MethodHandle mh = lookup.findStatic(CustomizeConcurrentBug.class, "print",
                      MethodType.methodType(void.class, String.class, String.class));
              MethodHandle mh1 = MethodHandles.insertArguments(mh, 0, "A");

              Thread1 t1 = new Thread1(mh1);
              t1.start();
          }

          public static void print(String x, String y) {
              System.out.printf("(%s, %s)%n", x, y);
          }

          public static class Thread1 extends Thread {
              public final MethodHandle mh;
              public final Thread2 thread2;

              Thread1(MethodHandle mh) {
                  super("Thread 1");
                  this.mh = mh;
                  this.thread2 = new Thread2(mh);
              }

              @Override
              public void run() {
                  try {
                      mh.invokeExact("B");
                      MethodHandles.insertArguments(mh, 0, "D").invokeExact();
                  } catch (Throwable e) {
                      e.printStackTrace();
                  }
              }
          }

          public static class Thread2 extends Thread {
              public final MethodHandle mh;

              Thread2(MethodHandle mh) {
                  super("Thread 2");
                  this.mh = mh;
              }

              @Override
              public void run() {
                  try {
                      mh.invokeExact("C");
                  } catch (Throwable e) {
                      e.printStackTrace();
                  }
              }
          }
      }

      ---------- END SOURCE ----------

      Attachments

        Activity

          People

            martin Martin Buchholz
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: