• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 package java.lang;
18 
19 import dalvik.system.VMRuntime;
20 import java.lang.ref.FinalizerReference;
21 import java.lang.ref.Reference;
22 import java.lang.ref.ReferenceQueue;
23 import java.util.concurrent.TimeoutException;
24 import libcore.util.EmptyArray;
25 
26 /**
27  * Calls Object.finalize() on objects in the finalizer reference queue. The VM
28  * will abort if any finalize() call takes more than the maximum finalize time
29  * to complete.
30  *
31  * @hide
32  */
33 public final class Daemons {
34     private static final int NANOS_PER_MILLI = 1000 * 1000;
35     private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
36     private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
37 
start()38     public static void start() {
39         ReferenceQueueDaemon.INSTANCE.start();
40         FinalizerDaemon.INSTANCE.start();
41         FinalizerWatchdogDaemon.INSTANCE.start();
42     }
43 
stop()44     public static void stop() {
45         ReferenceQueueDaemon.INSTANCE.stop();
46         FinalizerDaemon.INSTANCE.stop();
47         FinalizerWatchdogDaemon.INSTANCE.stop();
48     }
49 
50     /**
51      * A background task that provides runtime support to the application.
52      * Daemons can be stopped and started, but only so that the zygote can be a
53      * single-threaded process when it forks.
54      */
55     private static abstract class Daemon implements Runnable {
56         private Thread thread;
57 
start()58         public synchronized void start() {
59             if (thread != null) {
60                 throw new IllegalStateException("already running");
61             }
62             thread = new Thread(ThreadGroup.mSystem, this,
63                 getClass().getSimpleName());
64             thread.setDaemon(true);
65             thread.start();
66         }
67 
run()68         public abstract void run();
69 
70         /**
71          * Returns true while the current thread should continue to run; false
72          * when it should return.
73          */
isRunning()74         protected synchronized boolean isRunning() {
75             return thread != null;
76         }
77 
interrupt()78         public synchronized void interrupt() {
79             if (thread == null) {
80                 throw new IllegalStateException("not running");
81             }
82             thread.interrupt();
83         }
84 
85         /**
86          * Waits for the runtime thread to stop. This interrupts the thread
87          * currently running the runnable and then waits for it to exit.
88          */
stop()89         public void stop() {
90             Thread threadToStop;
91             synchronized (this) {
92                 threadToStop = thread;
93                 thread = null;
94             }
95             if (threadToStop == null) {
96                 throw new IllegalStateException("not running");
97             }
98             threadToStop.interrupt();
99             while (true) {
100                 try {
101                     threadToStop.join();
102                     return;
103                 } catch (InterruptedException ignored) {
104                 }
105             }
106         }
107 
108         /**
109          * Returns the current stack trace of the thread, or an empty stack trace
110          * if the thread is not currently running.
111          */
getStackTrace()112         public synchronized StackTraceElement[] getStackTrace() {
113             return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
114         }
115     }
116 
117     /**
118      * This heap management thread moves elements from the garbage collector's
119      * pending list to the managed reference queue.
120      */
121     private static class ReferenceQueueDaemon extends Daemon {
122         private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
123 
run()124         @Override public void run() {
125             while (isRunning()) {
126                 Reference<?> list;
127                 try {
128                     synchronized (ReferenceQueue.class) {
129                         while (ReferenceQueue.unenqueued == null) {
130                             ReferenceQueue.class.wait();
131                         }
132                         list = ReferenceQueue.unenqueued;
133                         ReferenceQueue.unenqueued = null;
134                     }
135                 } catch (InterruptedException e) {
136                     continue;
137                 }
138                 enqueue(list);
139             }
140         }
141 
enqueue(Reference<?> list)142         private void enqueue(Reference<?> list) {
143             while (list != null) {
144                 Reference<?> reference;
145                 // pendingNext is owned by the GC so no synchronization is required
146                 if (list == list.pendingNext) {
147                     reference = list;
148                     reference.pendingNext = null;
149                     list = null;
150                 } else {
151                     reference = list.pendingNext;
152                     list.pendingNext = reference.pendingNext;
153                     reference.pendingNext = null;
154                 }
155                 reference.enqueueInternal();
156             }
157         }
158     }
159 
160     private static class FinalizerDaemon extends Daemon {
161         private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
162         private final ReferenceQueue<Object> queue = FinalizerReference.queue;
163         private volatile Object finalizingObject;
164         private volatile long finalizingStartedNanos;
165 
run()166         @Override public void run() {
167             while (isRunning()) {
168                 // Take a reference, blocking until one is ready or the thread should stop
169                 try {
170                     doFinalize((FinalizerReference<?>) queue.remove());
171                 } catch (InterruptedException ignored) {
172                 }
173             }
174         }
175 
176         @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
doFinalize(FinalizerReference<?> reference)177         private void doFinalize(FinalizerReference<?> reference) {
178             FinalizerReference.remove(reference);
179             Object object = reference.get();
180             reference.clear();
181             try {
182                 finalizingStartedNanos = System.nanoTime();
183                 finalizingObject = object;
184                 synchronized (FinalizerWatchdogDaemon.INSTANCE) {
185                     FinalizerWatchdogDaemon.INSTANCE.notify();
186                 }
187                 object.finalize();
188             } catch (Throwable ex) {
189                 // The RI silently swallows these, but Android has always logged.
190                 System.logE("Uncaught exception thrown by finalizer", ex);
191             } finally {
192                 finalizingObject = null;
193             }
194         }
195     }
196 
197     /**
198      * The watchdog exits the VM if the finalizer ever gets stuck. We consider
199      * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS
200      * on one instance.
201      */
202     private static class FinalizerWatchdogDaemon extends Daemon {
203         private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon();
204 
run()205         @Override public void run() {
206             while (isRunning()) {
207                 Object object = waitForObject();
208                 if (object == null) {
209                     // We have been interrupted, need to see if this daemon has been stopped.
210                     continue;
211                 }
212                 boolean finalized = waitForFinalization(object);
213                 if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) {
214                     finalizerTimedOut(object);
215                     break;
216                 }
217             }
218         }
219 
waitForObject()220         private Object waitForObject() {
221             while (true) {
222                 Object object = FinalizerDaemon.INSTANCE.finalizingObject;
223                 if (object != null) {
224                     return object;
225                 }
226                 synchronized (this) {
227                     // wait until something is ready to be finalized
228                     // http://code.google.com/p/android/issues/detail?id=22778
229                     try {
230                         wait();
231                     } catch (InterruptedException e) {
232                         // Daemon.stop may have interrupted us.
233                         return null;
234                     }
235                 }
236             }
237         }
238 
sleepFor(long startNanos, long durationNanos)239         private void sleepFor(long startNanos, long durationNanos) {
240             while (true) {
241                 long elapsedNanos = System.nanoTime() - startNanos;
242                 long sleepNanos = durationNanos - elapsedNanos;
243                 long sleepMills = sleepNanos / NANOS_PER_MILLI;
244                 if (sleepMills <= 0) {
245                     return;
246                 }
247                 try {
248                     Thread.sleep(sleepMills);
249                 } catch (InterruptedException e) {
250                     if (!isRunning()) {
251                         return;
252                     }
253                 }
254             }
255         }
256 
waitForFinalization(Object object)257         private boolean waitForFinalization(Object object) {
258             sleepFor(FinalizerDaemon.INSTANCE.finalizingStartedNanos, MAX_FINALIZE_NANOS);
259             return object != FinalizerDaemon.INSTANCE.finalizingObject;
260         }
261 
finalizerTimedOut(Object object)262         private static void finalizerTimedOut(Object object) {
263             // The current object has exceeded the finalization deadline; abort!
264             String message = object.getClass().getName() + ".finalize() timed out after "
265                     + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds";
266             Exception syntheticException = new TimeoutException(message);
267             // We use the stack from where finalize() was running to show where it was stuck.
268             syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
269             Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
270             if (h == null) {
271                 // If we have no handler, log and exit.
272                 System.logE(message, syntheticException);
273                 System.exit(2);
274             }
275             // Otherwise call the handler to do crash reporting.
276             // We don't just throw because we're not the thread that
277             // timed out; we're the thread that detected it.
278             h.uncaughtException(Thread.currentThread(), syntheticException);
279         }
280     }
281 }
282