| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package java.lang; |
| |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.system.Os; |
| import android.system.OsConstants; |
| |
| import java.lang.ref.FinalizerReference; |
| import java.lang.ref.Reference; |
| import java.lang.ref.ReferenceQueue; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeoutException; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import libcore.util.EmptyArray; |
| |
| import dalvik.system.VMRuntime; |
| import dalvik.system.VMDebug; |
| |
| /** |
| * Calls Object.finalize() on objects in the finalizer reference queue. The VM |
| * will abort if any finalize() call takes more than the maximum finalize time |
| * to complete. |
| * |
| * @hide |
| */ |
| public final class Daemons { |
| private static final int NANOS_PER_MILLI = 1000 * 1000; |
| |
| // This used to be final. IT IS NOW ONLY WRITTEN. We now update it when we look at the command |
| // line argument, for the benefit of mis-behaved apps that might read it. SLATED FOR REMOVAL. |
| // There is no reason to use this: Finalizers should not rely on the value. If a finalizer takes |
| // appreciable time, the work should be done elsewhere. Based on disassembly of Daemons.class, |
| // the value is effectively inlined, so changing the field never did have an effect. |
| // DO NOT USE. FOR ANYTHING. THIS WILL BE REMOVED SHORTLY. |
| @UnsupportedAppUsage |
| private static long MAX_FINALIZE_NANOS = 10L * 1000 * NANOS_PER_MILLI; |
| |
| private static final Daemon[] DAEMONS = new Daemon[] { |
| HeapTaskDaemon.INSTANCE, |
| ReferenceQueueDaemon.INSTANCE, |
| FinalizerDaemon.INSTANCE, |
| FinalizerWatchdogDaemon.INSTANCE, |
| }; |
| private static final CountDownLatch POST_ZYGOTE_START_LATCH = new CountDownLatch(DAEMONS.length); |
| private static final CountDownLatch PRE_ZYGOTE_START_LATCH = new CountDownLatch(DAEMONS.length); |
| |
| private static boolean postZygoteFork = false; |
| |
| @UnsupportedAppUsage |
| public static void start() { |
| for (Daemon daemon : DAEMONS) { |
| daemon.start(); |
| } |
| } |
| |
| public static void startPostZygoteFork() { |
| postZygoteFork = true; |
| for (Daemon daemon : DAEMONS) { |
| daemon.startPostZygoteFork(); |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public static void stop() { |
| for (Daemon daemon : DAEMONS) { |
| daemon.stop(); |
| } |
| } |
| |
| private static void waitForDaemonStart() throws Exception { |
| if (postZygoteFork) { |
| POST_ZYGOTE_START_LATCH.await(); |
| } else { |
| PRE_ZYGOTE_START_LATCH.await(); |
| } |
| } |
| |
| /** |
| * A background task that provides runtime support to the application. |
| * Daemons can be stopped and started, but only so that the zygote can be a |
| * single-threaded process when it forks. |
| */ |
| private static abstract class Daemon implements Runnable { |
| @UnsupportedAppUsage |
| private Thread thread; |
| private String name; |
| private boolean postZygoteFork; |
| |
| protected Daemon(String name) { |
| this.name = name; |
| } |
| |
| @UnsupportedAppUsage |
| public synchronized void start() { |
| startInternal(); |
| } |
| |
| public synchronized void startPostZygoteFork() { |
| postZygoteFork = true; |
| startInternal(); |
| } |
| |
| public void startInternal() { |
| if (thread != null) { |
| throw new IllegalStateException("already running"); |
| } |
| thread = new Thread(ThreadGroup.systemThreadGroup, this, name); |
| thread.setDaemon(true); |
| thread.setSystemDaemon(true); |
| thread.start(); |
| } |
| |
| public final void run() { |
| if (postZygoteFork) { |
| // We don't set the priority before the Thread.start() call above because |
| // Thread.start() will call SetNativePriority and overwrite the desired native |
| // priority. We (may) use a native priority that doesn't have a corresponding |
| // java.lang.Thread-level priority (native priorities are more coarse-grained.) |
| VMRuntime.getRuntime().setSystemDaemonThreadPriority(); |
| POST_ZYGOTE_START_LATCH.countDown(); |
| } else { |
| PRE_ZYGOTE_START_LATCH.countDown(); |
| } |
| runInternal(); |
| } |
| |
| public abstract void runInternal(); |
| |
| /** |
| * Returns true while the current thread should continue to run; false |
| * when it should return. |
| */ |
| @UnsupportedAppUsage |
| protected synchronized boolean isRunning() { |
| return thread != null; |
| } |
| |
| public synchronized void interrupt() { |
| interrupt(thread); |
| } |
| |
| public synchronized void interrupt(Thread thread) { |
| if (thread == null) { |
| throw new IllegalStateException("not running"); |
| } |
| thread.interrupt(); |
| } |
| |
| /** |
| * Waits for the runtime thread to stop. This interrupts the thread |
| * currently running the runnable and then waits for it to exit. |
| */ |
| @UnsupportedAppUsage |
| public void stop() { |
| Thread threadToStop; |
| synchronized (this) { |
| threadToStop = thread; |
| thread = null; |
| } |
| if (threadToStop == null) { |
| throw new IllegalStateException("not running"); |
| } |
| interrupt(threadToStop); |
| while (true) { |
| try { |
| threadToStop.join(); |
| return; |
| } catch (InterruptedException ignored) { |
| } catch (OutOfMemoryError ignored) { |
| // An OOME may be thrown if allocating the InterruptedException failed. |
| } |
| } |
| } |
| |
| /** |
| * Returns the current stack trace of the thread, or an empty stack trace |
| * if the thread is not currently running. |
| */ |
| public synchronized StackTraceElement[] getStackTrace() { |
| return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT; |
| } |
| } |
| |
| /** |
| * This heap management thread moves elements from the garbage collector's |
| * pending list to the managed reference queue. |
| */ |
| private static class ReferenceQueueDaemon extends Daemon { |
| @UnsupportedAppUsage |
| private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon(); |
| |
| ReferenceQueueDaemon() { |
| super("ReferenceQueueDaemon"); |
| } |
| |
| @Override public void runInternal() { |
| while (isRunning()) { |
| Reference<?> list; |
| try { |
| synchronized (ReferenceQueue.class) { |
| while (ReferenceQueue.unenqueued == null) { |
| ReferenceQueue.class.wait(); |
| } |
| list = ReferenceQueue.unenqueued; |
| ReferenceQueue.unenqueued = null; |
| } |
| } catch (InterruptedException e) { |
| continue; |
| } catch (OutOfMemoryError e) { |
| continue; |
| } |
| ReferenceQueue.enqueuePending(list); |
| } |
| } |
| } |
| |
| private static class FinalizerDaemon extends Daemon { |
| @UnsupportedAppUsage |
| private static final FinalizerDaemon INSTANCE = new FinalizerDaemon(); |
| private final ReferenceQueue<Object> queue = FinalizerReference.queue; |
| private final AtomicInteger progressCounter = new AtomicInteger(0); |
| // Object (not reference!) being finalized. Accesses may race! |
| @UnsupportedAppUsage |
| private Object finalizingObject = null; |
| |
| FinalizerDaemon() { |
| super("FinalizerDaemon"); |
| } |
| |
| @Override public void runInternal() { |
| // This loop may be performance critical, since we need to keep up with mutator |
| // generation of finalizable objects. |
| // We minimize the amount of work we do per finalizable object. For example, we avoid |
| // reading the current time here, since that involves a kernel call per object. We |
| // limit fast path communication with FinalizerWatchDogDaemon to what's unavoidable: A |
| // non-volatile store to communicate the current finalizable object, e.g. for |
| // reporting, and a release store (lazySet) to a counter. |
| // We do stop the FinalizerWatchDogDaemon if we have nothing to do for a |
| // potentially extended period. This prevents the device from waking up regularly |
| // during idle times. |
| |
| // Local copy of progressCounter; saves a fence per increment on ARM and MIPS. |
| int localProgressCounter = progressCounter.get(); |
| |
| while (isRunning()) { |
| try { |
| // Use non-blocking poll to avoid FinalizerWatchdogDaemon communication |
| // when busy. |
| FinalizerReference<?> finalizingReference = (FinalizerReference<?>)queue.poll(); |
| if (finalizingReference != null) { |
| finalizingObject = finalizingReference.get(); |
| progressCounter.lazySet(++localProgressCounter); |
| } else { |
| finalizingObject = null; |
| progressCounter.lazySet(++localProgressCounter); |
| // Slow path; block. |
| FinalizerWatchdogDaemon.INSTANCE.goToSleep(); |
| finalizingReference = (FinalizerReference<?>)queue.remove(); |
| finalizingObject = finalizingReference.get(); |
| progressCounter.set(++localProgressCounter); |
| FinalizerWatchdogDaemon.INSTANCE.wakeUp(); |
| } |
| doFinalize(finalizingReference); |
| } catch (InterruptedException ignored) { |
| } catch (OutOfMemoryError ignored) { |
| } |
| } |
| } |
| |
| @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION") |
| private void doFinalize(FinalizerReference<?> reference) { |
| FinalizerReference.remove(reference); |
| Object object = reference.get(); |
| reference.clear(); |
| try { |
| object.finalize(); |
| } catch (Throwable ex) { |
| // The RI silently swallows these, but Android has always logged. |
| System.logE("Uncaught exception thrown by finalizer", ex); |
| } finally { |
| // Done finalizing, stop holding the object as live. |
| finalizingObject = null; |
| } |
| } |
| } |
| |
| /** |
| * The watchdog exits the VM if the finalizer ever gets stuck. We consider |
| * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS |
| * on one instance. |
| */ |
| private static class FinalizerWatchdogDaemon extends Daemon { |
| @UnsupportedAppUsage |
| private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon(); |
| |
| private boolean needToWork = true; // Only accessed in synchronized methods. |
| |
| private long finalizerTimeoutNs = 0; // Lazily initialized. |
| |
| FinalizerWatchdogDaemon() { |
| super("FinalizerWatchdogDaemon"); |
| } |
| |
| @Override public void runInternal() { |
| while (isRunning()) { |
| if (!sleepUntilNeeded()) { |
| // We have been interrupted, need to see if this daemon has been stopped. |
| continue; |
| } |
| final Object finalizing = waitForFinalization(); |
| if (finalizing != null && !VMDebug.isDebuggerConnected()) { |
| finalizerTimedOut(finalizing); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Wait until something is ready to be finalized. |
| * Return false if we have been interrupted |
| * See also http://code.google.com/p/android/issues/detail?id=22778. |
| */ |
| private synchronized boolean sleepUntilNeeded() { |
| while (!needToWork) { |
| try { |
| wait(); |
| } catch (InterruptedException e) { |
| // Daemon.stop may have interrupted us. |
| return false; |
| } catch (OutOfMemoryError e) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Notify daemon that it's OK to sleep until notified that something is ready to be |
| * finalized. |
| */ |
| private synchronized void goToSleep() { |
| needToWork = false; |
| } |
| |
| /** |
| * Notify daemon that there is something ready to be finalized. |
| */ |
| private synchronized void wakeUp() { |
| needToWork = true; |
| notify(); |
| } |
| |
| private synchronized boolean getNeedToWork() { |
| return needToWork; |
| } |
| |
| /** |
| * Sleep for the given number of nanoseconds, or slightly longer. |
| * @return false if we were interrupted. |
| */ |
| private boolean sleepForNanos(long durationNanos) { |
| // It's important to base this on nanoTime(), not currentTimeMillis(), since |
| // the former stops counting when the processor isn't running. |
| long startNanos = System.nanoTime(); |
| while (true) { |
| long elapsedNanos = System.nanoTime() - startNanos; |
| long sleepNanos = durationNanos - elapsedNanos; |
| if (sleepNanos <= 0) { |
| return true; |
| } |
| // Ensure the nano time is always rounded up to the next whole millisecond, |
| // ensuring the delay is >= the requested delay. |
| long sleepMillis = (sleepNanos + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI; |
| try { |
| Thread.sleep(sleepMillis); |
| } catch (InterruptedException e) { |
| if (!isRunning()) { |
| return false; |
| } |
| } catch (OutOfMemoryError ignored) { |
| if (!isRunning()) { |
| return false; |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Return an object that took too long to finalize or return null. |
| * Wait VMRuntime.getFinalizerTimeoutMs. If the FinalizerDaemon took essentially the |
| * whole time processing a single reference, return that reference. Otherwise return |
| * null. Only called from a single thread. |
| */ |
| private Object waitForFinalization() { |
| if (finalizerTimeoutNs == 0) { |
| finalizerTimeoutNs = |
| NANOS_PER_MILLI * VMRuntime.getRuntime().getFinalizerTimeoutMs(); |
| // Temporary app backward compatibility. Remove eventually. |
| MAX_FINALIZE_NANOS = finalizerTimeoutNs; |
| } |
| long startCount = FinalizerDaemon.INSTANCE.progressCounter.get(); |
| // Avoid remembering object being finalized, so as not to keep it alive. |
| if (!sleepForNanos(finalizerTimeoutNs)) { |
| // Don't report possibly spurious timeout if we are interrupted. |
| return null; |
| } |
| if (getNeedToWork() && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) { |
| // We assume that only remove() and doFinalize() may take time comparable to |
| // the finalizer timeout. |
| // We observed neither the effect of the gotoSleep() nor the increment preceding a |
| // later wakeUp. Any remove() call by the FinalizerDaemon during our sleep |
| // interval must have been followed by a wakeUp call before we checked needToWork. |
| // But then we would have seen the counter increment. Thus there cannot have |
| // been such a remove() call. |
| // The FinalizerDaemon must not have progressed (from either the beginning or the |
| // last progressCounter increment) to either the next increment or gotoSleep() |
| // call. Thus we must have taken essentially the whole finalizerTimeoutMs in a |
| // single doFinalize() call. Thus it's OK to time out. finalizingObject was set |
| // just before the counter increment, which preceded the doFinalize call. Thus we |
| // are guaranteed to get the correct finalizing value below, unless doFinalize() |
| // just finished as we were timing out, in which case we may get null or a later |
| // one. In this last case, we are very likely to discard it below. |
| Object finalizing = FinalizerDaemon.INSTANCE.finalizingObject; |
| sleepForNanos(500 * NANOS_PER_MILLI); |
| // Recheck to make it even less likely we report the wrong finalizing object in |
| // the case which a very slow finalization just finished as we were timing out. |
| if (getNeedToWork() |
| && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) { |
| return finalizing; |
| } |
| } |
| return null; |
| } |
| |
| private static void finalizerTimedOut(Object object) { |
| // The current object has exceeded the finalization deadline; abort! |
| String message = object.getClass().getName() + ".finalize() timed out after " |
| + VMRuntime.getRuntime().getFinalizerTimeoutMs() / 1000 + " seconds"; |
| Exception syntheticException = new TimeoutException(message); |
| // We use the stack from where finalize() was running to show where it was stuck. |
| syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); |
| |
| // Send SIGQUIT to get native stack traces. |
| try { |
| Os.kill(Os.getpid(), OsConstants.SIGQUIT); |
| // Sleep a few seconds to let the stack traces print. |
| Thread.sleep(5000); |
| } catch (Exception e) { |
| System.logE("failed to send SIGQUIT", e); |
| } catch (OutOfMemoryError ignored) { |
| // May occur while trying to allocate the exception. |
| } |
| |
| // Ideally, we'd want to do this if this Thread had no handler to dispatch to. |
| // Unfortunately, it's extremely to messy to query whether a given Thread has *some* |
| // handler to dispatch to, either via a handler set on itself, via its ThreadGroup |
| // object or via the defaultUncaughtExceptionHandler. |
| // |
| // As an approximation, we log by hand an exit if there's no pre-exception handler nor |
| // a default uncaught exception handler. |
| // |
| // Note that this condition will only ever be hit by ART host tests and standalone |
| // dalvikvm invocations. All zygote forked process *will* have a pre-handler set |
| // in RuntimeInit and they cannot subsequently override it. |
| if (Thread.getUncaughtExceptionPreHandler() == null && |
| Thread.getDefaultUncaughtExceptionHandler() == null) { |
| // If we have no handler, log and exit. |
| System.logE(message, syntheticException); |
| System.exit(2); |
| } |
| |
| // Otherwise call the handler to do crash reporting. |
| // We don't just throw because we're not the thread that |
| // timed out; we're the thread that detected it. |
| Thread.currentThread().dispatchUncaughtException(syntheticException); |
| } |
| } |
| |
| // Adds a heap trim task to the heap event processor, not called from java. Left for |
| // compatibility purposes due to reflection. |
| @UnsupportedAppUsage |
| public static void requestHeapTrim() { |
| VMRuntime.getRuntime().requestHeapTrim(); |
| } |
| |
| // Adds a concurrent GC request task ot the heap event processor, not called from java. Left |
| // for compatibility purposes due to reflection. |
| public static void requestGC() { |
| VMRuntime.getRuntime().requestConcurrentGC(); |
| } |
| |
| private static class HeapTaskDaemon extends Daemon { |
| private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon(); |
| |
| HeapTaskDaemon() { |
| super("HeapTaskDaemon"); |
| } |
| |
| // Overrides the Daemon.interupt method which is called from Daemons.stop. |
| public synchronized void interrupt(Thread thread) { |
| VMRuntime.getRuntime().stopHeapTaskProcessor(); |
| } |
| |
| @Override public void runInternal() { |
| synchronized (this) { |
| if (isRunning()) { |
| // Needs to be synchronized or else we there is a race condition where we start |
| // the thread, call stopHeapTaskProcessor before we start the heap task |
| // processor, resulting in a deadlock since startHeapTaskProcessor restarts it |
| // while the other thread is waiting in Daemons.stop(). |
| VMRuntime.getRuntime().startHeapTaskProcessor(); |
| } |
| } |
| // This runs tasks until we are stopped and there is no more pending task. |
| VMRuntime.getRuntime().runHeapTasks(); |
| } |
| } |
| } |