ART: Make test 030 slightly more robust
Run a serious of immediate GCs, synchronizer with the finalizer
starintg and then let the main thread sleep for a minute, in an
effort to make the test deterministic and get the finalizer
daemon scheduled.
Test: m test-art-host-run-test-030-bad-finalizer
Change-Id: Id379d8147ecc8f6aa0a7d8d5058030a4c2d4de46
diff --git a/test/030-bad-finalizer/expected.txt b/test/030-bad-finalizer/expected.txt
index ee9cfff..74e208c 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 942ee25..0e69a96 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 @@
* 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;