8062904: TEST_BUG: Tests java/lang/invoke/LFCaching fail when run with -Xcomp option
Reviewed-by: vlivanov
diff --git a/test/java/lang/invoke/LFCaching/LFCachingTestCase.java b/test/java/lang/invoke/LFCaching/LFCachingTestCase.java
index 50a57e4..b5f99bd 100644
--- a/test/java/lang/invoke/LFCaching/LFCachingTestCase.java
+++ b/test/java/lang/invoke/LFCaching/LFCachingTestCase.java
@@ -77,7 +77,7 @@
             }
         } catch (IllegalAccessException | IllegalArgumentException |
                 SecurityException | InvocationTargetException ex) {
-            throw new Error("Unexpected exception: ", ex);
+            throw new Error("Unexpected exception", ex);
         }
     }
 }
diff --git a/test/java/lang/invoke/LFCaching/LFGarbageCollectedTest.java b/test/java/lang/invoke/LFCaching/LFGarbageCollectedTest.java
index a60d460..347fabb 100644
--- a/test/java/lang/invoke/LFCaching/LFGarbageCollectedTest.java
+++ b/test/java/lang/invoke/LFCaching/LFGarbageCollectedTest.java
@@ -59,10 +59,10 @@
         super(testMethod);
     }
 
-     PhantomReference ph;
-     ReferenceQueue rq = new ReferenceQueue();
-     MethodType mtype;
-     Map<String, Object> data;
+    PhantomReference ph;
+    ReferenceQueue rq = new ReferenceQueue();
+    MethodType mtype;
+    Map<String, Object> data;
 
     @Override
     public void doTest() {
@@ -73,7 +73,7 @@
             try {
                 adapter = testCase.getTestCaseMH(data, TestMethods.Kind.ONE);
             } catch (NoSuchMethodException ex) {
-                throw new Error("Unexpected exception: ", ex);
+                throw new Error("Unexpected exception", ex);
             }
             mtype = adapter.type();
             Object lambdaForm = INTERNAL_FORM.invoke(adapter);
@@ -94,12 +94,12 @@
             collectLambdaForm();
         } catch (IllegalAccessException | IllegalArgumentException |
                 InvocationTargetException ex) {
-            throw new Error("Unexpected exception: ", ex);
+            throw new Error("Unexpected exception", ex);
         }
     }
 
     private void collectLambdaForm() throws IllegalAccessException {
-       // Usually, 2 System.GCs are necessary to enqueue a SoftReference.
+        // Usually, 2 System.GCs are necessary to enqueue a SoftReference.
         System.gc();
         System.gc();
 
@@ -129,7 +129,7 @@
         };
     }
 
-   private void dumpTestData() {
+    private void dumpTestData() {
         System.err.println("Test case: " + getTestMethod());
         for (String s : data.keySet()) {
             System.err.printf("\t%20s => %s\n", s, data.get(s));
diff --git a/test/java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java b/test/java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java
index 35c2f97..05d378b 100644
--- a/test/java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java
+++ b/test/java/lang/invoke/LFCaching/LFMultiThreadCachingTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,18 +35,23 @@
  */
 
 import java.lang.invoke.MethodHandle;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CyclicBarrier;
+import com.oracle.testlibrary.jsr292.CodeCacheOverflowProcessor;
 
 /**
  * Multiple threaded lambda forms caching test class.
  */
 public final class LFMultiThreadCachingTest extends LFCachingTestCase {
+
     private static final TestMethods.Kind[] KINDS;
+
     static {
         EnumSet<TestMethods.Kind> set = EnumSet.complementOf(EnumSet.of(TestMethods.Kind.EXCEPT));
         KINDS = set.toArray(new TestMethods.Kind[set.size()]);
@@ -72,21 +77,55 @@
         ConcurrentLinkedQueue<MethodHandle> adapters = new ConcurrentLinkedQueue<>();
         CyclicBarrier begin = new CyclicBarrier(CORES);
         CountDownLatch end = new CountDownLatch(CORES);
+        final Map<Thread, Throwable> threadUncaughtExceptions
+                = Collections.synchronizedMap(new HashMap<Thread, Throwable>(CORES));
+        Thread.UncaughtExceptionHandler exHandler = (t, e) -> {
+            threadUncaughtExceptions.put(t, e);
+        };
         for (int i = 0; i < CORES; ++i) {
             TestMethods.Kind kind = KINDS[i % KINDS.length];
-            new Thread(() -> {
+            Thread t = new Thread(() -> {
                 try {
                     begin.await();
                     adapters.add(getTestMethod().getTestCaseMH(data, kind));
-                } catch (InterruptedException | BrokenBarrierException | IllegalAccessException | NoSuchMethodException ex) {
-                    throw new Error("Unexpected exception: ", ex);
+                } catch (InterruptedException | BrokenBarrierException
+                        | IllegalAccessException | NoSuchMethodException ex) {
+                    throw new Error("Unexpected exception", ex);
                 } finally {
                     end.countDown();
                 }
-            }).start();
+            });
+            t.setUncaughtExceptionHandler(exHandler);
+            t.start();
         }
         try {
             end.await();
+            boolean vmeThrown = false;
+            boolean nonVmeThrown = false;
+            Throwable vme = null;
+            for (Map.Entry<Thread,
+                    Throwable> entry : threadUncaughtExceptions.entrySet()) {
+                Thread t =  entry.getKey();
+                Throwable e = entry.getValue();
+                System.err.printf("%nA thread with name \"%s\" of %d threads"
+                        + " has thrown exception:%n", t.getName(), CORES);
+                e.printStackTrace();
+                if (CodeCacheOverflowProcessor.isThrowableCausedByVME(e)) {
+                    vmeThrown = true;
+                    vme = e;
+                } else {
+                    nonVmeThrown = true;
+                }
+                if (nonVmeThrown) {
+                    throw new Error("One ore more threads have"
+                            + " thrown unexpected exceptions. See log.");
+                }
+                if (vmeThrown) {
+                    throw new Error("One ore more threads have"
+                            + " thrown VirtualMachineError caused by"
+                            + " code cache overflow. See log.", vme);
+                }
+            }
         } catch (InterruptedException ex) {
             throw new Error("Unexpected exception: ", ex);
         }
diff --git a/test/java/lang/invoke/LFCaching/LFSingleThreadCachingTest.java b/test/java/lang/invoke/LFCaching/LFSingleThreadCachingTest.java
index 6a44e44..3d468a4 100644
--- a/test/java/lang/invoke/LFCaching/LFSingleThreadCachingTest.java
+++ b/test/java/lang/invoke/LFCaching/LFSingleThreadCachingTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -62,7 +62,7 @@
             adapter1 = getTestMethod().getTestCaseMH(data, TestMethods.Kind.ONE);
             adapter2 = getTestMethod().getTestCaseMH(data, TestMethods.Kind.TWO);
         } catch (NoSuchMethodException | IllegalAccessException ex) {
-            throw new Error("Unexpected exception: ", ex);
+            throw new Error("Unexpected exception", ex);
         }
         checkLFCaching(adapter1, adapter2);
     }
diff --git a/test/java/lang/invoke/LFCaching/LambdaFormTestCase.java b/test/java/lang/invoke/LFCaching/LambdaFormTestCase.java
index 5d037ca..f945883 100644
--- a/test/java/lang/invoke/LFCaching/LambdaFormTestCase.java
+++ b/test/java/lang/invoke/LFCaching/LambdaFormTestCase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,8 +22,7 @@
  */
 
 import com.oracle.testlibrary.jsr292.Helper;
-import com.sun.management.HotSpotDiagnosticMXBean;
-
+import com.oracle.testlibrary.jsr292.CodeCacheOverflowProcessor;
 import java.lang.invoke.MethodHandle;
 import java.lang.management.GarbageCollectorMXBean;
 import java.lang.management.ManagementFactory;
@@ -45,8 +44,6 @@
  */
 public abstract class LambdaFormTestCase {
 
-    private static final double ITERATIONS_TO_CODE_CACHE_SIZE_RATIO
-            = 45 / (128.0 * 1024 * 1024);
     private static final long TIMEOUT = Helper.IS_THOROUGH ? 0L : (long) (Utils.adjustTimeout(Utils.DEFAULT_TEST_TIMEOUT) * 0.9);
 
     /**
@@ -73,23 +70,89 @@
             REF_FIELD = Reference.class.getDeclaredField("referent");
             REF_FIELD.setAccessible(true);
         } catch (Exception ex) {
-            throw new Error("Unexpected exception: ", ex);
+            throw new Error("Unexpected exception", ex);
         }
 
         gcInfo = ManagementFactory.getGarbageCollectorMXBeans();
-        if (gcInfo.size() == 0)  {
+        if (gcInfo.size() == 0) {
             throw new Error("No GarbageCollectorMXBeans found.");
         }
     }
 
     private final TestMethods testMethod;
-    private static long totalIterations = 0L;
-    private static long doneIterations = 0L;
-    private static boolean passed = true;
-    private static int testCounter = 0;
-    private static int failCounter = 0;
     private long gcCountAtStart;
 
+    private static class TestRun {
+
+        final Function<TestMethods, LambdaFormTestCase> ctor;
+        final Collection<TestMethods> testMethods;
+        final long totalIterations;
+        long doneIterations;
+        long testCounter;
+        long failCounter;
+        boolean passed;
+
+        TestRun(Function<TestMethods, LambdaFormTestCase> ctor, Collection<TestMethods> testMethods) {
+            this.ctor = ctor;
+            this.testMethods = testMethods;
+            long testCaseNum = testMethods.size();
+            long iterations = Math.max(1, Helper.TEST_LIMIT / testCaseNum);
+            System.out.printf("Number of iterations according to -DtestLimit is %d (%d cases)%n",
+                    iterations, iterations * testCaseNum);
+            System.out.printf("Number of iterations is set to %d (%d cases)%n",
+                    iterations, iterations * testCaseNum);
+            System.out.flush();
+            totalIterations = iterations;
+            doneIterations = 0L;
+            testCounter = 0L;
+            failCounter = 0L;
+            passed = true;
+        }
+
+        Boolean doIteration() {
+            if (doneIterations >= totalIterations) {
+                return false;
+            }
+            System.err.println(String.format("Iteration %d:", doneIterations));
+            for (TestMethods testMethod : testMethods) {
+                LambdaFormTestCase testCase = ctor.apply(testMethod);
+                try {
+                    System.err.printf("Tested LF caching feature"
+                            + " with MethodHandles.%s method.%n",
+                            testCase.getTestMethod().name);
+                    Throwable t = CodeCacheOverflowProcessor
+                            .runMHTest(testCase::doTest);
+                    if (t != null) {
+                        return false;
+                    }
+                    System.err.println("PASSED");
+                } catch (OutOfMemoryError oome) {
+                    // Don't swallow OOME so a heap dump can be created.
+                    System.err.println("FAILED");
+                    throw oome;
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                    System.err.printf("FAILED. Caused by %s%n", t.getMessage());
+                    passed = false;
+                    failCounter++;
+                }
+                testCounter++;
+            }
+            doneIterations++;
+            return true;
+        }
+
+        void checkPassed() {
+            if (!passed) {
+                throw new Error(String.format("%d of %d test cases FAILED! %n"
+                        + "Rerun the test with the same \"-Dseed=\" option as in the log file!",
+                        failCounter, testCounter));
+            } else {
+                System.err.printf("All %d test cases PASSED!%n", testCounter);
+            }
+        }
+    }
+
     /**
      * Test case constructor. Generates test cases with random method types for
      * given methods form {@code j.l.i.MethodHandles} class.
@@ -123,65 +186,15 @@
      * @param testMethods list of test methods
      */
     public static void runTests(Function<TestMethods, LambdaFormTestCase> ctor, Collection<TestMethods> testMethods) {
-        long testCaseNum = testMethods.size();
-        totalIterations = Math.max(1, Helper.TEST_LIMIT / testCaseNum);
-        System.out.printf("Number of iterations according to -DtestLimit is %d (%d cases)%n",
-                totalIterations, totalIterations * testCaseNum);
-        HotSpotDiagnosticMXBean hsDiagBean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
-        long codeCacheSize = Long.parseLong(
-                hsDiagBean.getVMOption("ReservedCodeCacheSize").getValue());
-        System.out.printf("Code Cache Size is %d bytes%n", codeCacheSize);
-        long iterationsByCodeCacheSize = (long) (codeCacheSize
-                * ITERATIONS_TO_CODE_CACHE_SIZE_RATIO);
-        System.out.printf("Number of iterations limited by code cache size is %d (%d cases)%n",
-                iterationsByCodeCacheSize, iterationsByCodeCacheSize * testCaseNum);
-        if (totalIterations > iterationsByCodeCacheSize) {
-            totalIterations = iterationsByCodeCacheSize;
-        }
-        System.out.printf("Number of iterations is set to %d (%d cases)%n",
-                totalIterations, totalIterations * testCaseNum);
-        System.out.flush();
-        TimeLimitedRunner runner = new TimeLimitedRunner(TIMEOUT, 4.0d,
-                () -> {
-                    if (doneIterations >= totalIterations) {
-                        return false;
-                    }
-                    System.err.println(String.format("Iteration %d:", doneIterations));
-                    for (TestMethods testMethod : testMethods) {
-                        LambdaFormTestCase testCase = ctor.apply(testMethod);
-                        try {
-                            System.err.printf("Tested LF caching feature with MethodHandles.%s method.%n",
-                                    testCase.getTestMethod().name);
-                            testCase.doTest();
-                            System.err.println("PASSED");
-                        } catch (OutOfMemoryError e) {
-                            // Don't swallow OOME so a heap dump can be created.
-                            System.err.println("FAILED");
-                            throw e;
-                        } catch (Throwable t) {
-                            t.printStackTrace();
-                            System.err.println("FAILED");
-                            passed = false;
-                            failCounter++;
-                        }
-                        testCounter++;
-                    }
-                    doneIterations++;
-                    return true;
-                });
+        LambdaFormTestCase.TestRun run
+                = new LambdaFormTestCase.TestRun(ctor, testMethods);
+        TimeLimitedRunner runner = new TimeLimitedRunner(TIMEOUT, 4.0d, run::doIteration);
         try {
             runner.call();
-        } catch (Throwable t) {
-            t.printStackTrace();
+        } catch (Exception ex) {
             System.err.println("FAILED");
-            throw new Error("Unexpected error!");
+            throw new Error("Unexpected error!", ex);
         }
-        if (!passed) {
-            throw new Error(String.format("%d of %d test cases FAILED! %n"
-                    + "Rerun the test with the same \"-Dseed=\" option as in the log file!",
-                    failCounter, testCounter));
-        } else {
-            System.err.println(String.format("All %d test cases PASSED!", testCounter));
-        }
+        run.checkPassed();
     }
 }