Rahul Ravikumar | 0533600 | 2019-10-14 15:04:32 -0700 | [diff] [blame] | 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.annotation.compat.UnsupportedAppUsage; |
| 22 | import dalvik.system.VMRuntime; |
| 23 | import java.lang.ref.FinalizerReference; |
| 24 | import java.lang.ref.Reference; |
| 25 | import java.lang.ref.ReferenceQueue; |
| 26 | import java.util.concurrent.atomic.AtomicBoolean; |
| 27 | import java.util.concurrent.atomic.AtomicInteger; |
| 28 | import java.util.concurrent.CountDownLatch; |
| 29 | import java.util.concurrent.TimeoutException; |
| 30 | import libcore.util.EmptyArray; |
| 31 | |
| 32 | /** |
| 33 | * Calls Object.finalize() on objects in the finalizer reference queue. The VM |
| 34 | * will abort if any finalize() call takes more than the maximum finalize time |
| 35 | * to complete. |
| 36 | * |
| 37 | * @hide |
| 38 | */ |
| 39 | public final class Daemons { |
| 40 | private static final int NANOS_PER_MILLI = 1000 * 1000; |
| 41 | |
| 42 | // This used to be final. IT IS NOW ONLY WRITTEN. We now update it when we look at the command |
| 43 | // line argument, for the benefit of mis-behaved apps that might read it. SLATED FOR REMOVAL. |
| 44 | // There is no reason to use this: Finalizers should not rely on the value. If a finalizer takes |
| 45 | // appreciable time, the work should be done elsewhere. Based on disassembly of Daemons.class, |
| 46 | // the value is effectively inlined, so changing the field never did have an effect. |
| 47 | // DO NOT USE. FOR ANYTHING. THIS WILL BE REMOVED SHORTLY. |
| 48 | @UnsupportedAppUsage |
| 49 | private static long MAX_FINALIZE_NANOS = 10L * 1000 * NANOS_PER_MILLI; |
| 50 | |
| 51 | private static final Daemon[] DAEMONS = new Daemon[] { |
| 52 | HeapTaskDaemon.INSTANCE, |
| 53 | ReferenceQueueDaemon.INSTANCE, |
| 54 | FinalizerDaemon.INSTANCE, |
| 55 | FinalizerWatchdogDaemon.INSTANCE, |
| 56 | }; |
| 57 | private static final CountDownLatch POST_ZYGOTE_START_LATCH = new CountDownLatch(DAEMONS.length); |
| 58 | private static final CountDownLatch PRE_ZYGOTE_START_LATCH = new CountDownLatch(DAEMONS.length); |
| 59 | |
| 60 | private static boolean postZygoteFork = false; |
| 61 | |
| 62 | @UnsupportedAppUsage |
| 63 | public static void start() { |
| 64 | for (Daemon daemon : DAEMONS) { |
| 65 | daemon.start(); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | public static void startPostZygoteFork() { |
| 70 | postZygoteFork = true; |
| 71 | for (Daemon daemon : DAEMONS) { |
| 72 | daemon.startPostZygoteFork(); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | @UnsupportedAppUsage |
| 77 | public static void stop() { |
| 78 | for (Daemon daemon : DAEMONS) { |
| 79 | daemon.stop(); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | private static void waitForDaemonStart() throws Exception { |
| 84 | if (postZygoteFork) { |
| 85 | POST_ZYGOTE_START_LATCH.await(); |
| 86 | } else { |
| 87 | PRE_ZYGOTE_START_LATCH.await(); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * A background task that provides runtime support to the application. |
| 93 | * Daemons can be stopped and started, but only so that the zygote can be a |
| 94 | * single-threaded process when it forks. |
| 95 | */ |
| 96 | private static abstract class Daemon implements Runnable { |
| 97 | @UnsupportedAppUsage |
| 98 | private Thread thread; |
| 99 | private String name; |
| 100 | private boolean postZygoteFork; |
| 101 | |
| 102 | protected Daemon(String name) { |
| 103 | this.name = name; |
| 104 | } |
| 105 | |
| 106 | @UnsupportedAppUsage |
| 107 | public synchronized void start() { |
| 108 | startInternal(); |
| 109 | } |
| 110 | |
| 111 | public synchronized void startPostZygoteFork() { |
| 112 | postZygoteFork = true; |
| 113 | startInternal(); |
| 114 | } |
| 115 | |
| 116 | public void startInternal() { |
| 117 | if (thread != null) { |
| 118 | throw new IllegalStateException("already running"); |
| 119 | } |
| 120 | thread = new Thread(ThreadGroup.systemThreadGroup, this, name); |
| 121 | thread.setDaemon(true); |
| 122 | thread.setSystemDaemon(true); |
| 123 | thread.start(); |
| 124 | } |
| 125 | |
| 126 | public final void run() { |
| 127 | if (postZygoteFork) { |
| 128 | // We don't set the priority before the Thread.start() call above because |
| 129 | // Thread.start() will call SetNativePriority and overwrite the desired native |
| 130 | // priority. We (may) use a native priority that doesn't have a corresponding |
| 131 | // java.lang.Thread-level priority (native priorities are more coarse-grained.) |
| 132 | VMRuntime.getRuntime().setSystemDaemonThreadPriority(); |
| 133 | POST_ZYGOTE_START_LATCH.countDown(); |
| 134 | } else { |
| 135 | PRE_ZYGOTE_START_LATCH.countDown(); |
| 136 | } |
| 137 | runInternal(); |
| 138 | } |
| 139 | |
| 140 | public abstract void runInternal(); |
| 141 | |
| 142 | /** |
| 143 | * Returns true while the current thread should continue to run; false |
| 144 | * when it should return. |
| 145 | */ |
| 146 | @UnsupportedAppUsage |
| 147 | protected synchronized boolean isRunning() { |
| 148 | return thread != null; |
| 149 | } |
| 150 | |
| 151 | public synchronized void interrupt() { |
| 152 | interrupt(thread); |
| 153 | } |
| 154 | |
| 155 | public synchronized void interrupt(Thread thread) { |
| 156 | if (thread == null) { |
| 157 | throw new IllegalStateException("not running"); |
| 158 | } |
| 159 | thread.interrupt(); |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * Waits for the runtime thread to stop. This interrupts the thread |
| 164 | * currently running the runnable and then waits for it to exit. |
| 165 | */ |
| 166 | @UnsupportedAppUsage |
| 167 | public void stop() { |
| 168 | Thread threadToStop; |
| 169 | synchronized (this) { |
| 170 | threadToStop = thread; |
| 171 | thread = null; |
| 172 | } |
| 173 | if (threadToStop == null) { |
| 174 | throw new IllegalStateException("not running"); |
| 175 | } |
| 176 | interrupt(threadToStop); |
| 177 | while (true) { |
| 178 | try { |
| 179 | threadToStop.join(); |
| 180 | return; |
| 181 | } catch (InterruptedException ignored) { |
| 182 | } catch (OutOfMemoryError ignored) { |
| 183 | // An OOME may be thrown if allocating the InterruptedException failed. |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Returns the current stack trace of the thread, or an empty stack trace |
| 190 | * if the thread is not currently running. |
| 191 | */ |
| 192 | public synchronized StackTraceElement[] getStackTrace() { |
| 193 | return thread != null ? thread.getStackTrace() : EmptyArray.STACK_TRACE_ELEMENT; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * This heap management thread moves elements from the garbage collector's |
| 199 | * pending list to the managed reference queue. |
| 200 | */ |
| 201 | private static class ReferenceQueueDaemon extends Daemon { |
| 202 | @UnsupportedAppUsage |
| 203 | private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon(); |
| 204 | |
| 205 | ReferenceQueueDaemon() { |
| 206 | super("ReferenceQueueDaemon"); |
| 207 | } |
| 208 | |
| 209 | @Override public void runInternal() { |
| 210 | while (isRunning()) { |
| 211 | Reference<?> list; |
| 212 | try { |
| 213 | synchronized (ReferenceQueue.class) { |
| 214 | while (ReferenceQueue.unenqueued == null) { |
| 215 | ReferenceQueue.class.wait(); |
| 216 | } |
| 217 | list = ReferenceQueue.unenqueued; |
| 218 | ReferenceQueue.unenqueued = null; |
| 219 | } |
| 220 | } catch (InterruptedException e) { |
| 221 | continue; |
| 222 | } catch (OutOfMemoryError e) { |
| 223 | continue; |
| 224 | } |
| 225 | ReferenceQueue.enqueuePending(list); |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | private static class FinalizerDaemon extends Daemon { |
| 231 | @UnsupportedAppUsage |
| 232 | private static final FinalizerDaemon INSTANCE = new FinalizerDaemon(); |
| 233 | private final ReferenceQueue<Object> queue = FinalizerReference.queue; |
| 234 | private final AtomicInteger progressCounter = new AtomicInteger(0); |
| 235 | // Object (not reference!) being finalized. Accesses may race! |
| 236 | @UnsupportedAppUsage |
| 237 | private Object finalizingObject = null; |
| 238 | |
| 239 | FinalizerDaemon() { |
| 240 | super("FinalizerDaemon"); |
| 241 | } |
| 242 | |
| 243 | @Override public void runInternal() { |
| 244 | // This loop may be performance critical, since we need to keep up with mutator |
| 245 | // generation of finalizable objects. |
| 246 | // We minimize the amount of work we do per finalizable object. For example, we avoid |
| 247 | // reading the current time here, since that involves a kernel call per object. We |
| 248 | // limit fast path communication with FinalizerWatchDogDaemon to what's unavoidable: A |
| 249 | // non-volatile store to communicate the current finalizable object, e.g. for |
| 250 | // reporting, and a release store (lazySet) to a counter. |
| 251 | // We do stop the FinalizerWatchDogDaemon if we have nothing to do for a |
| 252 | // potentially extended period. This prevents the device from waking up regularly |
| 253 | // during idle times. |
| 254 | |
| 255 | // Local copy of progressCounter; saves a fence per increment on ARM and MIPS. |
| 256 | int localProgressCounter = progressCounter.get(); |
| 257 | |
| 258 | while (isRunning()) { |
| 259 | try { |
| 260 | // Use non-blocking poll to avoid FinalizerWatchdogDaemon communication |
| 261 | // when busy. |
| 262 | FinalizerReference<?> finalizingReference = (FinalizerReference<?>)queue.poll(); |
| 263 | if (finalizingReference != null) { |
| 264 | finalizingObject = finalizingReference.get(); |
| 265 | progressCounter.lazySet(++localProgressCounter); |
| 266 | } else { |
| 267 | finalizingObject = null; |
| 268 | progressCounter.lazySet(++localProgressCounter); |
| 269 | // Slow path; block. |
| 270 | FinalizerWatchdogDaemon.INSTANCE.goToSleep(); |
| 271 | finalizingReference = (FinalizerReference<?>)queue.remove(); |
| 272 | finalizingObject = finalizingReference.get(); |
| 273 | progressCounter.set(++localProgressCounter); |
| 274 | FinalizerWatchdogDaemon.INSTANCE.wakeUp(); |
| 275 | } |
| 276 | doFinalize(finalizingReference); |
| 277 | } catch (InterruptedException ignored) { |
| 278 | } catch (OutOfMemoryError ignored) { |
| 279 | } |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION") |
| 284 | private void doFinalize(FinalizerReference<?> reference) { |
| 285 | FinalizerReference.remove(reference); |
| 286 | Object object = reference.get(); |
| 287 | reference.clear(); |
| 288 | try { |
| 289 | object.finalize(); |
| 290 | } catch (Throwable ex) { |
| 291 | // The RI silently swallows these, but Android has always logged. |
| 292 | System.logE("Uncaught exception thrown by finalizer", ex); |
| 293 | } finally { |
| 294 | // Done finalizing, stop holding the object as live. |
| 295 | finalizingObject = null; |
| 296 | } |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | /** |
| 301 | * The watchdog exits the VM if the finalizer ever gets stuck. We consider |
| 302 | * the finalizer to be stuck if it spends more than MAX_FINALIZATION_MILLIS |
| 303 | * on one instance. |
| 304 | */ |
| 305 | private static class FinalizerWatchdogDaemon extends Daemon { |
| 306 | @UnsupportedAppUsage |
| 307 | private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon(); |
| 308 | |
| 309 | private boolean needToWork = true; // Only accessed in synchronized methods. |
| 310 | |
| 311 | private long finalizerTimeoutMs = 0; // Lazily initialized. |
| 312 | |
| 313 | FinalizerWatchdogDaemon() { |
| 314 | super("FinalizerWatchdogDaemon"); |
| 315 | } |
| 316 | |
| 317 | @Override public void runInternal() { |
| 318 | while (isRunning()) { |
| 319 | if (!sleepUntilNeeded()) { |
| 320 | // We have been interrupted, need to see if this daemon has been stopped. |
| 321 | continue; |
| 322 | } |
| 323 | final Object finalizing = waitForFinalization(); |
| 324 | if (finalizing != null && !VMRuntime.getRuntime().isDebuggerActive()) { |
| 325 | finalizerTimedOut(finalizing); |
| 326 | break; |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /** |
| 332 | * Wait until something is ready to be finalized. |
| 333 | * Return false if we have been interrupted |
| 334 | * See also http://code.google.com/p/android/issues/detail?id=22778. |
| 335 | */ |
| 336 | private synchronized boolean sleepUntilNeeded() { |
| 337 | while (!needToWork) { |
| 338 | try { |
| 339 | wait(); |
| 340 | } catch (InterruptedException e) { |
| 341 | // Daemon.stop may have interrupted us. |
| 342 | return false; |
| 343 | } catch (OutOfMemoryError e) { |
| 344 | return false; |
| 345 | } |
| 346 | } |
| 347 | return true; |
| 348 | } |
| 349 | |
| 350 | /** |
| 351 | * Notify daemon that it's OK to sleep until notified that something is ready to be |
| 352 | * finalized. |
| 353 | */ |
| 354 | private synchronized void goToSleep() { |
| 355 | needToWork = false; |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * Notify daemon that there is something ready to be finalized. |
| 360 | */ |
| 361 | private synchronized void wakeUp() { |
| 362 | needToWork = true; |
| 363 | notify(); |
| 364 | } |
| 365 | |
| 366 | private synchronized boolean getNeedToWork() { |
| 367 | return needToWork; |
| 368 | } |
| 369 | |
| 370 | /** |
| 371 | * Sleep for the given number of milliseconds. |
| 372 | * @return false if we were interrupted. |
| 373 | */ |
| 374 | private boolean sleepForMillis(long durationMillis) { |
| 375 | long startMillis = System.currentTimeMillis(); |
| 376 | while (true) { |
| 377 | long elapsedMillis = System.currentTimeMillis() - startMillis; |
| 378 | long sleepMillis = durationMillis - elapsedMillis; |
| 379 | if (sleepMillis <= 0) { |
| 380 | return true; |
| 381 | } |
| 382 | try { |
| 383 | Thread.sleep(sleepMillis); |
| 384 | } catch (InterruptedException e) { |
| 385 | if (!isRunning()) { |
| 386 | return false; |
| 387 | } |
| 388 | } catch (OutOfMemoryError ignored) { |
| 389 | if (!isRunning()) { |
| 390 | return false; |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | |
| 397 | /** |
| 398 | * Return an object that took too long to finalize or return null. |
| 399 | * Wait VMRuntime.getFinalizerTimeoutMs. If the FinalizerDaemon took essentially the |
| 400 | * whole time processing a single reference, return that reference. Otherwise return |
| 401 | * null. Only called from a single thread. |
| 402 | */ |
| 403 | private Object waitForFinalization() { |
| 404 | if (finalizerTimeoutMs == 0) { |
| 405 | finalizerTimeoutMs = VMRuntime.getRuntime().getFinalizerTimeoutMs(); |
| 406 | // Temporary app backward compatibility. Remove eventually. |
| 407 | MAX_FINALIZE_NANOS = NANOS_PER_MILLI * finalizerTimeoutMs; |
| 408 | } |
| 409 | long startCount = FinalizerDaemon.INSTANCE.progressCounter.get(); |
| 410 | // Avoid remembering object being finalized, so as not to keep it alive. |
| 411 | if (!sleepForMillis(finalizerTimeoutMs)) { |
| 412 | // Don't report possibly spurious timeout if we are interrupted. |
| 413 | return null; |
| 414 | } |
| 415 | if (getNeedToWork() && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) { |
| 416 | // We assume that only remove() and doFinalize() may take time comparable to |
| 417 | // the finalizer timeout. |
| 418 | // We observed neither the effect of the gotoSleep() nor the increment preceding a |
| 419 | // later wakeUp. Any remove() call by the FinalizerDaemon during our sleep |
| 420 | // interval must have been followed by a wakeUp call before we checked needToWork. |
| 421 | // But then we would have seen the counter increment. Thus there cannot have |
| 422 | // been such a remove() call. |
| 423 | // The FinalizerDaemon must not have progressed (from either the beginning or the |
| 424 | // last progressCounter increment) to either the next increment or gotoSleep() |
| 425 | // call. Thus we must have taken essentially the whole finalizerTimeoutMs in a |
| 426 | // single doFinalize() call. Thus it's OK to time out. finalizingObject was set |
| 427 | // just before the counter increment, which preceded the doFinalize call. Thus we |
| 428 | // are guaranteed to get the correct finalizing value below, unless doFinalize() |
| 429 | // just finished as we were timing out, in which case we may get null or a later |
| 430 | // one. In this last case, we are very likely to discard it below. |
| 431 | Object finalizing = FinalizerDaemon.INSTANCE.finalizingObject; |
| 432 | sleepForMillis(500); |
| 433 | // Recheck to make it even less likely we report the wrong finalizing object in |
| 434 | // the case which a very slow finalization just finished as we were timing out. |
| 435 | if (getNeedToWork() |
| 436 | && FinalizerDaemon.INSTANCE.progressCounter.get() == startCount) { |
| 437 | return finalizing; |
| 438 | } |
| 439 | } |
| 440 | return null; |
| 441 | } |
| 442 | |
| 443 | private static void finalizerTimedOut(Object object) { |
| 444 | // The current object has exceeded the finalization deadline; abort! |
| 445 | String message = object.getClass().getName() + ".finalize() timed out after " |
| 446 | + VMRuntime.getRuntime().getFinalizerTimeoutMs() / 1000 + " seconds"; |
| 447 | Exception syntheticException = new TimeoutException(message); |
| 448 | // We use the stack from where finalize() was running to show where it was stuck. |
| 449 | syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); |
| 450 | |
| 451 | // Send SIGQUIT to get native stack traces. |
| 452 | try { |
| 453 | Os.kill(Os.getpid(), OsConstants.SIGQUIT); |
| 454 | // Sleep a few seconds to let the stack traces print. |
| 455 | Thread.sleep(5000); |
| 456 | } catch (Exception e) { |
| 457 | System.logE("failed to send SIGQUIT", e); |
| 458 | } catch (OutOfMemoryError ignored) { |
| 459 | // May occur while trying to allocate the exception. |
| 460 | } |
| 461 | |
| 462 | // Ideally, we'd want to do this if this Thread had no handler to dispatch to. |
| 463 | // Unfortunately, it's extremely to messy to query whether a given Thread has *some* |
| 464 | // handler to dispatch to, either via a handler set on itself, via its ThreadGroup |
| 465 | // object or via the defaultUncaughtExceptionHandler. |
| 466 | // |
| 467 | // As an approximation, we log by hand an exit if there's no pre-exception handler nor |
| 468 | // a default uncaught exception handler. |
| 469 | // |
| 470 | // Note that this condition will only ever be hit by ART host tests and standalone |
| 471 | // dalvikvm invocations. All zygote forked process *will* have a pre-handler set |
| 472 | // in RuntimeInit and they cannot subsequently override it. |
| 473 | if (Thread.getUncaughtExceptionPreHandler() == null && |
| 474 | Thread.getDefaultUncaughtExceptionHandler() == null) { |
| 475 | // If we have no handler, log and exit. |
| 476 | System.logE(message, syntheticException); |
| 477 | System.exit(2); |
| 478 | } |
| 479 | |
| 480 | // Otherwise call the handler to do crash reporting. |
| 481 | // We don't just throw because we're not the thread that |
| 482 | // timed out; we're the thread that detected it. |
| 483 | Thread.currentThread().dispatchUncaughtException(syntheticException); |
| 484 | } |
| 485 | } |
| 486 | |
| 487 | // Adds a heap trim task to the heap event processor, not called from java. Left for |
| 488 | // compatibility purposes due to reflection. |
| 489 | @UnsupportedAppUsage |
| 490 | public static void requestHeapTrim() { |
| 491 | VMRuntime.getRuntime().requestHeapTrim(); |
| 492 | } |
| 493 | |
| 494 | // Adds a concurrent GC request task ot the heap event processor, not called from java. Left |
| 495 | // for compatibility purposes due to reflection. |
| 496 | public static void requestGC() { |
| 497 | VMRuntime.getRuntime().requestConcurrentGC(); |
| 498 | } |
| 499 | |
| 500 | private static class HeapTaskDaemon extends Daemon { |
| 501 | private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon(); |
| 502 | |
| 503 | HeapTaskDaemon() { |
| 504 | super("HeapTaskDaemon"); |
| 505 | } |
| 506 | |
| 507 | // Overrides the Daemon.interupt method which is called from Daemons.stop. |
| 508 | public synchronized void interrupt(Thread thread) { |
| 509 | VMRuntime.getRuntime().stopHeapTaskProcessor(); |
| 510 | } |
| 511 | |
| 512 | @Override public void runInternal() { |
| 513 | synchronized (this) { |
| 514 | if (isRunning()) { |
| 515 | // Needs to be synchronized or else we there is a race condition where we start |
| 516 | // the thread, call stopHeapTaskProcessor before we start the heap task |
| 517 | // processor, resulting in a deadlock since startHeapTaskProcessor restarts it |
| 518 | // while the other thread is waiting in Daemons.stop(). |
| 519 | VMRuntime.getRuntime().startHeapTaskProcessor(); |
| 520 | } |
| 521 | } |
| 522 | // This runs tasks until we are stopped and there is no more pending task. |
| 523 | VMRuntime.getRuntime().runHeapTasks(); |
| 524 | } |
| 525 | } |
| 526 | } |