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
17package libcore.java.lang.ref;
18
19import java.util.concurrent.CountDownLatch;
20import java.util.concurrent.atomic.AtomicBoolean;
21import java.util.concurrent.atomic.AtomicInteger;
22import junit.framework.TestCase;
23
24public final class FinalizeTest extends TestCase {
25
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     */
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. */
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
74    private void exceptionInConstructor() {
75        try {
76            new ConstructionFails();
77        } catch (AssertionError expected) {
78        }
79    }
80
81    // http://b/issue?id=2136462
82    public void testBackFromTheDead() throws Exception {
83        exceptionInConstructor();
84        FinalizationTester.induceFinalization();
85        assertTrue("object whose constructor threw was not finalized", ConstructionFails.finalized);
86    }
87
88    static class ConstructionFails {
89        private static boolean finalized;
90
91        ConstructionFails() {
92            throw new AssertionError();
93        }
94
95        @Override protected void finalize() throws Throwable {
96            finalized = true;
97        }
98    }
99
100    /**
101     * The finalizer watch dog exits the VM if any object takes more than 10 s
102     * to finalize. Check that objects near that limit are okay.
103     */
104    public void testWatchdogDoesNotFailForObjectsThatAreNearTheDeadline() throws Exception {
105        CountDownLatch latch = new CountDownLatch(5);
106        createSlowFinalizer(   1, latch);
107        createSlowFinalizer(1000, latch);
108        createSlowFinalizer(2000, latch);
109        createSlowFinalizer(4000, latch);
110        createSlowFinalizer(8000, latch);
111        FinalizationTester.induceFinalization();
112        latch.await();
113    }
114
115    public void createSlowFinalizer(final long millis, final CountDownLatch latch) {
116        new Object() {
117            @Override protected void finalize() throws Throwable {
118                System.out.println("finalize sleeping " + millis + " ms");
119                Thread.sleep(millis);
120                latch.countDown();
121            }
122        };
123    }
124
125    /**
126     * Make sure that System.runFinalization() returns even if the finalization
127     * queue is never completely empty. http://b/4193517
128     */
129    public void testSystemRunFinalizationReturnsEvenIfQueueIsNonEmpty() throws Exception {
130        AtomicInteger count = new AtomicInteger();
131        AtomicBoolean keepGoing = new AtomicBoolean(true);
132        createChainedFinalizer(count, keepGoing);
133        FinalizationTester.induceFinalization();
134        keepGoing.set(false);
135        assertTrue(count.get() > 0);
136    }
137
138    public void createChainedFinalizer(final AtomicInteger counter, final AtomicBoolean keepGoing) {
139        new Object() {
140            @Override protected void finalize() throws Throwable {
141                int count = counter.incrementAndGet();
142                System.out.println(count);
143                if (keepGoing.get()) {
144                    createChainedFinalizer(counter, keepGoing); // recursive!
145                }
146                System.gc();
147                FinalizationTester.enqueueReferences();
148            }
149        };
150    }
151}
152