diff options
| -rw-r--r-- | test/030-bad-finalizer/expected.txt | 2 | ||||
| -rw-r--r-- | test/030-bad-finalizer/src/Main.java | 58 |
2 files changed, 51 insertions, 9 deletions
diff --git a/test/030-bad-finalizer/expected.txt b/test/030-bad-finalizer/expected.txt index ee9cfff107..74e208c3f9 100644 --- a/test/030-bad-finalizer/expected.txt +++ b/test/030-bad-finalizer/expected.txt @@ -1,4 +1,4 @@ -About to null reference and request GC. +About to null reference. Finalizer started and spinning... Finalizer done spinning. Finalizer sleeping forever now. diff --git a/test/030-bad-finalizer/src/Main.java b/test/030-bad-finalizer/src/Main.java index 942ee25900..0e69a966f5 100644 --- a/test/030-bad-finalizer/src/Main.java +++ b/test/030-bad-finalizer/src/Main.java @@ -14,26 +14,60 @@ * limitations under the License. */ +import java.util.concurrent.CountDownLatch; +import static java.util.concurrent.TimeUnit.MINUTES; + /** * Test a class with a bad finalizer. + * + * This test is inherently flaky. It assumes that the system will schedule the finalizer daemon + * and finalizer watchdog daemon enough to reach the timeout and throwing the fatal exception. */ public class Main { - public static void main(String[] args) { - BadFinalizer bf = new BadFinalizer(); + public static void main(String[] args) throws Exception { + CountDownLatch finalizerWait = new CountDownLatch(1); - System.out.println("About to null reference and request GC."); - bf = null; - Runtime.getRuntime().gc(); + // A separate method to ensure no dex register keeps the object alive. + createBadFinalizer(finalizerWait); - for (int i = 0; i < 8; i++) { - snooze(4000); + // Should have at least two iterations to trigger finalization, but just to make sure run + // some more. + for (int i = 0; i < 5; i++) { Runtime.getRuntime().gc(); } + // Now wait for the finalizer to start running. Give it a minute. + finalizerWait.await(1, MINUTES); + + // Now fall asleep with a timeout. The timeout is large enough that we expect the + // finalizer daemon to have killed the process before the deadline elapses. + // Note: the timeout is here (instead of an infinite sleep) to protect the test + // environment (e.g., in case this is run without a timeout wrapper). + final long timeout = 60 * 1000; // 1 minute. + long remainingWait = timeout; + final long waitStart = System.currentTimeMillis(); + while (remainingWait > 0) { + synchronized (args) { // Just use an already existing object for simplicity... + try { + args.wait(remainingWait); + } catch (Exception e) { + } + } + remainingWait = timeout - (System.currentTimeMillis() - waitStart); + } + + // We should not get here. System.out.println("UNREACHABLE"); System.exit(0); } + private static void createBadFinalizer(CountDownLatch finalizerWait) { + BadFinalizer bf = new BadFinalizer(finalizerWait); + + System.out.println("About to null reference."); + bf = null; // Not that this would make a difference, could be eliminated earlier. + } + public static void snooze(int ms) { try { Thread.sleep(ms); @@ -45,9 +79,17 @@ public class Main { * Class with a bad finalizer. */ public static class BadFinalizer { + private CountDownLatch finalizerWait; + private volatile int j = 0; // Volatile in an effort to curb loop optimization. + + public BadFinalizer(CountDownLatch finalizerWait) { + this.finalizerWait = finalizerWait; + } + protected void finalize() { + finalizerWait.countDown(); + System.out.println("Finalizer started and spinning..."); - int j = 0; /* spin for a bit */ long start, end; |