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