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.compat.annotation.UnsupportedAppUsage;
20 import android.system.Os;
21 import android.system.OsConstants;
22 
23 import java.lang.invoke.MethodHandles;
24 import java.lang.invoke.VarHandle;
25 import java.lang.ref.Cleaner;
26 import java.lang.ref.FinalizerReference;
27 import java.lang.ref.Reference;
28 import java.lang.ref.ReferenceQueue;
29 import java.util.concurrent.CountDownLatch;
30 import java.util.concurrent.TimeoutException;
31 import java.util.concurrent.atomic.AtomicInteger;
32 import libcore.util.EmptyArray;
33 
34 import dalvik.system.VMRuntime;
35 import dalvik.system.VMDebug;
36 
37 import jdk.internal.ref.CleanerImpl;
38 
39 /**
40  * Calls Object.finalize() on objects in the finalizer reference queue. The VM
41  * will abort if any finalize() call takes more than the maximum finalize time
42  * to complete.
43  *
44  * @hide
45  */
46 public final class Daemons {
47     private static final int NANOS_PER_MILLI = 1000 * 1000;
48 
49     // This used to be final. IT IS NOW ONLY WRITTEN. We now update it when we look at the command
50     // line argument, for the benefit of mis-behaved apps that might read it.  SLATED FOR REMOVAL.
51     // There is no reason to use this: Finalizers should not rely on the value. If a finalizer takes
52     // appreciable time, the work should be done elsewhere.  Based on disassembly of Daemons.class,
53     // the value is effectively inlined, so changing the field never did have an effect.
54     // DO NOT USE. FOR ANYTHING. THIS WILL BE REMOVED SHORTLY.
55     @UnsupportedAppUsage
56     private static long MAX_FINALIZE_NANOS = 10L * 1000 * NANOS_PER_MILLI;
57 
58     private static final Daemon[] DAEMONS = new Daemon[] {
59             HeapTaskDaemon.INSTANCE,
60             ReferenceQueueDaemon.INSTANCE,
61             FinalizerDaemon.INSTANCE,
62             FinalizerWatchdogDaemon.INSTANCE,
63     };
64     private static CountDownLatch zygoteStartLatch;
65 
66     private static boolean postZygoteFork = false;
67 
68     @UnsupportedAppUsage
start()69     public static void start() {
70         zygoteStartLatch = new CountDownLatch(DAEMONS.length);
71         for (Daemon daemon : DAEMONS) {
72             daemon.start();
73         }
74     }
75 
startPostZygoteFork()76     public static void startPostZygoteFork() {
77         postZygoteFork = true;
78         start();
79     }
80 
81     @UnsupportedAppUsage
stop()82     public static void stop() {
83         for (Daemon daemon : DAEMONS) {
84             daemon.stop();
85         }
86     }
87 
waitForDaemonStart()88     private static void waitForDaemonStart() throws Exception {
89         zygoteStartLatch.await();
90     }
91 
92     /**
93      * A background task that provides runtime support to the application.
94      * Daemons can be stopped and started, but only so that the zygote can be a
95      * single-threaded process when it forks.
96      */
97     private static abstract class Daemon implements Runnable {
98         @UnsupportedAppUsage
99         private Thread thread;
100         private String name;
101 
Daemon(String name)102         protected Daemon(String name) {
103             this.name = name;
104         }
105 
106         @UnsupportedAppUsage
start()107         public synchronized void start() {
108             startInternal();
109         }
110 
startInternal()111         public void startInternal() {
112             if (thread != null) {
113                 throw new IllegalStateException("already running");
114             }
115             thread = new Thread(ThreadGroup.systemThreadGroup, this, name);
116             thread.setDaemon(true);
117             thread.setSystemDaemon(true);
118             thread.start();
119         }
120 
run()121         public final void run() {
122             if (postZygoteFork) {
123                 // We don't set the priority before the Thread.start() call above because
124                 // Thread.start() will call SetNativePriority and overwrite the desired native
125                 // priority. We (may) use a native priority that doesn't have a corresponding
126                 // java.lang.Thread-level priority (native priorities are more coarse-grained.)
127                 VMRuntime.getRuntime().setSystemDaemonThreadPriority();
128             }
129             zygoteStartLatch.countDown();
130             try {
131                 runInternal();
132                 // This thread is about to exit, and we may have to wait for it to do so.
133                 // Terminate the underlying system thread as quickly as possible.
134                 // Mirroring setSystemDaemonThreadPriority, we only touch the native priority,
135                 // bypassing the rest of setPriority().
136                 Thread.currentThread().setPriority0(Thread.MAX_PRIORITY);
137             } catch (Throwable ex) {
138                 // Usually caught in runInternal. May not o.w. get reported, e.g. in zygote.
139                 // Risk logging redundantly, rather than losing it.
140                 System.logE("Uncaught exception in system thread " + name, ex);
141                 throw ex;
142             }
143         }
144 
145         /*
146          * Do the actual work. Returns normally when asked to stop.
147          */
runInternal()148         public abstract void runInternal();
149 
150         /**
151          * Returns true while the current thread should continue to run; false
152          * when it should return.
153          */
154         @UnsupportedAppUsage
isRunning()155         protected synchronized boolean isRunning() {
156             return thread != null;
157         }
158 
interrupt()159         public synchronized void interrupt() {
160             interrupt(thread);
161         }
162 
interrupt(Thread thread)163         public synchronized void interrupt(Thread thread) {
164             if (thread == null) {
165                 throw new IllegalStateException("not running");
166             }
167             thread.interrupt();
168         }
169 
170         /**
171          * Waits for the runtime thread to stop. This interrupts the thread
172          * currently running the runnable and then waits for it to exit.
173          */
174         @UnsupportedAppUsage
stop()175         public void stop() {
176             // This can be called on shutdown with the GC already disabled.
177             // Allocation either here or while handling the request in the
178             // daemon thread should be minimized.
179             Thread threadToStop;
180             synchronized (this) {
181                 threadToStop = thread;
182                 thread = null;
183             }
184             if (threadToStop == null) {
185                 throw new IllegalStateException("not running");
186             }
187             interrupt(threadToStop);
188             while (true) {
189                 try {
190                     threadToStop.join();
191                     return;
192                 } catch (InterruptedException ignored) {
193                 } catch (OutOfMemoryError ignored) {
194                     // An OOME may be thrown if allocating the InterruptedException failed.
195                 }
196             }
197         }
198 
199         /**
200          * Returns the current stack trace of the thread, or an empty stack trace
201          * if the thread is not currently running.
202          */
getStackTrace()203         public synchronized StackTraceElement[] getStackTrace() {
204             return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT;
205         }
206     }
207 
208     // Allocate these strings on start-up.
209     // Don't declare them private, to minimize chances that the compiler can defer allocation.
210     /**
211      * @hide
212      */
213     public static final String FD_OOM_MESSAGE = "Ignoring unexpected OOME in FinalizerDaemon";
214     /**
215      * @hide
216      */
217     public static final String RQD_OOM_MESSAGE = "Ignoring unexpected OOME in ReferenceQueueDaemon";
218 
219     /**
220      * This heap management thread moves elements from the garbage collector's
221      * pending list to the managed reference queue.
222      */
223     private static class ReferenceQueueDaemon extends Daemon {
224         @UnsupportedAppUsage
225         private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon();
226 
227         // Monitored by FinalizerWatchdogDaemon to make sure we're still working.
228         private final AtomicInteger progressCounter = new AtomicInteger(0);
229 
ReferenceQueueDaemon()230         ReferenceQueueDaemon() {
231             super("ReferenceQueueDaemon");
232         }
233 
runInternal()234         @Override public void runInternal() {
235             FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(FinalizerWatchdogDaemon.RQ_DAEMON);
236 
237             // Call once early to reduce later allocation, and hence chance of OOMEs.
238             FinalizerWatchdogDaemon.INSTANCE.resetTimeouts();
239 
240             while (isRunning()) {
241                 Reference<?> list;
242                 try {
243                     synchronized (ReferenceQueue.class) {
244                         if (ReferenceQueue.unenqueued == null) {
245                             FinalizerWatchdogDaemon.INSTANCE.monitoringNotNeeded(
246                                     FinalizerWatchdogDaemon.RQ_DAEMON);
247                             // Increment after above call. If watchdog saw it active, it should see
248                             // the counter update.
249                             progressCounter.incrementAndGet();
250                             do {
251                                ReferenceQueue.class.wait();
252                             } while (ReferenceQueue.unenqueued == null);
253                             progressCounter.incrementAndGet();
254                             FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(
255                                     FinalizerWatchdogDaemon.RQ_DAEMON);
256                         }
257                         list = ReferenceQueue.unenqueued;
258                         ReferenceQueue.unenqueued = null;
259                     }
260                     ReferenceQueue.enqueuePending(list, progressCounter);
261                     FinalizerWatchdogDaemon.INSTANCE.resetTimeouts();
262                 } catch (InterruptedException e) {
263                     // Happens when we are asked to stop.
264                 } catch (OutOfMemoryError ignored) {
265                     // Very unlikely. Cleaner.clean OOMEs are caught elsewhere, and nothing else
266                     // should allocate regularly. Could result in enqueuePending dropping
267                     // references. Does occur in tests that run out of memory.
268                     System.logW(RQD_OOM_MESSAGE);
269                 }
270             }
271         }
272 
currentlyProcessing()273         Object currentlyProcessing() {
274           return ReferenceQueue.getCurrentTarget();
275         }
276     }
277 
278     private static class FinalizerDaemon extends Daemon {
279         @UnsupportedAppUsage
280         private static final FinalizerDaemon INSTANCE = new FinalizerDaemon();
281         private final ReferenceQueue<Object> queue = FinalizerReference.queue;
282         private final AtomicInteger progressCounter = new AtomicInteger(0);
283         // Object (not reference!) being finalized. Accesses may race!
284         @UnsupportedAppUsage
285         private Object finalizingObject = null;
286 
287         // Track if we are currently logging an exception. We don't want to time out
288         // in the middle.
289         public static int NONE = 0;
290         public static int LOGGING = 1;
291         public static int TIMED_OUT = 2;
292         public volatile int exceptionLoggingState = NONE;
293 
FinalizerDaemon()294         FinalizerDaemon() {
295             super("FinalizerDaemon");
296         }
297 
runInternal()298         @Override public void runInternal() {
299             // This loop may be performance critical, since we need to keep up with mutator
300             // generation of finalizable objects.
301             // We minimize the amount of work we do per finalizable object. For example, we avoid
302             // reading the current time here, since that involves a kernel call per object.  We
303             // limit fast path communication with FinalizerWatchDogDaemon to what's unavoidable: A
304             // non-volatile store to communicate the current finalizable object, e.g. for
305             // reporting, and a release store (lazySet) to a counter.
306             // We do stop the  FinalizerWatchDogDaemon if we have nothing to do for a
307             // potentially extended period.  This prevents the device from waking up regularly
308             // during idle times.
309 
310             // Local copy of progressCounter; saves a fence per increment on ARM.
311             int localProgressCounter = progressCounter.get();
312 
313             FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(
314                     FinalizerWatchdogDaemon.FINALIZER_DAEMON);
315             while (isRunning()) {
316                 try {
317                     // Use non-blocking poll to avoid FinalizerWatchdogDaemon communication
318                     // when busy.
319                     Object nextReference = queue.poll();
320                     if (nextReference != null) {
321                         progressCounter.lazySet(++localProgressCounter);
322                         processReference(nextReference);
323                     } else {
324                         finalizingObject = null;
325                         // Slow path; block.
326                         FinalizerWatchdogDaemon.INSTANCE.monitoringNotNeeded(
327                                 FinalizerWatchdogDaemon.FINALIZER_DAEMON);
328                         // Increment after above call. If watchdog saw it active, it should see
329                         // the counter update.
330                         progressCounter.set(++localProgressCounter);
331                         nextReference = queue.remove();
332                         progressCounter.set(++localProgressCounter);
333                         FinalizerWatchdogDaemon.INSTANCE.monitoringNeeded(
334                                 FinalizerWatchdogDaemon.FINALIZER_DAEMON);
335                         processReference(nextReference);
336                     }
337                 } catch (InterruptedException e) {
338                     // Happens when we are asked to stop.
339                 } catch (OutOfMemoryError ignored) {
340                     // An  OOME here is unlikely to be actionable. Bravely/foolishly continue.
341                     System.logW(FD_OOM_MESSAGE);
342                 }
343             }
344         }
345 
processReference(Object ref)346         private void processReference(Object ref) {
347             if (ref instanceof FinalizerReference finalizingReference) {
348                 finalizingObject = finalizingReference.get();
349                 try {
350                     doFinalize(finalizingReference);
351                 } finally {
352                     // Make really sure we delay any PhantomReference enqueueing until we are
353                     // really done. Possibly redundant, but the rules are complex.
354                     Reference.reachabilityFence(finalizingObject);
355                 }
356             } else if (ref instanceof Cleaner.Cleanable cleanableReference) {
357                 finalizingObject = cleanableReference;
358                 doClean(cleanableReference);
359             } else {
360                 throw new AssertionError("Unknown class was placed into queue: " + ref);
361             }
362         }
363 
364         @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
doFinalize(FinalizerReference<?> reference)365         private void doFinalize(FinalizerReference<?> reference) {
366             FinalizerReference.remove(reference);
367             Object object = reference.get();
368             reference.clear();
369             try {
370                 object.finalize();
371             } catch (Throwable ex) {
372                 // The RI silently swallows these, but Android has always logged.
373                 exceptionLoggingState = LOGGING;
374                 System.logE("Uncaught exception thrown by finalizer", ex);
375                 if (exceptionLoggingState == TIMED_OUT) {
376                   // We would have timed out. Attempt to crash the process here to leave a trace.
377                   throw new AssertionError("Timed out logging finalizer exception", ex);
378                 }
379             } finally {
380                 // Done finalizing, stop holding the object as live.
381                 finalizingObject = null;
382                 exceptionLoggingState = NONE;
383             }
384         }
385 
doClean(Cleaner.Cleanable cleanable)386         private void doClean(Cleaner.Cleanable cleanable) {
387             try {
388                 cleanable.clean();
389                 // We only get here for SystemCleaner, and are thus not constrained to ignore
390                 // exceptions/errors.
391             } finally {
392                 finalizingObject = null;
393             }
394         }
395     }
396 
397     /**
398      * The watchdog exits the VM if either the FinalizerDaemon, or the ReferenceQueueDaemon
399      * gets stuck. We consider the finalizer to be stuck if it spends more than
400      * MAX_FINALIZATION_MILLIS on one instance. We consider ReferenceQueueDaemon to be
401      * potentially stuck if it spends more than MAX_FINALIZATION_MILLIS processing a single
402      * Cleaner or transferring objects into a single queue, but only report if this happens
403      * a few times in a row, to compensate for the fact that multiple Cleaners may be involved.
404      */
405     private static class FinalizerWatchdogDaemon extends Daemon {
406         // Single bit values to identify daemon to be watched.
407         static final int FINALIZER_DAEMON = 1;
408         static final int RQ_DAEMON = 2;
409 
410         @UnsupportedAppUsage
411         private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon();
412         private static final VarHandle VH_ACTION;
413         static {
414             try {
415                 VH_ACTION = MethodHandles
416                         .privateLookupIn(
417                                 CleanerImpl.PhantomCleanableRef.class, MethodHandles.lookup())
418                         .findVarHandle(
419                                 CleanerImpl.PhantomCleanableRef.class, "action", Runnable.class);
420             } catch (NoSuchFieldException | IllegalAccessException e) {
421                 throw new AssertionError("PhantomCleanableRef should have action field", e);
422             }
423         }
424 
425         private int activeWatchees;  // Only synchronized accesses.
426 
427         private long finalizerTimeoutNs = 0;  // Lazily initialized.
428 
429         // We tolerate this many timeouts during an enqueuePending call.
430         // This number is > 1, since we may only report enqueuePending progress rarely.
431         private static final int TOLERATED_REFERENCE_QUEUE_TIMEOUTS = 5;
432         private static final AtomicInteger observedReferenceQueueTimeouts = new AtomicInteger(0);
433 
FinalizerWatchdogDaemon()434         FinalizerWatchdogDaemon() {
435             super("FinalizerWatchdogDaemon");
436         }
437 
resetTimeouts()438         void resetTimeouts() {
439             observedReferenceQueueTimeouts.lazySet(0);
440         }
441 
runInternal()442         @Override public void runInternal() {
443             while (isRunning()) {
444                 if (!sleepUntilNeeded()) {
445                     // We have been interrupted, need to see if this daemon has been stopped.
446                     continue;
447                 }
448                 final TimeoutException exception = waitForProgress();
449                 if (exception != null && !VMDebug.isDebuggerConnected()) {
450                     timedOut(exception);
451                     break;
452                 }
453             }
454         }
455 
456         /**
457          * Wait until something is ready to be finalized.
458          * Return false if we have been interrupted
459          * See also http://code.google.com/p/android/issues/detail?id=22778.
460          */
sleepUntilNeeded()461         private synchronized boolean sleepUntilNeeded() {
462             while (activeWatchees == 0) {
463                 try {
464                     wait();
465                 } catch (InterruptedException e) {
466                     // Daemon.stop may have interrupted us.
467                     return false;
468                 } catch (OutOfMemoryError e) {
469                     return false;
470                 }
471             }
472             return true;
473         }
474 
475         /**
476          * Notify daemon that it's OK to sleep until notified that something is ready to be
477          * finalized.
478          */
monitoringNotNeeded(int whichDaemon)479         private synchronized void monitoringNotNeeded(int whichDaemon) {
480             activeWatchees &= ~whichDaemon;
481         }
482 
483         /**
484          * Notify daemon that there is something ready to be finalized.
485          */
monitoringNeeded(int whichDaemon)486         private synchronized void monitoringNeeded(int whichDaemon) {
487             int oldWatchees = activeWatchees;
488             activeWatchees |= whichDaemon;
489 
490             if (oldWatchees == 0) {
491                 notify();
492             }
493         }
494 
isActive(int whichDaemon)495         private synchronized boolean isActive(int whichDaemon) {
496             return (activeWatchees & whichDaemon) != 0;
497         }
498 
499         /**
500          * Sleep for the given number of nanoseconds, or slightly longer.
501          * @return false if we were interrupted.
502          */
sleepForNanos(long durationNanos)503         private boolean sleepForNanos(long durationNanos) {
504             // It's important to base this on nanoTime(), not currentTimeMillis(), since
505             // the former stops counting when the processor isn't running.
506             long startNanos = System.nanoTime();
507             while (true) {
508                 long elapsedNanos = System.nanoTime() - startNanos;
509                 long sleepNanos = durationNanos - elapsedNanos;
510                 if (sleepNanos <= 0) {
511                     return true;
512                 }
513                 // Ensure the nano time is always rounded up to the next whole millisecond,
514                 // ensuring the delay is >= the requested delay.
515                 long sleepMillis = (sleepNanos + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI;
516                 try {
517                     Thread.sleep(sleepMillis);
518                 } catch (InterruptedException e) {
519                     if (!isRunning()) {
520                         return false;
521                     }
522                 } catch (OutOfMemoryError ignored) {
523                     if (!isRunning()) {
524                         return false;
525                     }
526                 }
527             }
528         }
529 
530 
531         /**
532          * Return null (normal case) or an exception describing what timed out.
533          * Wait VMRuntime.getFinalizerTimeoutMs.  If the FinalizerDaemon took essentially the
534          * whole time processing a single reference, or the ReferenceQueueDaemon failed to make
535          * visible progress during that time, return an exception.  Only called from a single
536          * thread.
537          */
waitForProgress()538         private TimeoutException waitForProgress() {
539             if (finalizerTimeoutNs == 0) {
540                 finalizerTimeoutNs =
541                         NANOS_PER_MILLI * VMRuntime.getRuntime().getFinalizerTimeoutMs();
542                 // Temporary app backward compatibility. Remove eventually.
543                 MAX_FINALIZE_NANOS = finalizerTimeoutNs;
544             }
545             // Read the counter before we read the "active" state the first time, and after
546             // we read it the last time, to guarantee that if the state was ever inactive,
547             // we'll see a changed counter.
548             int finalizerStartCount = FinalizerDaemon.INSTANCE.progressCounter.get();
549             boolean monitorFinalizer = isActive(FINALIZER_DAEMON);
550             int refQueueStartCount = ReferenceQueueDaemon.INSTANCE.progressCounter.get();
551             boolean monitorRefQueue = isActive(RQ_DAEMON);
552             // Avoid remembering object being finalized, so as not to keep it alive.
553             final long startMillis = System.currentTimeMillis();
554             final long startNanos = System.nanoTime();
555 
556             // Rather than just sleeping for finalizerTimeoutNs and checking whether we made
557             // progress, we sleep repeatedly. This means that if our process makes no progress,
558             // e.g. because it is frozen, the watchdog also won't, making it less likely we will
559             // spuriously time out. It does mean that in the normal case, we will go to sleep
560             // and wake up twice per timeout period, rather than once.
561             final int NUM_WAKEUPS = 5;
562             for (int i = 1; i <= NUM_WAKEUPS; ++i) {
563                 if (!sleepForNanos(finalizerTimeoutNs / NUM_WAKEUPS)) {
564                     // Don't report possibly spurious timeout if we are interrupted.
565                     return null;
566                 }
567                 if (monitorFinalizer && isActive(FINALIZER_DAEMON)
568                     && FinalizerDaemon.INSTANCE.progressCounter.get() == finalizerStartCount) {
569                     // Still working on same finalizer or Java 9 Cleaner.
570                     continue;
571                 }
572                 if (monitorRefQueue && isActive(RQ_DAEMON)
573                     && ReferenceQueueDaemon.INSTANCE.progressCounter.get() == refQueueStartCount) {
574                     // Still working on same ReferenceQueue or sun.misc.Cleaner.
575                     continue;
576                 }
577                 // Everything that could make progress, already did. Just sleep for the rest of the
578                 // timeout interval.
579                 if (i < NUM_WAKEUPS) {
580                     sleepForNanos((finalizerTimeoutNs  / NUM_WAKEUPS) * (NUM_WAKEUPS - i));
581                     return null;
582                 }
583             }
584             // Either a state change to inactive, or a task completion would have caused us to see a
585             // counter change. Thus at least one of the daemons appears stuck.
586             if (monitorFinalizer && isActive(FINALIZER_DAEMON)
587                 && FinalizerDaemon.INSTANCE.progressCounter.get() == finalizerStartCount) {
588                 if (FinalizerDaemon.INSTANCE.exceptionLoggingState == FinalizerDaemon.LOGGING) {
589                     // Try to let it finish and crash. We will time out if we get here again.
590                     FinalizerDaemon.INSTANCE.exceptionLoggingState = FinalizerDaemon.TIMED_OUT;
591                 }
592                 // The finalizingObject field was set just before the counter increment, which
593                 // preceded the doFinalize() or doClean() call.  Thus we are guaranteed to get the
594                 // correct finalizing value below, unless doFinalize() just finished as we were
595                 // timing out, in which case we may get null or a later one.
596                 Object finalizing = FinalizerDaemon.INSTANCE.finalizingObject;
597                 System.logE("Was finalizing " + finalizingObjectAsString(finalizing)
598                     + ", now finalizing "
599                     + finalizingObjectAsString(FinalizerDaemon.INSTANCE.finalizingObject));
600                 // Print both time of day and monotonic time differences:
601                 System.logE("Total elapsed millis: "
602                     + (System.currentTimeMillis() - startMillis));
603                 System.logE("Total elapsed nanos: " + (System.nanoTime() - startNanos));
604                 return finalizerTimeoutException(finalizing);
605             }
606             if (monitorRefQueue && isActive(RQ_DAEMON)
607                 && ReferenceQueueDaemon.INSTANCE.progressCounter.get() == refQueueStartCount) {
608                 // Report RQD timeouts only if they occur repeatedly.
609                 // TODO: Consider changing that, but we have historically been more tolerant here,
610                 // since we may not increment the reference counter for every processed queue
611                 // element.
612                 Object current = ReferenceQueueDaemon.INSTANCE.currentlyProcessing();
613                 String currentTarget = current == null ? "unknown" : current.toString();
614                 System.logE("ReferenceQueueDaemon timed out while targeting " + currentTarget
615                         + ". Total nanos: " + (System.nanoTime() - startNanos));
616                 if (observedReferenceQueueTimeouts.incrementAndGet()
617                         > TOLERATED_REFERENCE_QUEUE_TIMEOUTS) {
618                     return refQueueTimeoutException(currentTarget);
619                 }
620             }
621             return null;
622         }
623 
finalizerTimeoutException(Object object)624         private static TimeoutException finalizerTimeoutException(Object object) {
625             if (object == null) {
626                 return new TimeoutException("Unknown finalizer timed out");
627             }
628             StringBuilder messageBuilder = new StringBuilder();
629 
630             if (object instanceof Cleaner.Cleanable) {
631                 messageBuilder.append(VH_ACTION.get(object).getClass().getName());
632             } else {
633                 messageBuilder.append(object.getClass().getName()).append(".finalize()");
634             }
635 
636             messageBuilder.append(" timed out after ")
637                     .append(VMRuntime.getRuntime().getFinalizerTimeoutMs() / 1000)
638                     .append(" seconds");
639             TimeoutException syntheticException = new TimeoutException(messageBuilder.toString());
640             // We use the stack from where finalize() was running to show where it was stuck.
641             syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace());
642             return syntheticException;
643         }
644 
finalizingObjectAsString(Object obj)645         private static String finalizingObjectAsString(Object obj) {
646             if (obj == null) {
647                 return "unknown";
648             }
649             if (obj instanceof Cleaner.Cleanable) {
650                 return VH_ACTION.get(obj).toString();
651             } else {
652                 return obj.toString();
653             }
654         }
655 
refQueueTimeoutException(String target)656         private static TimeoutException refQueueTimeoutException(String target) {
657             String message = "ReferenceQueueDaemon timed out while targeting " + target;
658             return new TimeoutException(message);
659         }
660 
timedOut(TimeoutException exception)661         private static void timedOut(TimeoutException exception) {
662             // Send SIGQUIT to get native stack traces.
663             try {
664                 Os.kill(Os.getpid(), OsConstants.SIGQUIT);
665                 // Sleep a few seconds to let the stack traces print.
666                 Thread.sleep(5000);
667             } catch (Exception e) {
668                 System.logE("failed to send SIGQUIT", e);
669             } catch (OutOfMemoryError ignored) {
670                 // May occur while trying to allocate the exception.
671             }
672 
673             // Ideally, we'd want to do this if this Thread had no handler to dispatch to.
674             // Unfortunately, it's extremely to messy to query whether a given Thread has *some*
675             // handler to dispatch to, either via a handler set on itself, via its ThreadGroup
676             // object or via the defaultUncaughtExceptionHandler.
677             //
678             // As an approximation, we log by hand and exit if there's no pre-exception handler nor
679             // a default uncaught exception handler.
680             //
681             // Note that this condition will only ever be hit by ART host tests and standalone
682             // dalvikvm invocations. All zygote forked process *will* have a pre-handler set
683             // in RuntimeInit and they cannot subsequently override it.
684             if (Thread.getUncaughtExceptionPreHandler() == null &&
685                     Thread.getDefaultUncaughtExceptionHandler() == null) {
686                 // If we have no handler, log and exit.
687                 System.logE(exception.getMessage(), exception);
688                 System.exit(2);
689             }
690 
691             // Otherwise call the handler to do crash reporting.
692             // We don't just throw because we're not the thread that
693             // timed out; we're the thread that detected it.
694             Thread.currentThread().dispatchUncaughtException(exception);
695         }
696     }
697 
698     // Adds a heap trim task to the heap event processor, not called from java. Left for
699     // compatibility purposes due to reflection.
700     @UnsupportedAppUsage
requestHeapTrim()701     public static void requestHeapTrim() {
702         VMRuntime.getRuntime().requestHeapTrim();
703     }
704 
705     // Adds a concurrent GC request task ot the heap event processor, not called from java. Left
706     // for compatibility purposes due to reflection.
requestGC()707     public static void requestGC() {
708         VMRuntime.getRuntime().requestConcurrentGC();
709     }
710 
711     private static class HeapTaskDaemon extends Daemon {
712         private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon();
713 
HeapTaskDaemon()714         HeapTaskDaemon() {
715             super("HeapTaskDaemon");
716         }
717 
718         // Overrides the Daemon.interupt method which is called from Daemons.stop.
interrupt(Thread thread)719         public synchronized void interrupt(Thread thread) {
720             VMRuntime.getRuntime().stopHeapTaskProcessor();
721         }
722 
runInternal()723         @Override public void runInternal() {
724             synchronized (this) {
725                 if (isRunning()) {
726                   // Needs to be synchronized or else we there is a race condition where we start
727                   // the thread, call stopHeapTaskProcessor before we start the heap task
728                   // processor, resulting in a deadlock since startHeapTaskProcessor restarts it
729                   // while the other thread is waiting in Daemons.stop().
730                   VMRuntime.getRuntime().startHeapTaskProcessor();
731                 }
732             }
733             // This runs tasks until we are stopped and there is no more pending task.
734             VMRuntime.getRuntime().runHeapTasks();
735         }
736     }
737 }
738