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.TimeoutException; 27import libcore.util.EmptyArray; 28 29/** 30 * Calls Object.finalize() on objects in the finalizer reference queue. The VM 31 * will abort if any finalize() call takes more than the maximum finalize time 32 * to complete. 33 * 34 * @hide 35 */ 36public final class Daemons { 37 private static final int NANOS_PER_MILLI = 1000 * 1000; 38 private static final int NANOS_PER_SECOND = NANOS_PER_MILLI * 1000; 39 private static final long MAX_FINALIZE_NANOS = 10L * NANOS_PER_SECOND; 40 41 public static void start() { 42 ReferenceQueueDaemon.INSTANCE.start(); 43 FinalizerDaemon.INSTANCE.start(); 44 FinalizerWatchdogDaemon.INSTANCE.start(); 45 HeapTaskDaemon.INSTANCE.start(); 46 } 47 48 public static void stop() { 49 HeapTaskDaemon.INSTANCE.stop(); 50 ReferenceQueueDaemon.INSTANCE.stop(); 51 FinalizerDaemon.INSTANCE.stop(); 52 FinalizerWatchdogDaemon.INSTANCE.stop(); 53 } 54 55 /** 56 * A background task that provides runtime support to the application. 57 * Daemons can be stopped and started, but only so that the zygote can be a 58 * single-threaded process when it forks. 59 */ 60 private static abstract class Daemon implements Runnable { 61 private Thread thread; 62 private String name; 63 64 protected Daemon(String name) { 65 this.name = name; 66 } 67 68 public synchronized void start() { 69 if (thread != null) { 70 throw new IllegalStateException("already running"); 71 } 72 thread = new Thread(ThreadGroup.systemThreadGroup, this, name); 73 thread.setDaemon(true); 74 thread.start(); 75 } 76 77 public abstract void run(); 78 79 /** 80 * Returns true while the current thread should continue to run; false 81 * when it should return. 82 */ 83 protected synchronized boolean isRunning() { 84 return thread != null; 85 } 86 87 public synchronized void interrupt() { 88 interrupt(thread); 89 } 90 91 public synchronized void interrupt(Thread thread) { 92 if (thread == null) { 93 throw new IllegalStateException("not running"); 94 } 95 thread.interrupt(); 96 } 97 98 /** 99 * Waits for the runtime thread to stop. This interrupts the thread 100 * currently running the runnable and then waits for it to exit. 101 */ 102 public void stop() { 103 Thread threadToStop; 104 synchronized (this) { 105 threadToStop = thread; 106 thread = null; 107 } 108 if (threadToStop == null) { 109 throw new IllegalStateException("not running"); 110 } 111 interrupt(threadToStop); 112 while (true) { 113 try { 114 threadToStop.join(); 115 return; 116 } catch (InterruptedException ignored) { 117 } 118 } 119 } 120 121 /** 122 * Returns the current stack trace of the thread, or an empty stack trace 123 * if the thread is not currently running. 124 */ 125 public synchronized StackTraceElement[] getStackTrace() { 126 return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT; 127 } 128 } 129 130 /** 131 * This heap management thread moves elements from the garbage collector's 132 * pending list to the managed reference queue. 133 */ 134 private static class ReferenceQueueDaemon extends Daemon { 135 private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon(); 136 137 ReferenceQueueDaemon() { 138 super("ReferenceQueueDaemon"); 139 } 140 141 @Override public void run() { 142 while (isRunning()) { 143 Reference<?> list; 144 try { 145 synchronized (ReferenceQueue.class) { 146 while (ReferenceQueue.unenqueued == null) { 147 ReferenceQueue.class.wait(); 148 } 149 list = ReferenceQueue.unenqueued; 150 ReferenceQueue.unenqueued = null; 151 } 152 } catch (InterruptedException e) { 153 continue; 154 } 155 enqueue(list); 156 } 157 } 158 159 private void enqueue(Reference<?> list) { 160 Reference<?> start = list; 161 do { 162 // pendingNext is owned by the GC so no synchronization is required. 163 Reference<?> next = list.pendingNext; 164 list.pendingNext = null; 165 list.enqueueInternal(); 166 list = next; 167 } while (list != start); 168 } 169 } 170 171 private static class FinalizerDaemon extends Daemon { 172 private static final FinalizerDaemon INSTANCE = new FinalizerDaemon(); 173 private final ReferenceQueue<Object> queue = FinalizerReference.queue; 174 private volatile Object finalizingObject; 175 private volatile long finalizingStartedNanos; 176 177 FinalizerDaemon() { 178 super("FinalizerDaemon"); 179 } 180 181 @Override public void run() { 182 while (isRunning()) { 183 // Take a reference, blocking until one is ready or the thread should stop 184 try { 185 doFinalize((FinalizerReference<?>) queue.remove()); 186 } catch (InterruptedException ignored) { 187 } 188 } 189 } 190 191 @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION") 192 private void doFinalize(FinalizerReference<?> reference) { 193 FinalizerReference.remove(reference); 194 Object object = reference.get(); 195 reference.clear(); 196 try { 197 finalizingStartedNanos = System.nanoTime(); 198 finalizingObject = object; 199 synchronized (FinalizerWatchdogDaemon.INSTANCE) { 200 FinalizerWatchdogDaemon.INSTANCE.notify(); 201 } 202 object.finalize(); 203 } catch (Throwable ex) { 204 // The RI silently swallows these, but Android has always logged. 205 System.logE("Uncaught exception thrown by finalizer", ex); 206 } finally { 207 // Done finalizing, stop holding the object as live. 208 finalizingObject = null; 209 } 210 } 211 } 212 213 /** 214 * The watchdog exits the VM if the finalizer ever gets stuck. We consider 215 * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS 216 * on one instance. 217 */ 218 private static class FinalizerWatchdogDaemon extends Daemon { 219 private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon(); 220 221 FinalizerWatchdogDaemon() { 222 super("FinalizerWatchdogDaemon"); 223 } 224 225 @Override public void run() { 226 while (isRunning()) { 227 boolean waitSuccessful = waitForObject(); 228 if (waitSuccessful == false) { 229 // We have been interrupted, need to see if this daemon has been stopped. 230 continue; 231 } 232 boolean finalized = waitForFinalization(); 233 if (!finalized && !VMRuntime.getRuntime().isDebuggerActive()) { 234 Object finalizedObject = FinalizerDaemon.INSTANCE.finalizingObject; 235 // At this point we probably timed out, look at the object in case the finalize 236 // just finished. 237 if (finalizedObject != null) { 238 finalizerTimedOut(finalizedObject); 239 break; 240 } 241 } 242 } 243 } 244 245 private boolean waitForObject() { 246 while (true) { 247 Object object = FinalizerDaemon.INSTANCE.finalizingObject; 248 if (object != null) { 249 return true; 250 } 251 synchronized (this) { 252 // wait until something is ready to be finalized 253 // http://code.google.com/p/android/issues/detail?id=22778 254 try { 255 wait(); 256 } catch (InterruptedException e) { 257 // Daemon.stop may have interrupted us. 258 return false; 259 } 260 } 261 } 262 } 263 264 private void sleepFor(long startNanos, long durationNanos) { 265 while (true) { 266 long elapsedNanos = System.nanoTime() - startNanos; 267 long sleepNanos = durationNanos - elapsedNanos; 268 long sleepMills = sleepNanos / NANOS_PER_MILLI; 269 if (sleepMills <= 0) { 270 return; 271 } 272 try { 273 Thread.sleep(sleepMills); 274 } catch (InterruptedException e) { 275 if (!isRunning()) { 276 return; 277 } 278 } 279 } 280 } 281 282 private boolean waitForFinalization() { 283 long startTime = FinalizerDaemon.INSTANCE.finalizingStartedNanos; 284 sleepFor(startTime, MAX_FINALIZE_NANOS); 285 // If we are finalizing an object and the start time is the same, it must be that we 286 // timed out finalizing something. It may not be the same object that we started out 287 // with but this doesn't matter. 288 return FinalizerDaemon.INSTANCE.finalizingObject == null || 289 FinalizerDaemon.INSTANCE.finalizingStartedNanos != startTime; 290 } 291 292 private static void finalizerTimedOut(Object object) { 293 // The current object has exceeded the finalization deadline; abort! 294 String message = object.getClass().getName() + ".finalize() timed out after " 295 + (MAX_FINALIZE_NANOS / NANOS_PER_SECOND) + " seconds"; 296 Exception syntheticException = new TimeoutException(message); 297 // We use the stack from where finalize() was running to show where it was stuck. 298 syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); 299 Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler(); 300 // Send SIGQUIT to get native stack traces. 301 try { 302 Os.kill(Os.getpid(), OsConstants.SIGQUIT); 303 // Sleep a few seconds to let the stack traces print. 304 Thread.sleep(5000); 305 } catch (Exception e) { 306 System.logE("failed to send SIGQUIT", e); 307 } 308 if (h == null) { 309 // If we have no handler, log and exit. 310 System.logE(message, syntheticException); 311 System.exit(2); 312 } 313 // Otherwise call the handler to do crash reporting. 314 // We don't just throw because we're not the thread that 315 // timed out; we're the thread that detected it. 316 h.uncaughtException(Thread.currentThread(), syntheticException); 317 } 318 } 319 320 // Adds a heap trim task ot the heap event processor, not called from java. Left for 321 // compatibility purposes due to reflection. 322 public static void requestHeapTrim() { 323 VMRuntime.getRuntime().requestHeapTrim(); 324 } 325 326 // Adds a concurrent GC request task ot the heap event processor, not called from java. Left 327 // for compatibility purposes due to reflection. 328 public static void requestGC() { 329 VMRuntime.getRuntime().requestConcurrentGC(); 330 } 331 332 private static class HeapTaskDaemon extends Daemon { 333 private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon(); 334 335 HeapTaskDaemon() { 336 super("HeapTaskDaemon"); 337 } 338 339 // Overrides the Daemon.interupt method which is called from Daemons.stop. 340 public synchronized void interrupt(Thread thread) { 341 VMRuntime.getRuntime().stopHeapTaskProcessor(); 342 } 343 344 @Override public void run() { 345 synchronized (this) { 346 if (isRunning()) { 347 // Needs to be synchronized or else we there is a race condition where we start 348 // the thread, call stopHeapTaskProcessor before we start the heap task 349 // processor, resulting in a deadlock since startHeapTaskProcessor restarts it 350 // while the other thread is waiting in Daemons.stop(). 351 VMRuntime.getRuntime().startHeapTaskProcessor(); 352 } 353 } 354 // This runs tasks until we are stopped and there is no more pending task. 355 VMRuntime.getRuntime().runHeapTasks(); 356 } 357 } 358} 359