8006500: compiler/8004741/Test8004741.java fails intermediately
Summary: rewrote the test to be more reliable, add test for invalid size exception
Reviewed-by: kvn
--- a/hotspot/test/compiler/8004741/Test8004741.java Fri Jan 25 16:31:47 2013 -0800
+++ b/hotspot/test/compiler/8004741/Test8004741.java Fri Jan 25 16:09:14 2013 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2013, 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
@@ -25,70 +25,160 @@
* @test Test8004741.java
* @bug 8004741
* @summary Missing compiled exception handle table entry for multidimensional array allocation
+ * @run main/othervm -Xmx64m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:+StressCompiledExceptionHandlers -XX:+SafepointALot -XX:GuaranteedSafepointInterval=100 Test8004741
* @run main/othervm -Xmx64m -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:+StressCompiledExceptionHandlers Test8004741
- *
*/
import java.util.*;
public class Test8004741 extends Thread {
+ static int passed = 0;
+
+ /**
+ * Loop forever allocating 2-d arrays.
+ * Catches and rethrows all exceptions; in the case of ThreadDeath, increments passed.
+ * Note that passed is incremented here because this is the exception handler with
+ * the smallest scope; we only want to declare success in the case where it is highly
+ * likely that the test condition
+ * (exception in 2-d array alloc interrupted by ThreadDeath)
+ * actually occurs.
+ */
static int[][] test(int a, int b) throws Exception {
- int[][] ar = null;
+ int[][] ar;
try {
ar = new int[a][b];
- } catch (Error e) {
- System.out.println("test got Error");
- passed = true;
- throw(e);
- } catch (Exception e) {
- System.out.println("test got Exception");
+ } catch (ThreadDeath e) {
+ System.out.println("test got ThreadDeath");
+ passed++;
throw(e);
}
return ar;
}
- static boolean passed = false;
+ /* Cookbook wait-notify to track progress of test thread. */
+ Object progressLock = new Object();
+ private static final int NOT_STARTED = 0;
+ private static final int RUNNING = 1;
+ private static final int STOPPING = 2;
+
+ int progressState = NOT_STARTED;
- public void run() {
- System.out.println("test started");
- try {
- while(true) {
- test(2,20000);
+ void toState(int state) {
+ synchronized (progressLock) {
+ progressState = state;
+ progressLock.notify();
+ }
+ }
+
+ void waitFor(int state) {
+ synchronized (progressLock) {
+ while (progressState < state) {
+ try {
+ progressLock.wait();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ System.out.println("unexpected InterruptedException");
+ fail();
}
- } catch (ThreadDeath e) {
- System.out.println("test got ThreadDeath");
- passed = true;
- } catch (Error e) {
- e.printStackTrace();
- System.out.println("test got Error");
- } catch (Exception e) {
- e.printStackTrace();
- System.out.println("test got Exception");
+ }
+ if (progressState > state) {
+ System.out.println("unexpected test state change, expected " +
+ state + " but saw " + progressState);
+ fail();
+ }
+ }
+ }
+
+ /**
+ * Loops running test until some sort of an exception or error,
+ * expects to see ThreadDeath.
+ */
+ public void run() {
+ try {
+ // Print before state change, so that other thread is most likely
+ // to see this thread executing calls to test() in a loop.
+ System.out.println("thread running");
+ toState(RUNNING);
+ while (true) {
+ // (2,2) (2,10) (2,100) were observed to tickle the bug;
+ test(2, 100);
}
+ } catch (ThreadDeath e) {
+ // nothing to say, passing was incremented by the test.
+ } catch (Throwable e) {
+ e.printStackTrace();
+ System.out.println("unexpected Throwable " + e);
+ fail();
+ }
+ toState(STOPPING);
+ }
+
+ /**
+ * Runs a single trial of the test in a thread.
+ * No single trial is definitive, since the ThreadDeath
+ * exception might not land in the tested region of code.
+ */
+ public static void threadTest() throws InterruptedException {
+ Test8004741 t = new Test8004741();
+ t.start();
+ t.waitFor(RUNNING);
+ Thread.sleep(100);
+ System.out.println("stopping thread");
+ t.stop();
+ t.waitFor(STOPPING);
+ t.join();
}
public static void main(String[] args) throws Exception {
+ // Warm up "test"
+ // t will never be started.
for (int n = 0; n < 11000; n++) {
- test(2, 20);
+ test(2, 100);
+ }
+
+ // Will this sleep help ensure that the compiler is run?
+ Thread.sleep(500);
+ passed = 0;
+
+ try {
+ test(-1, 100);
+ System.out.println("Missing NegativeArraySizeException #1");
+ fail();
+ } catch ( java.lang.NegativeArraySizeException e ) {
+ System.out.println("Saw expected NegativeArraySizeException #1");
}
- // First test exception catch
- Test8004741 t = new Test8004741();
+ try {
+ test(100, -1);
+ fail();
+ System.out.println("Missing NegativeArraySizeException #2");
+ fail();
+ } catch ( java.lang.NegativeArraySizeException e ) {
+ System.out.println("Saw expected NegativeArraySizeException #2");
+ }
- passed = false;
- t.start();
- Thread.sleep(1000);
- t.stop();
+ /* Test repetitions. If the test succeeds-mostly, it succeeds,
+ * as long as it does not crash (the outcome if the exception range
+ * table entry for the array allocation is missing).
+ */
+ int N = 12;
+ for (int n = 0; n < N; n++) {
+ threadTest();
+ }
- Thread.sleep(5000);
- t.join();
- if (passed) {
+ if (passed > N/2) {
+ System.out.println("Saw " + passed + " out of " + N + " possible ThreadDeath hits");
System.out.println("PASSED");
} else {
- System.out.println("FAILED");
- System.exit(97);
+ System.out.println("Too few ThreadDeath hits; expected at least " + N/2 +
+ " but saw only " + passed);
+ fail();
}
}
+ static void fail() {
+ System.out.println("FAILED");
+ System.exit(97);
+ }
};