Home | History | Annotate | Download | only in lang
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package java.lang;
     18 
     19 import android.system.Os;
     20 import android.system.OsConstants;
     21 import dalvik.system.VMRuntime;
     22 import java.lang.ref.FinalizerReference;
     23 import java.lang.ref.Reference;
     24 import java.lang.ref.ReferenceQueue;
     25 import java.util.concurrent.atomic.AtomicBoolean;
     26 import java.util.concurrent.atomic.AtomicInteger;
     27 import java.util.concurrent.TimeoutException;
     28 import libcore.util.EmptyArray;
     29 
     30 /**
     31  * Calls Object.finalize() on objects in the finalizer reference queue. The VM
     32  * will abort if any finalize() call takes more than the maximum finalize time
     33  * to complete.
     34  *
     35  * @hide
     36  */
     37 public final class Daemons {
     38     private static final int NANOS_PER_MILLI = 1000 * 1000;
     39     private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000;
     40     private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND;
     41 
     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 
    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 
    415             // Ideally, we'd want to do this if this Thread had no handler to dispatch to.
    416             // Unfortunately, it's extremely to messy to query whether a given Thread has *some*
    417             // handler to dispatch to, either via a handler set on itself, via its ThreadGroup
    418             // object or via the defaultUncaughtExceptionHandler.
    419             //
    420             // As an approximation, we log by hand an exit if there's no pre-exception handler nor
    421             // a default uncaught exception handler.
    422             //
    423             // Note that this condition will only ever be hit by ART host tests and standalone
    424             // dalvikvm invocations. All zygote forked process *will* have a pre-handler set
    425             // in RuntimeInit and they cannot subsequently override it.
    426             if (Thread.getUncaughtExceptionPreHandler() == null &&
    427                     Thread.getDefaultUncaughtExceptionHandler() == null) {
    428                 // If we have no handler, log and exit.
    429                 System.logE(message, syntheticException);
    430                 System.exit(2);
    431             }
    432 
    433             // Otherwise call the handler to do crash reporting.
    434             // We don't just throw because we're not the thread that
    435             // timed out; we're the thread that detected it.
    436             Thread.currentThread().dispatchUncaughtException(syntheticException);
    437         }
    438     }
    439 
    440     // Adds a heap trim task to the heap event processor, not called from java. Left for
    441     // compatibility purposes due to reflection.
    442     public static void requestHeapTrim() {
    443         VMRuntime.getRuntime().requestHeapTrim();
    444     }
    445 
    446     // Adds a concurrent GC request task ot the heap event processor, not called from java. Left
    447     // for compatibility purposes due to reflection.
    448     public static void requestGC() {
    449         VMRuntime.getRuntime().requestConcurrentGC();
    450     }
    451 
    452     private static class HeapTaskDaemon extends Daemon {
    453         private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon();
    454 
    455         HeapTaskDaemon() {
    456             super("HeapTaskDaemon");
    457         }
    458 
    459         // Overrides the Daemon.interupt method which is called from Daemons.stop.
    460         public synchronized void interrupt(Thread thread) {
    461             VMRuntime.getRuntime().stopHeapTaskProcessor();
    462         }
    463 
    464         @Override public void runInternal() {
    465             synchronized (this) {
    466                 if (isRunning()) {
    467                   // Needs to be synchronized or else we there is a race condition where we start
    468                   // the thread, call stopHeapTaskProcessor before we start the heap task
    469                   // processor, resulting in a deadlock since startHeapTaskProcessor restarts it
    470                   // while the other thread is waiting in Daemons.stop().
    471                   VMRuntime.getRuntime().startHeapTaskProcessor();
    472                 }
    473             }
    474             // This runs tasks until we are stopped and there is no more pending task.
    475             VMRuntime.getRuntime().runHeapTasks();
    476         }
    477     }
    478 }
    479