1 /*
2  * Copyright (C) 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.java.lang.ref;
18 
19 import java.util.concurrent.CountDownLatch;
20 import java.util.concurrent.atomic.AtomicBoolean;
21 import java.util.concurrent.atomic.AtomicInteger;
22 import junit.framework.TestCase;
23 
24 public final class FinalizeTest extends TestCase {
25 
testFinalizeIsCalled()26     public void testFinalizeIsCalled() throws Exception {
27         AtomicBoolean finalized = new AtomicBoolean();
28         createFinalizableObject(finalized);
29 
30         FinalizationTester.induceFinalization();
31         if (!finalized.get()) {
32             fail("object not yet finalized");
33         }
34     }
35 
36     /**
37      * Test verifies that runFinalization() does not mess up objects
38      * that should be finalized later on. http://b/6907299
39      */
testInducedFinalization()40     public void testInducedFinalization() throws Exception {
41         AtomicBoolean finalized1 = new AtomicBoolean();
42         AtomicBoolean finalized2 = new AtomicBoolean();
43         createFinalizableObject(finalized1);
44         createFinalizableObject(finalized2);
45         FinalizationTester.induceFinalization();
46         if (!finalized1.get() || !finalized2.get()) {
47             fail("not yet finalized: " + finalized1.get() + " " + finalized2.get());
48         }
49     }
50 
51     /** Do not inline this method; that could break non-precise GCs. See FinalizationTester. */
createFinalizableObject(final AtomicBoolean finalized)52     private X createFinalizableObject(final AtomicBoolean finalized) {
53         X result = new X() {
54             @Override protected void finalize() throws Throwable {
55                 super.finalize();
56                 finalized.set(true);
57             }
58         };
59         FinalizationTester.induceFinalization();
60         // Dance around a bit to discourage dx from realizing that 'result' is no longer live.
61         boolean wasFinalized = finalized.get();
62         if (wasFinalized) {
63             fail("finalizer called early"); // ...because 'result' is still live until we return.
64         }
65         // But we don't actually want to return 'result' because then we'd have to worry about
66         // the caller accidentally keeping it live.
67         return wasFinalized ? result : null;
68     }
69 
70     static class X {}
71 
72     // Helper function since we do not want a vreg to keep the allocated object live.
73     // For b/25851249
exceptionInConstructor()74     private void exceptionInConstructor() {
75         boolean thrown = false;
76         try {
77             new ConstructionFails();
78             // can't fail() here since AssertionFailedError extends AssertionError, which
79             // we expect
80         } catch (AssertionError expected) {
81             thrown = true;
82         }
83         if (!thrown) {
84             fail();
85         }
86     }
87 
88     // http://b/issue?id=2136462
testBackFromTheDead()89     public void testBackFromTheDead() throws Exception {
90         exceptionInConstructor();
91         FinalizationTester.induceFinalization();
92         assertTrue("object whose constructor threw was not finalized", ConstructionFails.finalized);
93     }
94 
95     static class ConstructionFails {
96         private static boolean finalized;
97 
ConstructionFails()98         ConstructionFails() {
99             throw new AssertionError();
100         }
101 
finalize()102         @Override protected void finalize() throws Throwable {
103             finalized = true;
104         }
105     }
106 
107     /**
108      * The finalizer watch dog exits the VM if any object takes more than 10 s
109      * to finalize. Check that objects near that limit are okay.
110      */
testWatchdogDoesNotFailForObjectsThatAreNearTheDeadline()111     public void testWatchdogDoesNotFailForObjectsThatAreNearTheDeadline() throws Exception {
112         CountDownLatch latch = new CountDownLatch(3);
113         createSlowFinalizer(   1, latch);
114         createSlowFinalizer(1000, latch);
115         createSlowFinalizer(8000, latch);
116         FinalizationTester.induceFinalization();
117         latch.await();
118     }
119 
createSlowFinalizer(final long millis, final CountDownLatch latch)120     public void createSlowFinalizer(final long millis, final CountDownLatch latch) {
121         new Object() {
122             @Override protected void finalize() throws Throwable {
123                 System.out.println("finalize sleeping " + millis + " ms");
124                 Thread.sleep(millis);
125                 latch.countDown();
126             }
127         };
128     }
129 
130     /**
131      * Make sure that System.runFinalization() returns even if the finalization
132      * queue is never completely empty. http://b/4193517
133      */
testSystemRunFinalizationReturnsEvenIfQueueIsNonEmpty()134     public void testSystemRunFinalizationReturnsEvenIfQueueIsNonEmpty() throws Exception {
135         AtomicInteger count = new AtomicInteger();
136         AtomicBoolean keepGoing = new AtomicBoolean(true);
137         createChainedFinalizer(count, keepGoing);
138         FinalizationTester.induceFinalization();
139         keepGoing.set(false);
140         assertTrue(count.get() > 0);
141     }
142 
createChainedFinalizer(final AtomicInteger counter, final AtomicBoolean keepGoing)143     public void createChainedFinalizer(final AtomicInteger counter, final AtomicBoolean keepGoing) {
144         new Object() {
145             @Override protected void finalize() throws Throwable {
146                 int count = counter.incrementAndGet();
147                 System.out.println(count);
148                 if (keepGoing.get()) {
149                     createChainedFinalizer(counter, keepGoing); // recursive!
150                 }
151                 System.gc();
152                 FinalizationTester.enqueueReferences();
153             }
154         };
155     }
156 }
157