The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 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 | */ |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 16 | |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 17 | /* |
| 18 | * An async worker thread to handle certain heap operations that |
| 19 | * need to be done in a separate thread to avoid synchronization |
| 20 | * problems. HeapWorkers and reference clearing/enqueuing are |
| 21 | * handled by this thread. |
| 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 |
| 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 |
The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 115 | * amount of time inside a finalizer. |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 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 | |
The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 136 | if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT && |
| 137 | (gDvm.debuggerActive || gDvm.nativeDebuggerActive)) |
| 138 | { |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 139 | /* |
| 140 | * Debugger suspension can block the thread indefinitely. For |
| 141 | * best results we should reset this explicitly whenever the |
The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 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. |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 146 | */ |
| 147 | LOGI("Debugger is attached -- suppressing HeapWorker watchdog\n"); |
| 148 | heapWorkerInterpStartTime = now; /* reset timer */ |
| 149 | } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) { |
| 150 | char* desc = dexProtoCopyMethodDescriptor( |
| 151 | &gDvm.gcHeap->heapWorkerCurrentMethod->prototype); |
| 152 | LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s\n", |
| 153 | delta / 1000, |
| 154 | gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor, |
| 155 | gDvm.gcHeap->heapWorkerCurrentMethod->name, desc); |
| 156 | free(desc); |
| 157 | dvmDumpAllThreads(true); |
| 158 | |
| 159 | /* abort the VM */ |
| 160 | dvmAbort(); |
| 161 | } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) { |
| 162 | char* desc = dexProtoCopyMethodDescriptor( |
| 163 | &gDvm.gcHeap->heapWorkerCurrentMethod->prototype); |
| 164 | LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s\n", |
| 165 | delta / 1000, |
| 166 | gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor, |
| 167 | gDvm.gcHeap->heapWorkerCurrentMethod->name, desc); |
| 168 | free(desc); |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | static void callMethod(Thread *self, Object *obj, Method *method) |
| 174 | { |
| 175 | JValue unused; |
| 176 | |
| 177 | /* Keep track of the method we're about to call and |
| 178 | * the current time so that other threads can detect |
| 179 | * when this thread wedges and provide useful information. |
| 180 | */ |
| 181 | gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec(); |
| 182 | gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec(); |
| 183 | gDvm.gcHeap->heapWorkerCurrentMethod = method; |
| 184 | gDvm.gcHeap->heapWorkerCurrentObject = obj; |
| 185 | |
| 186 | /* Call the method. |
| 187 | * |
| 188 | * Don't hold the lock when executing interpreted |
| 189 | * code. It may suspend, and the GC needs to grab |
| 190 | * heapWorkerLock. |
| 191 | */ |
| 192 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 193 | if (false) { |
| 194 | /* Log entry/exit; this will likely flood the log enough to |
| 195 | * cause "logcat" to drop entries. |
| 196 | */ |
| 197 | char tmpTag[16]; |
| 198 | sprintf(tmpTag, "HW%d", self->systemTid); |
| 199 | LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor); |
| 200 | dvmCallMethod(self, method, obj, &unused); |
| 201 | LOG(LOG_DEBUG, tmpTag, " done\n"); |
| 202 | } else { |
| 203 | dvmCallMethod(self, method, obj, &unused); |
| 204 | } |
| 205 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 206 | |
| 207 | gDvm.gcHeap->heapWorkerCurrentObject = NULL; |
| 208 | gDvm.gcHeap->heapWorkerCurrentMethod = NULL; |
| 209 | gDvm.gcHeap->heapWorkerInterpStartTime = 0LL; |
| 210 | |
| 211 | /* Exceptions thrown during these calls interrupt |
| 212 | * the method, but are otherwise ignored. |
| 213 | */ |
| 214 | if (dvmCheckException(self)) { |
| 215 | #if DVM_SHOW_EXCEPTION >= 1 |
| 216 | LOGI("Uncaught exception thrown by finalizer (will be discarded):\n"); |
| 217 | dvmLogExceptionStackTrace(); |
| 218 | #endif |
| 219 | dvmClearException(self); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | /* Process all enqueued heap work, including finalizers and reference |
| 224 | * clearing/enqueueing. |
| 225 | * |
| 226 | * Caller must hold gDvm.heapWorkerLock. |
| 227 | */ |
| 228 | static void doHeapWork(Thread *self) |
| 229 | { |
| 230 | Object *obj; |
| 231 | HeapWorkerOperation op; |
| 232 | int numFinalizersCalled, numReferencesEnqueued; |
| 233 | #if FANCY_REFERENCE_SUBCLASS |
| 234 | int numReferencesCleared = 0; |
| 235 | #endif |
| 236 | |
| 237 | assert(gDvm.voffJavaLangObject_finalize >= 0); |
| 238 | #if FANCY_REFERENCE_SUBCLASS |
| 239 | assert(gDvm.voffJavaLangRefReference_clear >= 0); |
| 240 | assert(gDvm.voffJavaLangRefReference_enqueue >= 0); |
| 241 | #else |
| 242 | assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL); |
| 243 | #endif |
| 244 | |
| 245 | numFinalizersCalled = 0; |
| 246 | numReferencesEnqueued = 0; |
| 247 | while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) { |
| 248 | Method *method = NULL; |
| 249 | |
| 250 | /* Make sure the object hasn't been collected since |
| 251 | * being scheduled. |
| 252 | */ |
| 253 | assert(dvmIsValidObject(obj)); |
| 254 | |
| 255 | /* Call the appropriate method(s). |
| 256 | */ |
| 257 | if (op == WORKER_FINALIZE) { |
| 258 | numFinalizersCalled++; |
| 259 | method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize]; |
| 260 | assert(dvmCompareNameDescriptorAndMethod("finalize", "()V", |
| 261 | method) == 0); |
| 262 | assert(method->clazz != gDvm.classJavaLangObject); |
| 263 | callMethod(self, obj, method); |
| 264 | } else { |
| 265 | #if FANCY_REFERENCE_SUBCLASS |
| 266 | /* clear() *must* happen before enqueue(), otherwise |
| 267 | * a non-clear reference could appear on a reference |
| 268 | * queue. |
| 269 | */ |
| 270 | if (op & WORKER_CLEAR) { |
| 271 | numReferencesCleared++; |
| 272 | method = obj->clazz->vtable[ |
| 273 | gDvm.voffJavaLangRefReference_clear]; |
| 274 | assert(dvmCompareNameDescriptorAndMethod("clear", "()V", |
| 275 | method) == 0); |
| 276 | assert(method->clazz != gDvm.classJavaLangRefReference); |
| 277 | callMethod(self, obj, method); |
| 278 | } |
| 279 | if (op & WORKER_ENQUEUE) { |
| 280 | numReferencesEnqueued++; |
| 281 | method = obj->clazz->vtable[ |
| 282 | gDvm.voffJavaLangRefReference_enqueue]; |
| 283 | assert(dvmCompareNameDescriptorAndMethod("enqueue", "()Z", |
| 284 | method) == 0); |
| 285 | /* We call enqueue() even when it isn't overridden, |
| 286 | * so don't assert(!classJavaLangRefReference) here. |
| 287 | */ |
| 288 | callMethod(self, obj, method); |
| 289 | } |
| 290 | #else |
| 291 | assert((op & WORKER_CLEAR) == 0); |
| 292 | if (op & WORKER_ENQUEUE) { |
| 293 | numReferencesEnqueued++; |
| 294 | callMethod(self, obj, |
| 295 | gDvm.methJavaLangRefReference_enqueueInternal); |
| 296 | } |
| 297 | #endif |
| 298 | } |
| 299 | |
| 300 | /* Let the GC collect the object. |
| 301 | */ |
| 302 | dvmReleaseTrackedAlloc(obj, self); |
| 303 | } |
| 304 | LOGV("Called %d finalizers\n", numFinalizersCalled); |
| 305 | LOGV("Enqueued %d references\n", numReferencesEnqueued); |
| 306 | #if FANCY_REFERENCE_SUBCLASS |
| 307 | LOGV("Cleared %d overridden references\n", numReferencesCleared); |
| 308 | #endif |
| 309 | } |
| 310 | |
| 311 | /* |
| 312 | * The heap worker thread sits quietly until the GC tells it there's work |
| 313 | * to do. |
| 314 | */ |
| 315 | static void* heapWorkerThreadStart(void* arg) |
| 316 | { |
| 317 | Thread *self = dvmThreadSelf(); |
| 318 | int cc; |
| 319 | |
| 320 | UNUSED_PARAMETER(arg); |
| 321 | |
| 322 | LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId); |
| 323 | |
| 324 | /* tell the main thread that we're ready */ |
| 325 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 326 | gDvm.heapWorkerReady = true; |
| 327 | cc = pthread_cond_signal(&gDvm.heapWorkerCond); |
| 328 | assert(cc == 0); |
| 329 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 330 | |
| 331 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 332 | while (!gDvm.haltHeapWorker) { |
| 333 | struct timespec trimtime; |
| 334 | bool timedwait = false; |
| 335 | |
| 336 | /* We're done running interpreted code for now. */ |
| 337 | dvmChangeStatus(NULL, THREAD_VMWAIT); |
| 338 | |
| 339 | /* Signal anyone who wants to know when we're done. */ |
| 340 | cc = pthread_cond_broadcast(&gDvm.heapWorkerIdleCond); |
| 341 | assert(cc == 0); |
| 342 | |
| 343 | /* Trim the heap if we were asked to. */ |
| 344 | trimtime = gDvm.gcHeap->heapWorkerNextTrim; |
| 345 | if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) { |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 346 | struct timespec now; |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 347 | |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 348 | #ifdef HAVE_TIMEDWAIT_MONOTONIC |
| 349 | clock_gettime(CLOCK_MONOTONIC, &now); // relative time |
| 350 | #else |
| 351 | struct timeval tvnow; |
| 352 | gettimeofday(&tvnow, NULL); // absolute time |
| 353 | now.tv_sec = tvnow.tv_sec; |
| 354 | now.tv_nsec = tvnow.tv_usec * 1000; |
| 355 | #endif |
| 356 | |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 357 | if (trimtime.tv_sec < now.tv_sec || |
| 358 | (trimtime.tv_sec == now.tv_sec && |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 359 | trimtime.tv_nsec <= now.tv_nsec)) |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 360 | { |
| 361 | size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT]; |
| 362 | |
| 363 | /* The heap must be locked before the HeapWorker; |
| 364 | * unroll and re-order the locks. dvmLockHeap() |
| 365 | * will put us in VMWAIT if necessary. Once it |
| 366 | * returns, there shouldn't be any contention on |
| 367 | * heapWorkerLock. |
| 368 | */ |
| 369 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 370 | dvmLockHeap(); |
| 371 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 372 | |
| 373 | memset(madvisedSizes, 0, sizeof(madvisedSizes)); |
| 374 | dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT); |
| 375 | dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT); |
| 376 | |
| 377 | dvmUnlockHeap(); |
| 378 | |
| 379 | trimtime.tv_sec = 0; |
| 380 | trimtime.tv_nsec = 0; |
| 381 | gDvm.gcHeap->heapWorkerNextTrim = trimtime; |
| 382 | } else { |
| 383 | timedwait = true; |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | /* sleep until signaled */ |
| 388 | if (timedwait) { |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 389 | #ifdef HAVE_TIMEDWAIT_MONOTONIC |
| 390 | cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond, |
| 391 | &gDvm.heapWorkerLock, &trimtime); |
| 392 | #else |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 393 | cc = pthread_cond_timedwait(&gDvm.heapWorkerCond, |
| 394 | &gDvm.heapWorkerLock, &trimtime); |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 395 | #endif |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 396 | assert(cc == 0 || cc == ETIMEDOUT || cc == EINTR); |
| 397 | } else { |
| 398 | cc = pthread_cond_wait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock); |
| 399 | assert(cc == 0); |
| 400 | } |
| 401 | |
| 402 | /* dvmChangeStatus() may block; don't hold heapWorkerLock. |
| 403 | */ |
| 404 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 405 | dvmChangeStatus(NULL, THREAD_RUNNING); |
| 406 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 407 | LOGV("HeapWorker is awake\n"); |
| 408 | |
| 409 | /* Process any events in the queue. |
| 410 | */ |
| 411 | doHeapWork(self); |
| 412 | } |
| 413 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 414 | |
| 415 | LOGD("HeapWorker thread shutting down\n"); |
| 416 | return NULL; |
| 417 | } |
| 418 | |
| 419 | /* |
| 420 | * Wake up the heap worker to let it know that there's work to be done. |
| 421 | */ |
| 422 | void dvmSignalHeapWorker(bool shouldLock) |
| 423 | { |
| 424 | int cc; |
| 425 | |
| 426 | if (shouldLock) { |
| 427 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 428 | } |
| 429 | |
| 430 | cc = pthread_cond_signal(&gDvm.heapWorkerCond); |
| 431 | assert(cc == 0); |
| 432 | |
| 433 | if (shouldLock) { |
| 434 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 435 | } |
| 436 | } |
| 437 | |
| 438 | /* |
| 439 | * Block until all pending heap worker work has finished. |
| 440 | */ |
| 441 | void dvmWaitForHeapWorkerIdle() |
| 442 | { |
| 443 | int cc; |
| 444 | |
| 445 | assert(gDvm.heapWorkerReady); |
| 446 | |
| 447 | dvmChangeStatus(NULL, THREAD_VMWAIT); |
| 448 | |
| 449 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 450 | |
| 451 | /* Wake up the heap worker and wait for it to finish. */ |
| 452 | //TODO(http://b/issue?id=699704): This will deadlock if |
| 453 | // called from finalize(), enqueue(), or clear(). We |
| 454 | // need to detect when this is called from the HeapWorker |
| 455 | // context and just give up. |
| 456 | dvmSignalHeapWorker(false); |
| 457 | cc = pthread_cond_wait(&gDvm.heapWorkerIdleCond, &gDvm.heapWorkerLock); |
| 458 | assert(cc == 0); |
| 459 | |
| 460 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 461 | |
| 462 | dvmChangeStatus(NULL, THREAD_RUNNING); |
| 463 | } |
| 464 | |
| 465 | /* |
| 466 | * Do not return until any pending heap work has finished. This may |
| 467 | * or may not happen in the context of the calling thread. |
| 468 | * No exceptions will escape. |
| 469 | */ |
| 470 | void dvmRunFinalizationSync() |
| 471 | { |
| 472 | if (gDvm.zygote) { |
| 473 | assert(!gDvm.heapWorkerReady); |
| 474 | |
| 475 | /* When in zygote mode, there is no heap worker. |
| 476 | * Do the work in the current thread. |
| 477 | */ |
| 478 | dvmLockMutex(&gDvm.heapWorkerLock); |
| 479 | doHeapWork(dvmThreadSelf()); |
| 480 | dvmUnlockMutex(&gDvm.heapWorkerLock); |
| 481 | } else { |
| 482 | /* Outside of zygote mode, we can just ask the |
| 483 | * heap worker thread to do the work. |
| 484 | */ |
| 485 | dvmWaitForHeapWorkerIdle(); |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | /* |
| 490 | * Requests that dvmHeapSourceTrim() be called no sooner |
| 491 | * than timeoutSec seconds from now. If timeoutSec |
| 492 | * is zero, any pending trim is cancelled. |
| 493 | * |
| 494 | * Caller must hold heapWorkerLock. |
| 495 | */ |
| 496 | void dvmScheduleHeapSourceTrim(size_t timeoutSec) |
| 497 | { |
| 498 | GcHeap *gcHeap = gDvm.gcHeap; |
| 499 | struct timespec timeout; |
| 500 | |
| 501 | if (timeoutSec == 0) { |
| 502 | timeout.tv_sec = 0; |
| 503 | timeout.tv_nsec = 0; |
| 504 | /* Don't wake up the thread just to tell it to cancel. |
| 505 | * If it wakes up naturally, we can avoid the extra |
| 506 | * context switch. |
| 507 | */ |
| 508 | } else { |
| 509 | struct timeval now; |
| 510 | |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 511 | #ifdef HAVE_TIMEDWAIT_MONOTONIC |
| 512 | clock_gettime(CLOCK_MONOTONIC, &timeout); |
| 513 | timeout.tv_sec += timeoutSec; |
| 514 | #else |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 515 | gettimeofday(&now, NULL); |
| 516 | timeout.tv_sec = now.tv_sec + timeoutSec; |
| 517 | timeout.tv_nsec = now.tv_usec * 1000; |
Andy McFadden | eaf2ee0 | 2009-06-02 13:42:44 -0700 | [diff] [blame] | 518 | #endif |
The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 519 | dvmSignalHeapWorker(false); |
| 520 | } |
| 521 | gcHeap->heapWorkerNextTrim = timeout; |
| 522 | } |