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
17package java.lang;
18
19import android.system.Os;
20import android.system.OsConstants;
21import dalvik.system.VMRuntime;
22import java.lang.ref.FinalizerReference;
23import java.lang.ref.Reference;
24import java.lang.ref.ReferenceQueue;
25import java.util.concurrent.atomic.AtomicBoolean;
26import java.util.concurrent.atomic.AtomicInteger;
27import java.util.concurrent.TimeoutException;
28import 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 */
37public 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
42    public static void start() {
43        ReferenceQueueDaemon.INSTANCE.start();
44        FinalizerDaemon.INSTANCE.start();
45        FinalizerWatchdogDaemon.INSTANCE.start();
46        HeapTaskDaemon.INSTANCE.start();
47    }
48
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
65        protected Daemon(String name) {
66            this.name = name;
67        }
68
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
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         */
84        protected synchronized boolean isRunning() {
85            return thread != null;
86        }
87
88        public synchronized void interrupt() {
89            interrupt(thread);
90        }
91
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         */
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         */
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
140        ReferenceQueueDaemon() {
141            super("ReferenceQueueDaemon");
142        }
143
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
172        FinalizerDaemon() {
173            super("FinalizerDaemon");
174        }
175
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")
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
243        FinalizerWatchdogDaemon() {
244            super("FinalizerWatchdogDaemon");
245        }
246
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         */
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         */
284        private synchronized void goToSleep() {
285            needToWork = false;
286        }
287
288        /**
289         * Notify daemon that there is something ready to be finalized.
290         */
291        private synchronized void wakeUp() {
292            needToWork = true;
293            notify();
294        }
295
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         */
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         */
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
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.
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.
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
413        HeapTaskDaemon() {
414            super("HeapTaskDaemon");
415        }
416
417        // Overrides the Daemon.interupt method which is called from Daemons.stop.
418        public synchronized void interrupt(Thread thread) {
419            VMRuntime.getRuntime().stopHeapTaskProcessor();
420        }
421
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