1 /*
2  * Copyright (C) 2023 The Android Open Source Project
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 import android.system.SystemCleaner;
18 import dalvik.system.VMRuntime;
19 import java.util.concurrent.CountDownLatch;
20 
21 import static java.util.concurrent.TimeUnit.MINUTES;
22 
23 /**
24  * Test SystemCleaner with a bad cleaning action.
25  *
26  * This test is inherently very slightly flaky. It assumes that the system will schedule the
27  * finalizer daemon and finalizer watchdog daemon soon and often enough to reach the timeout and
28  * throw the fatal exception before we time out. Since we build in a 100 second buffer, failures
29  * should be very rare.
30  */
31 public class Main {
main(String[] args)32     public static void main(String[] args) throws Exception {
33         CountDownLatch cleanerWait = new CountDownLatch(1);
34 
35         registerBadCleaner(cleanerWait);
36 
37         // Should have at least two iterations to trigger finalization, but just to make sure run
38         // some more.
39         for (int i = 0; i < 5; i++) {
40             Runtime.getRuntime().gc();
41         }
42 
43         // Now wait for the finalizer to start running. Give it a minute.
44         cleanerWait.await(1, MINUTES);
45 
46         // Now fall asleep with a timeout. The timeout is large enough that we expect the
47         // finalizer daemon to have killed the process before the deadline elapses.
48         // The timeout is also large enough to cover the extra 5 seconds we wait
49         // to dump threads, plus potentially substantial gcstress overhead.
50         // Note: the timeout is here (instead of an infinite sleep) to protect the test
51         //       environment (e.g., in case this is run without a timeout wrapper).
52         final long timeout = 100 * 1000 + VMRuntime.getRuntime().getFinalizerTimeoutMs();
53         long remainingWait = timeout;
54         final long waitStart = System.currentTimeMillis();
55         while (remainingWait > 0) {
56             synchronized (args) {  // Just use an already existing object for simplicity...
57                 try {
58                     args.wait(remainingWait);
59                 } catch (Exception e) {
60                     System.out.println("UNEXPECTED EXCEPTION");
61                 }
62             }
63             remainingWait = timeout - (System.currentTimeMillis() - waitStart);
64         }
65 
66         // We should not get here.
67         System.out.println("UNREACHABLE");
68         System.exit(0);
69     }
70 
registerBadCleaner(CountDownLatch cleanerWait)71     private static void registerBadCleaner(CountDownLatch cleanerWait) {
72         Object obj = new Object();
73         SystemCleaner.cleaner().register(obj, () -> neverEndingCleanup(cleanerWait));
74 
75         System.out.println("About to null reference.");
76         obj = null;  // Not that this would make a difference, could be eliminated earlier.
77     }
78 
snooze(int ms)79     public static void snooze(int ms) {
80         try {
81             Thread.sleep(ms);
82         } catch (InterruptedException ie) {
83             System.out.println("Unexpected interrupt");
84         }
85     }
86 
neverEndingCleanup(CountDownLatch cleanerWait)87     private static void neverEndingCleanup(CountDownLatch cleanerWait) {
88         cleanerWait.countDown();
89 
90         System.out.println("Cleaner started and sleeping briefly...");
91 
92         long start, end;
93         start = System.nanoTime();
94         snooze(2000);
95         end = System.nanoTime();
96         System.out.println("Cleaner done snoozing.");
97 
98         System.out.println("Cleaner sleeping forever now.");
99         while (true) {
100             snooze(10000);
101         }
102     }
103 }
104