1 /* 2 * Copyright (C) 2008 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 /* 18 * An async worker thread to handle certain heap operations that need 19 * to be done in a separate thread to avoid synchronization problems. 20 * HeapWorkers and reference enqueuing are handled by this thread. 21 * The VM does all clearing. 22 */ 23 #include "Dalvik.h" 24 #include "HeapInternal.h" 25 26 #include <sys/time.h> 27 #include <stdlib.h> 28 #include <pthread.h> 29 #include <signal.h> 30 #include <errno.h> // for ETIMEDOUT, etc. 31 32 static void* heapWorkerThreadStart(void* arg); 33 34 /* 35 * Initialize any HeapWorker state that Heap.c 36 * cares about. This lets the GC start before the 37 * HeapWorker thread is initialized. 38 */ 39 void dvmInitializeHeapWorkerState() 40 { 41 assert(!gDvm.heapWorkerInitialized); 42 43 dvmInitMutex(&gDvm.heapWorkerLock); 44 pthread_cond_init(&gDvm.heapWorkerCond, NULL); 45 pthread_cond_init(&gDvm.heapWorkerIdleCond, NULL); 46 47 gDvm.heapWorkerInitialized = true; 48 } 49 50 /* 51 * Crank up the heap worker thread. 52 * 53 * Does not return until the thread is ready for business. 54 */ 55 bool dvmHeapWorkerStartup(void) 56 { 57 assert(!gDvm.haltHeapWorker); 58 assert(!gDvm.heapWorkerReady); 59 assert(gDvm.heapWorkerHandle == 0); 60 assert(gDvm.heapWorkerInitialized); 61 62 /* use heapWorkerLock/heapWorkerCond to communicate readiness */ 63 dvmLockMutex(&gDvm.heapWorkerLock); 64 65 //BUG: If a GC happens in here or in the new thread while we hold the lock, 66 // the GC will deadlock when trying to acquire heapWorkerLock. 67 if (!dvmCreateInternalThread(&gDvm.heapWorkerHandle, 68 "HeapWorker", heapWorkerThreadStart, NULL)) 69 { 70 dvmUnlockMutex(&gDvm.heapWorkerLock); 71 return false; 72 } 73 74 /* 75 * Wait for the heap worker to come up. We know the thread was created, 76 * so this should not get stuck. 77 */ 78 while (!gDvm.heapWorkerReady) { 79 int cc = pthread_cond_wait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock); 80 assert(cc == 0); 81 } 82 83 dvmUnlockMutex(&gDvm.heapWorkerLock); 84 return true; 85 } 86 87 /* 88 * Shut down the heap worker thread if it was started. 89 */ 90 void dvmHeapWorkerShutdown(void) 91 { 92 void* threadReturn; 93 94 /* note: assuming that (pthread_t)0 is not a valid thread handle */ 95 if (gDvm.heapWorkerHandle != 0) { 96 gDvm.haltHeapWorker = true; 97 dvmSignalHeapWorker(true); 98 99 /* 100 * We may not want to wait for the heapWorkers to complete. It's 101 * a good idea to do so, in case they're holding some sort of OS 102 * resource that doesn't get reclaimed when the process exits 103 * (e.g. an open temp file). 104 */ 105 if (pthread_join(gDvm.heapWorkerHandle, &threadReturn) != 0) 106 LOGW("HeapWorker thread join failed\n"); 107 else if (gDvm.verboseShutdown) 108 LOGD("HeapWorker thread has shut down\n"); 109 110 gDvm.heapWorkerReady = false; 111 } 112 } 113 114 /* Make sure that the HeapWorker thread hasn't spent an inordinate 115 * amount of time inside a finalizer. 116 * 117 * Aborts the VM if the thread appears to be wedged. 118 * 119 * The caller must hold the heapWorkerLock to guarantee an atomic 120 * read of the watchdog values. 121 */ 122 void dvmAssertHeapWorkerThreadRunning() 123 { 124 if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) { 125 static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec 126 127 u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime; 128 u8 now = dvmGetRelativeTimeUsec(); 129 u8 delta = now - heapWorkerInterpStartTime; 130 131 u8 heapWorkerInterpCpuStartTime = 132 gDvm.gcHeap->heapWorkerInterpCpuStartTime; 133 u8 nowCpu = dvmGetOtherThreadCpuTimeUsec(gDvm.heapWorkerHandle); 134 u8 deltaCpu = nowCpu - heapWorkerInterpCpuStartTime; 135 136 if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT && 137 (gDvm.debuggerActive || gDvm.nativeDebuggerActive)) 138 { 139 /* 140 * Debugger suspension can block the thread indefinitely. For 141 * best results we should reset this explicitly whenever the 142 * HeapWorker thread is resumed. Unfortunately this is also 143 * affected by native debuggers, and we have no visibility 144 * into how they're manipulating us. So, we ignore the 145 * watchdog and just reset the timer. 146 */ 147 LOGI("Debugger is attached -- suppressing HeapWorker watchdog\n"); 148 gDvm.gcHeap->heapWorkerInterpStartTime = now; /* reset timer */ 149 } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) { 150 /* 151 * Before we give up entirely, see if maybe we're just not 152 * getting any CPU time because we're stuck in a background 153 * process group. If we successfully move the thread into the 154 * foreground we'll just leave it there (it doesn't do anything 155 * if the process isn't GCing). 156 */ 157 dvmLockThreadList(NULL); 158 Thread* thread = dvmGetThreadByHandle(gDvm.heapWorkerHandle); 159 dvmUnlockThreadList(); 160 161 if (thread != NULL) { 162 int priChangeFlags, threadPrio; 163 SchedPolicy threadPolicy; 164 priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread, 165 &threadPrio, &threadPolicy); 166 if (priChangeFlags != 0) { 167 LOGI("HeapWorker watchdog expired, raising priority" 168 " and retrying\n"); 169 gDvm.gcHeap->heapWorkerInterpStartTime = now; 170 return; 171 } 172 } 173 174 char* desc = dexProtoCopyMethodDescriptor( 175 &gDvm.gcHeap->heapWorkerCurrentMethod->prototype); 176 LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s\n", 177 delta / 1000, 178 gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor, 179 gDvm.gcHeap->heapWorkerCurrentMethod->name, desc); 180 free(desc); 181 dvmDumpAllThreads(true); 182 183 /* try to get a debuggerd dump from the target thread */ 184 dvmNukeThread(thread); 185 186 /* abort the VM */ 187 dvmAbort(); 188 } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) { 189 char* desc = dexProtoCopyMethodDescriptor( 190 &gDvm.gcHeap->heapWorkerCurrentMethod->prototype); 191 LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s\n", 192 delta / 1000, 193 gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor, 194 gDvm.gcHeap->heapWorkerCurrentMethod->name, desc); 195 free(desc); 196 } 197 } 198 } 199 200 static void callMethod(Thread *self, Object *obj, Method *method) 201 { 202 JValue unused; 203 204 /* Keep track of the method we're about to call and 205 * the current time so that other threads can detect 206 * when this thread wedges and provide useful information. 207 */ 208 gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec(); 209 gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec(); 210 gDvm.gcHeap->heapWorkerCurrentMethod = method; 211 gDvm.gcHeap->heapWorkerCurrentObject = obj; 212 213 /* Call the method. 214 * 215 * Don't hold the lock when executing interpreted 216 * code. It may suspend, and the GC needs to grab 217 * heapWorkerLock. 218 */ 219 dvmUnlockMutex(&gDvm.heapWorkerLock); 220 if (false) { 221 /* Log entry/exit; this will likely flood the log enough to 222 * cause "logcat" to drop entries. 223 */ 224 char tmpTag[16]; 225 sprintf(tmpTag, "HW%d", self->systemTid); 226 LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor); 227 dvmCallMethod(self, method, obj, &unused); 228 LOG(LOG_DEBUG, tmpTag, " done\n"); 229 } else { 230 dvmCallMethod(self, method, obj, &unused); 231 } 232 dvmLockMutex(&gDvm.heapWorkerLock); 233 234 gDvm.gcHeap->heapWorkerCurrentObject = NULL; 235 gDvm.gcHeap->heapWorkerCurrentMethod = NULL; 236 gDvm.gcHeap->heapWorkerInterpStartTime = 0LL; 237 238 /* Exceptions thrown during these calls interrupt 239 * the method, but are otherwise ignored. 240 */ 241 if (dvmCheckException(self)) { 242 #if DVM_SHOW_EXCEPTION >= 1 243 LOGI("Uncaught exception thrown by finalizer (will be discarded):\n"); 244 dvmLogExceptionStackTrace(); 245 #endif 246 dvmClearException(self); 247 } 248 } 249 250 /* Process all enqueued heap work, including finalizers and reference 251 * enqueueing. Clearing has already been done by the VM. 252 * 253 * Caller must hold gDvm.heapWorkerLock. 254 */ 255 static void doHeapWork(Thread *self) 256 { 257 Object *obj; 258 HeapWorkerOperation op; 259 int numFinalizersCalled, numReferencesEnqueued; 260 261 assert(gDvm.voffJavaLangObject_finalize >= 0); 262 assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL); 263 264 numFinalizersCalled = 0; 265 numReferencesEnqueued = 0; 266 while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) { 267 Method *method = NULL; 268 269 /* Make sure the object hasn't been collected since 270 * being scheduled. 271 */ 272 assert(dvmIsValidObject(obj)); 273 274 /* Call the appropriate method(s). 275 */ 276 if (op == WORKER_FINALIZE) { 277 numFinalizersCalled++; 278 method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize]; 279 assert(dvmCompareNameDescriptorAndMethod("finalize", "()V", 280 method) == 0); 281 assert(method->clazz != gDvm.classJavaLangObject); 282 callMethod(self, obj, method); 283 } else { 284 if (op & WORKER_ENQUEUE) { 285 numReferencesEnqueued++; 286 callMethod(self, obj, 287 gDvm.methJavaLangRefReference_enqueueInternal); 288 } 289 } 290 291 /* Let the GC collect the object. 292 */ 293 dvmReleaseTrackedAlloc(obj, self); 294 } 295 LOGV("Called %d finalizers\n", numFinalizersCalled); 296 LOGV("Enqueued %d references\n", numReferencesEnqueued); 297 } 298 299 /* 300 * The heap worker thread sits quietly until the GC tells it there's work 301 * to do. 302 */ 303 static void* heapWorkerThreadStart(void* arg) 304 { 305 Thread *self = dvmThreadSelf(); 306 int cc; 307 308 UNUSED_PARAMETER(arg); 309 310 LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId); 311 312 /* tell the main thread that we're ready */ 313 dvmLockMutex(&gDvm.heapWorkerLock); 314 gDvm.heapWorkerReady = true; 315 cc = pthread_cond_signal(&gDvm.heapWorkerCond); 316 assert(cc == 0); 317 dvmUnlockMutex(&gDvm.heapWorkerLock); 318 319 dvmLockMutex(&gDvm.heapWorkerLock); 320 while (!gDvm.haltHeapWorker) { 321 struct timespec trimtime; 322 bool timedwait = false; 323 324 /* We're done running interpreted code for now. */ 325 dvmChangeStatus(NULL, THREAD_VMWAIT); 326 327 /* Signal anyone who wants to know when we're done. */ 328 cc = pthread_cond_broadcast(&gDvm.heapWorkerIdleCond); 329 assert(cc == 0); 330 331 /* Trim the heap if we were asked to. */ 332 trimtime = gDvm.gcHeap->heapWorkerNextTrim; 333 if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) { 334 struct timespec now; 335 336 #ifdef HAVE_TIMEDWAIT_MONOTONIC 337 clock_gettime(CLOCK_MONOTONIC, &now); // relative time 338 #else 339 struct timeval tvnow; 340 gettimeofday(&tvnow, NULL); // absolute time 341 now.tv_sec = tvnow.tv_sec; 342 now.tv_nsec = tvnow.tv_usec * 1000; 343 #endif 344 345 if (trimtime.tv_sec < now.tv_sec || 346 (trimtime.tv_sec == now.tv_sec && 347 trimtime.tv_nsec <= now.tv_nsec)) 348 { 349 size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT]; 350 351 /* The heap must be locked before the HeapWorker; 352 * unroll and re-order the locks. dvmLockHeap() 353 * will put us in VMWAIT if necessary. Once it 354 * returns, there shouldn't be any contention on 355 * heapWorkerLock. 356 */ 357 dvmUnlockMutex(&gDvm.heapWorkerLock); 358 dvmLockHeap(); 359 dvmLockMutex(&gDvm.heapWorkerLock); 360 361 memset(madvisedSizes, 0, sizeof(madvisedSizes)); 362 dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT); 363 dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT); 364 365 dvmUnlockHeap(); 366 367 trimtime.tv_sec = 0; 368 trimtime.tv_nsec = 0; 369 gDvm.gcHeap->heapWorkerNextTrim = trimtime; 370 } else { 371 timedwait = true; 372 } 373 } 374 375 /* sleep until signaled */ 376 if (timedwait) { 377 #ifdef HAVE_TIMEDWAIT_MONOTONIC 378 cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond, 379 &gDvm.heapWorkerLock, &trimtime); 380 #else 381 cc = pthread_cond_timedwait(&gDvm.heapWorkerCond, 382 &gDvm.heapWorkerLock, &trimtime); 383 #endif 384 assert(cc == 0 || cc == ETIMEDOUT || cc == EINTR); 385 } else { 386 cc = pthread_cond_wait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock); 387 assert(cc == 0); 388 } 389 390 /* dvmChangeStatus() may block; don't hold heapWorkerLock. 391 */ 392 dvmUnlockMutex(&gDvm.heapWorkerLock); 393 dvmChangeStatus(NULL, THREAD_RUNNING); 394 dvmLockMutex(&gDvm.heapWorkerLock); 395 LOGV("HeapWorker is awake\n"); 396 397 /* Process any events in the queue. 398 */ 399 doHeapWork(self); 400 } 401 dvmUnlockMutex(&gDvm.heapWorkerLock); 402 403 if (gDvm.verboseShutdown) 404 LOGD("HeapWorker thread shutting down\n"); 405 return NULL; 406 } 407 408 /* 409 * Wake up the heap worker to let it know that there's work to be done. 410 */ 411 void dvmSignalHeapWorker(bool shouldLock) 412 { 413 int cc; 414 415 if (shouldLock) { 416 dvmLockMutex(&gDvm.heapWorkerLock); 417 } 418 419 cc = pthread_cond_signal(&gDvm.heapWorkerCond); 420 assert(cc == 0); 421 422 if (shouldLock) { 423 dvmUnlockMutex(&gDvm.heapWorkerLock); 424 } 425 } 426 427 /* 428 * Block until all pending heap worker work has finished. 429 */ 430 void dvmWaitForHeapWorkerIdle() 431 { 432 int cc; 433 434 assert(gDvm.heapWorkerReady); 435 436 dvmChangeStatus(NULL, THREAD_VMWAIT); 437 438 dvmLockMutex(&gDvm.heapWorkerLock); 439 440 /* Wake up the heap worker and wait for it to finish. */ 441 //TODO(http://b/issue?id=699704): This will deadlock if 442 // called from finalize(), enqueue(), or clear(). We 443 // need to detect when this is called from the HeapWorker 444 // context and just give up. 445 dvmSignalHeapWorker(false); 446 cc = pthread_cond_wait(&gDvm.heapWorkerIdleCond, &gDvm.heapWorkerLock); 447 assert(cc == 0); 448 449 dvmUnlockMutex(&gDvm.heapWorkerLock); 450 451 dvmChangeStatus(NULL, THREAD_RUNNING); 452 } 453 454 /* 455 * Do not return until any pending heap work has finished. This may 456 * or may not happen in the context of the calling thread. 457 * No exceptions will escape. 458 */ 459 void dvmRunFinalizationSync() 460 { 461 if (gDvm.zygote) { 462 assert(!gDvm.heapWorkerReady); 463 464 /* When in zygote mode, there is no heap worker. 465 * Do the work in the current thread. 466 */ 467 dvmLockMutex(&gDvm.heapWorkerLock); 468 doHeapWork(dvmThreadSelf()); 469 dvmUnlockMutex(&gDvm.heapWorkerLock); 470 } else { 471 /* Outside of zygote mode, we can just ask the 472 * heap worker thread to do the work. 473 */ 474 dvmWaitForHeapWorkerIdle(); 475 } 476 } 477 478 /* 479 * Requests that dvmHeapSourceTrim() be called no sooner 480 * than timeoutSec seconds from now. If timeoutSec 481 * is zero, any pending trim is cancelled. 482 * 483 * Caller must hold heapWorkerLock. 484 */ 485 void dvmScheduleHeapSourceTrim(size_t timeoutSec) 486 { 487 GcHeap *gcHeap = gDvm.gcHeap; 488 struct timespec timeout; 489 490 if (timeoutSec == 0) { 491 timeout.tv_sec = 0; 492 timeout.tv_nsec = 0; 493 /* Don't wake up the thread just to tell it to cancel. 494 * If it wakes up naturally, we can avoid the extra 495 * context switch. 496 */ 497 } else { 498 struct timeval now; 499 500 #ifdef HAVE_TIMEDWAIT_MONOTONIC 501 clock_gettime(CLOCK_MONOTONIC, &timeout); 502 timeout.tv_sec += timeoutSec; 503 #else 504 gettimeofday(&now, NULL); 505 timeout.tv_sec = now.tv_sec + timeoutSec; 506 timeout.tv_nsec = now.tv_usec * 1000; 507 #endif 508 dvmSignalHeapWorker(false); 509 } 510 gcHeap->heapWorkerNextTrim = timeout; 511 } 512