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 android.system.Os;
20 import android.system.OsConstants;
21 import dalvik.system.VMRuntime;
22 import java.lang.ref.FinalizerReference;
23 import java.lang.ref.Reference;
24 import java.lang.ref.ReferenceQueue;
25 import java.util.concurrent.atomic.AtomicBoolean;
26 import java.util.concurrent.atomic.AtomicInteger;
27 import java.util.concurrent.TimeoutException;
28 import libcore.util.EmptyArray;
29 
30 /**
31  * Calls Object.finalize() on objects in the finalizer reference queue. The VM
32  * will abort if any finalize() call takes more than the maximum finalize time
33  * to complete.
34  *
35  * @hide
36  */
37 public final class Daemons {
38     private static final int NANOS_PER_MILLI = 1000 * 1000;
39     private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
40     private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
41 
start()42     public static void start() {
43         ReferenceQueueDaemon.INSTANCE.start();
44         FinalizerDaemon.INSTANCE.start();
45         FinalizerWatchdogDaemon.INSTANCE.start();
46         HeapTaskDaemon.INSTANCE.start();
47     }
48 
stop()49     public static void stop() {
50         HeapTaskDaemon.INSTANCE.stop();
51         ReferenceQueueDaemon.INSTANCE.stop();
52         FinalizerDaemon.INSTANCE.stop();
53         FinalizerWatchdogDaemon.INSTANCE.stop();
54     }
55 
56     /**
57      * A background task that provides runtime support to the application.
58      * Daemons can be stopped and started, but only so that the zygote can be a
59      * single-threaded process when it forks.
60      */
61     private static abstract class Daemon implements Runnable {
62         private Thread thread;
63         private String name;
64 
Daemon(String name)65         protected Daemon(String name) {
66             this.name = name;
67         }
68 
start()69         public synchronized void start() {
70             if (thread != null) {
71                 throw new IllegalStateException("already running");
72             }
73             thread = new Thread(ThreadGroup.systemThreadGroup, this, name);
74             thread.setDaemon(true);
75             thread.start();
76         }
77 
run()78         public abstract void run();
79 
80         /**
81          * Returns true while the current thread should continue to run; false
82          * when it should return.
83          */
isRunning()84         protected synchronized boolean isRunning() {
85             return thread != null;
86         }
87 
interrupt()88         public synchronized void interrupt() {
89             interrupt(thread);
90         }
91 
interrupt(Thread thread)92         public synchronized void interrupt(Thread thread) {
93             if (thread == null) {
94                 throw new IllegalStateException("not running");
95             }
96             thread.interrupt();
97         }
98 
99         /**
100          * Waits for the runtime thread to stop. This interrupts the thread
101          * currently running the runnable and then waits for it to exit.
102          */
stop()103         public void stop() {
104             Thread threadToStop;
105             synchronized (this) {
106                 threadToStop = thread;
107                 thread = null;
108             }
109             if (threadToStop == null) {
110                 throw new IllegalStateException("not running");
111             }
112             interrupt(threadToStop);
113             while (true) {
114                 try {
115                     threadToStop.join();
116                     return;
117                 } catch (InterruptedException ignored) {
118                 } catch (OutOfMemoryError ignored) {
119                     // An OOME may be thrown if allocating the InterruptedException failed.
120                 }
121             }
122         }
123 
124         /**
125          * Returns the current stack trace of the thread, or an empty stack trace
126          * if the thread is not currently running.
127          */
getStackTrace()128         public synchronized StackTraceElement[] getStackTrace() {
129             return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
130         }
131     }
132 
133     /**
134      * This heap management thread moves elements from the garbage collector's
135      * pending list to the managed reference queue.
136      */
137     private static class ReferenceQueueDaemon extends Daemon {
138         private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
139 
ReferenceQueueDaemon()140         ReferenceQueueDaemon() {
141             super("ReferenceQueueDaemon");
142         }
143 
run()144         @Override public void run() {
145             while (isRunning()) {
146                 Reference<?> list;
147                 try {
148                     synchronized (ReferenceQueue.class) {
149                         while (ReferenceQueue.unenqueued == null) {
150                             ReferenceQueue.class.wait();
151                         }
152                         list = ReferenceQueue.unenqueued;
153                         ReferenceQueue.unenqueued = null;
154                     }
155                 } catch (InterruptedException e) {
156                     continue;
157                 } catch (OutOfMemoryError e) {
158                     continue;
159                 }
160                 ReferenceQueue.enqueuePending(list);
161             }
162         }
163     }
164 
165     private static class FinalizerDaemon extends Daemon {
166         private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
167         private final ReferenceQueue<Object> queue = FinalizerReference.queue;
168         private final AtomicInteger progressCounter = new AtomicInteger(0);
169         // Object (not reference!) being finalized. Accesses may race!
170         private Object finalizingObject = null;
171 
FinalizerDaemon()172         FinalizerDaemon() {
173             super("FinalizerDaemon");
174         }
175 
run()176         @Override public void run() {
177             // This loop may be performance critical, since we need to keep up with mutator
178             // generation of finalizable objects.
179             // We minimize the amount of work we do per finalizable object. For example, we avoid
180             // reading the current time here, since that involves a kernel call per object.  We
181             // limit fast path communication with FinalizerWatchDogDaemon to what's unavoidable: A
182             // non-volatile store to communicate the current finalizable object, e.g. for
183             // reporting, and a release store (lazySet) to a counter.
184             // We do stop the  FinalizerWatchDogDaemon if we have nothing to do for a
185             // potentially extended period.  This prevents the device from waking up regularly
186             // during idle times.
187 
188             // Local copy of progressCounter; saves a fence per increment on ARM and MIPS.
189             int localProgressCounter = progressCounter.get();
190 
191             while (isRunning()) {
192                 try {
193                     // Use non-blocking poll to avoid FinalizerWatchdogDaemon communication
194                     // when busy.
195                     FinalizerReference<?> finalizingReference = (FinalizerReference<?>)queue.poll();
196                     if (finalizingReference != null) {
197                         finalizingObject = finalizingReference.get();
198                         progressCounter.lazySet(++localProgressCounter);
199                     } else {
200                         finalizingObject = null;
201                         progressCounter.lazySet(++localProgressCounter);
202                         // Slow path; block.
203                         FinalizerWatchdogDaemon.INSTANCE.goToSleep();
204                         finalizingReference = (FinalizerReference<?>)queue.remove();
205                         finalizingObject = finalizingReference.get();
206                         progressCounter.set(++localProgressCounter);
207                         FinalizerWatchdogDaemon.INSTANCE.wakeUp();
208                     }
209                     doFinalize(finalizingReference);
210                 } catch (InterruptedException ignored) {
211                 } catch (OutOfMemoryError ignored) {
212                 }
213             }
214         }
215 
216         @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
doFinalize(FinalizerReference<?> reference)217         private void doFinalize(FinalizerReference<?> reference) {
218             FinalizerReference.remove(reference);
219             Object object = reference.get();
220             reference.clear();
221             try {
222                 object.finalize();
223             } catch (Throwable ex) {
224                 // The RI silently swallows these, but Android has always logged.
225                 System.logE("Uncaught exception thrown by finalizer", ex);
226             } finally {
227                 // Done finalizing, stop holding the object as live.
228                 finalizingObject = null;
229             }
230         }
231     }
232 
233     /**
234      * The watchdog exits the VM if the finalizer ever gets stuck. We consider
235      * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS
236      * on one instance.
237      */
238     private static class FinalizerWatchdogDaemon extends Daemon {
239         private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon();
240 
241         private boolean needToWork = true;  // Only accessed in synchronized methods.
242 
FinalizerWatchdogDaemon()243         FinalizerWatchdogDaemon() {
244             super("FinalizerWatchdogDaemon");
245         }
246 
run()247         @Override public void run() {
248             while (isRunning()) {
249                 if (!sleepUntilNeeded()) {
250                     // We have been interrupted, need to see if this daemon has been stopped.
251                     continue;
252                 }
253                 final Object finalizing = waitForFinalization();
254                 if (finalizing != null && !VMRuntime.getRuntime().isDebuggerActive()) {
255                     finalizerTimedOut(finalizing);
256                     break;
257                 }
258             }
259         }
260 
261         /**
262          * Wait until something is ready to be finalized.
263          * Return false if we have been interrupted
264          * See also http://code.google.com/p/android/issues/detail?id=22778.
265          */
sleepUntilNeeded()266         private synchronized boolean sleepUntilNeeded() {
267             while (!needToWork) {
268                 try {
269                     wait();
270                 } catch (InterruptedException e) {
271                     // Daemon.stop may have interrupted us.
272                     return false;
273                 } catch (OutOfMemoryError e) {
274                     return false;
275                 }
276             }
277             return true;
278         }
279 
280         /**
281          * Notify daemon that it's OK to sleep until notified that something is ready to be
282          * finalized.
283          */
goToSleep()284         private synchronized void goToSleep() {
285             needToWork = false;
286         }
287 
288         /**
289          * Notify daemon that there is something ready to be finalized.
290          */
wakeUp()291         private synchronized void wakeUp() {
292             needToWork = true;
293             notify();
294         }
295 
getNeedToWork()296         private synchronized boolean getNeedToWork() {
297             return needToWork;
298         }
299 
300         /**
301          * Sleep for the given number of nanoseconds.
302          * @return false if we were interrupted.
303          */
sleepFor(long durationNanos)304         private boolean sleepFor(long durationNanos) {
305             long startNanos = System.nanoTime();
306             while (true) {
307                 long elapsedNanos = System.nanoTime() - startNanos;
308                 long sleepNanos = durationNanos - elapsedNanos;
309                 long sleepMills = sleepNanos / NANOS_PER_MILLI;
310                 if (sleepMills <= 0) {
311                     return true;
312                 }
313                 try {
314                     Thread.sleep(sleepMills);
315                 } catch (InterruptedException e) {
316                     if (!isRunning()) {
317                         return false;
318                     }
319                 } catch (OutOfMemoryError ignored) {
320                     if (!isRunning()) {
321                         return false;
322                     }
323                 }
324             }
325         }
326 
327 
328         /**
329          * Return an object that took too long to finalize or return null.
330          * Wait MAX_FINALIZE_NANOS.  If the FinalizerDaemon took essentially the whole time
331          * processing a single reference, return that reference.  Otherwise return null.
332          */
waitForFinalization()333         private Object waitForFinalization() {
334             long startCount = FinalizerDaemon.INSTANCE.progressCounter.get();
335             // Avoid remembering object being finalized, so as not to keep it alive.
336             if (!sleepFor(MAX_FINALIZE_NANOS)) {
337                 // Don't report possibly spurious timeout if we are interrupted.
338                 return null;
339             }
340             if (getNeedToWork() && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) {
341                 // We assume that only remove() and doFinalize() may take time comparable to
342                 // MAX_FINALIZE_NANOS.
343                 // We observed neither the effect of the gotoSleep() nor the increment preceding a
344                 // later wakeUp. Any remove() call by the FinalizerDaemon during our sleep
345                 // interval must have been followed by a wakeUp call before we checked needToWork.
346                 // But then we would have seen the counter increment.  Thus there cannot have
347                 // been such a remove() call.
348                 // The FinalizerDaemon must not have progressed (from either the beginning or the
349                 // last progressCounter increment) to either the next increment or gotoSleep()
350                 // call.  Thus we must have taken essentially the whole MAX_FINALIZE_NANOS in a
351                 // single doFinalize() call.  Thus it's OK to time out.  finalizingObject was set
352                 // just before the counter increment, which preceded the doFinalize call.  Thus we
353                 // are guaranteed to get the correct finalizing value below, unless doFinalize()
354                 // just finished as we were timing out, in which case we may get null or a later
355                 // one.  In this last case, we are very likely to discard it below.
356                 Object finalizing = FinalizerDaemon.INSTANCE.finalizingObject;
357                 sleepFor(NANOS_PER_SECOND / 2);
358                 // Recheck to make it even less likely we report the wrong finalizing object in
359                 // the case which a very slow finalization just finished as we were timing out.
360                 if (getNeedToWork()
361                         && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) {
362                     return finalizing;
363                 }
364             }
365             return null;
366         }
367 
finalizerTimedOut(Object object)368         private static void finalizerTimedOut(Object object) {
369             // The current object has exceeded the finalization deadline; abort!
370             String message = object.getClass().getName() + ".finalize() timed out after "
371                     + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds";
372             Exception syntheticException = new TimeoutException(message);
373             // We use the stack from where finalize() was running to show where it was stuck.
374             syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
375             Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler();
376             // Send SIGQUIT to get native stack traces.
377             try {
378                 Os.kill(Os.getpid(), OsConstants.SIGQUIT);
379                 // Sleep a few seconds to let the stack traces print.
380                 Thread.sleep(5000);
381             } catch (Exception e) {
382                 System.logE("failed to send SIGQUIT", e);
383             } catch (OutOfMemoryError ignored) {
384                 // May occur while trying to allocate the exception.
385             }
386             if (h == null) {
387                 // If we have no handler, log and exit.
388                 System.logE(message, syntheticException);
389                 System.exit(2);
390             }
391             // Otherwise call the handler to do crash reporting.
392             // We don't just throw because we're not the thread that
393             // timed out; we're the thread that detected it.
394             h.uncaughtException(Thread.currentThread(), syntheticException);
395         }
396     }
397 
398     // Adds a heap trim task to the heap event processor, not called from java. Left for
399     // compatibility purposes due to reflection.
requestHeapTrim()400     public static void requestHeapTrim() {
401         VMRuntime.getRuntime().requestHeapTrim();
402     }
403 
404     // Adds a concurrent GC request task ot the heap event processor, not called from java. Left
405     // for compatibility purposes due to reflection.
requestGC()406     public static void requestGC() {
407         VMRuntime.getRuntime().requestConcurrentGC();
408     }
409 
410     private static class HeapTaskDaemon extends Daemon {
411         private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon();
412 
HeapTaskDaemon()413         HeapTaskDaemon() {
414             super("HeapTaskDaemon");
415         }
416 
417         // Overrides the Daemon.interupt method which is called from Daemons.stop.
interrupt(Thread thread)418         public synchronized void interrupt(Thread thread) {
419             VMRuntime.getRuntime().stopHeapTaskProcessor();
420         }
421 
run()422         @Override public void run() {
423             synchronized (this) {
424                 if (isRunning()) {
425                   // Needs to be synchronized or else we there is a race condition where we start
426                   // the thread, call stopHeapTaskProcessor before we start the heap task
427                   // processor, resulting in a deadlock since startHeapTaskProcessor restarts it
428                   // while the other thread is waiting in Daemons.stop().
429                   VMRuntime.getRuntime().startHeapTaskProcessor();
430                 }
431             }
432             // This runs tasks until we are stopped and there is no more pending task.
433             VMRuntime.getRuntime().runHeapTasks();
434         }
435     }
436 }
437