summaryrefslogtreecommitdiffstats
path: root/vm/alloc/HeapWorker.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm/alloc/HeapWorker.c')
-rw-r--r--vm/alloc/HeapWorker.c472
1 files changed, 0 insertions, 472 deletions
diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c
deleted file mode 100644
index 60173818e..000000000
--- a/vm/alloc/HeapWorker.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * An async worker thread to handle certain heap operations that need
- * to be done in a separate thread to avoid synchronization problems.
- * HeapWorkers and reference enqueuing are handled by this thread.
- * The VM does all clearing.
- */
-#include "Dalvik.h"
-#include "alloc/HeapInternal.h"
-#include "alloc/HeapWorker.h"
-
-#include <sys/time.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <signal.h>
-#include <errno.h> // for ETIMEDOUT, etc.
-
-static void* heapWorkerThreadStart(void* arg);
-
-/*
- * Initialize any HeapWorker state that Heap.c
- * cares about. This lets the GC start before the
- * HeapWorker thread is initialized.
- */
-void dvmInitializeHeapWorkerState()
-{
- assert(!gDvm.heapWorkerInitialized);
-
- dvmInitMutex(&gDvm.heapWorkerLock);
- pthread_cond_init(&gDvm.heapWorkerCond, NULL);
- pthread_cond_init(&gDvm.heapWorkerIdleCond, NULL);
-
- gDvm.heapWorkerInitialized = true;
-}
-
-/*
- * Crank up the heap worker thread.
- *
- * Does not return until the thread is ready for business.
- */
-bool dvmHeapWorkerStartup(void)
-{
- assert(!gDvm.haltHeapWorker);
- assert(!gDvm.heapWorkerReady);
- assert(gDvm.heapWorkerHandle == 0);
- assert(gDvm.heapWorkerInitialized);
-
- /* use heapWorkerLock/heapWorkerCond to communicate readiness */
- dvmLockMutex(&gDvm.heapWorkerLock);
-
-//BUG: If a GC happens in here or in the new thread while we hold the lock,
-// the GC will deadlock when trying to acquire heapWorkerLock.
- if (!dvmCreateInternalThread(&gDvm.heapWorkerHandle,
- "HeapWorker", heapWorkerThreadStart, NULL))
- {
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- return false;
- }
-
- /*
- * Wait for the heap worker to come up. We know the thread was created,
- * so this should not get stuck.
- */
- while (!gDvm.heapWorkerReady) {
- dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
- }
-
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- return true;
-}
-
-/*
- * Shut down the heap worker thread if it was started.
- */
-void dvmHeapWorkerShutdown(void)
-{
- void* threadReturn;
-
- /* note: assuming that (pthread_t)0 is not a valid thread handle */
- if (gDvm.heapWorkerHandle != 0) {
- gDvm.haltHeapWorker = true;
- dvmSignalHeapWorker(true);
-
- /*
- * We may not want to wait for the heapWorkers to complete. It's
- * a good idea to do so, in case they're holding some sort of OS
- * resource that doesn't get reclaimed when the process exits
- * (e.g. an open temp file).
- */
- if (pthread_join(gDvm.heapWorkerHandle, &threadReturn) != 0)
- LOGW("HeapWorker thread join failed\n");
- else if (gDvm.verboseShutdown)
- LOGD("HeapWorker thread has shut down\n");
-
- gDvm.heapWorkerReady = false;
- }
-}
-
-/* Make sure that the HeapWorker thread hasn't spent an inordinate
- * amount of time inside a finalizer.
- *
- * Aborts the VM if the thread appears to be wedged.
- *
- * The caller must hold the heapWorkerLock to guarantee an atomic
- * read of the watchdog values.
- */
-void dvmAssertHeapWorkerThreadRunning()
-{
- if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) {
- static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec
-
- u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime;
- u8 now = dvmGetRelativeTimeUsec();
- u8 delta = now - heapWorkerInterpStartTime;
-
- if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT &&
- (gDvm.debuggerActive || gDvm.nativeDebuggerActive))
- {
- /*
- * Debugger suspension can block the thread indefinitely. For
- * best results we should reset this explicitly whenever the
- * HeapWorker thread is resumed. Unfortunately this is also
- * affected by native debuggers, and we have no visibility
- * into how they're manipulating us. So, we ignore the
- * watchdog and just reset the timer.
- */
- LOGI("Debugger is attached -- suppressing HeapWorker watchdog\n");
- gDvm.gcHeap->heapWorkerInterpStartTime = now; /* reset timer */
- } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) {
- /*
- * Before we give up entirely, see if maybe we're just not
- * getting any CPU time because we're stuck in a background
- * process group. If we successfully move the thread into the
- * foreground we'll just leave it there (it doesn't do anything
- * if the process isn't GCing).
- */
- dvmLockThreadList(NULL);
- Thread* thread = dvmGetThreadByHandle(gDvm.heapWorkerHandle);
- dvmUnlockThreadList();
-
- if (thread != NULL) {
- int priChangeFlags, threadPrio;
- SchedPolicy threadPolicy;
- priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
- &threadPrio, &threadPolicy);
- if (priChangeFlags != 0) {
- LOGI("HeapWorker watchdog expired, raising priority"
- " and retrying\n");
- gDvm.gcHeap->heapWorkerInterpStartTime = now;
- return;
- }
- }
-
- char* desc = dexProtoCopyMethodDescriptor(
- &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
- LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s\n",
- delta / 1000,
- gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
- gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
- free(desc);
- dvmDumpAllThreads(true);
-
- /* try to get a debuggerd dump from the target thread */
- dvmNukeThread(thread);
-
- /* abort the VM */
- dvmAbort();
- } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) {
- char* desc = dexProtoCopyMethodDescriptor(
- &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
- LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s\n",
- delta / 1000,
- gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
- gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
- free(desc);
- }
- }
-}
-
-/*
- * Acquires a mutex, transitioning to the VMWAIT state if the mutex is
- * held. This allows the thread to suspend while it waits for another
- * thread to release the mutex.
- */
-static void lockMutex(pthread_mutex_t *mu)
-{
- Thread *self;
- ThreadStatus oldStatus;
-
- assert(mu != NULL);
- if (dvmTryLockMutex(mu) != 0) {
- self = dvmThreadSelf();
- assert(self != NULL);
- oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
- dvmLockMutex(mu);
- dvmChangeStatus(self, oldStatus);
- }
-}
-
-static void callMethod(Thread *self, Object *obj, Method *method)
-{
- JValue unused;
-
- /* Keep track of the method we're about to call and
- * the current time so that other threads can detect
- * when this thread wedges and provide useful information.
- */
- gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec();
- gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec();
- gDvm.gcHeap->heapWorkerCurrentMethod = method;
- gDvm.gcHeap->heapWorkerCurrentObject = obj;
-
- /* Call the method.
- *
- * Don't hold the lock when executing interpreted
- * code. It may suspend, and the GC needs to grab
- * heapWorkerLock.
- */
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- if (false) {
- /* Log entry/exit; this will likely flood the log enough to
- * cause "logcat" to drop entries.
- */
- char tmpTag[16];
- sprintf(tmpTag, "HW%d", self->systemTid);
- LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor);
- dvmCallMethod(self, method, obj, &unused);
- LOG(LOG_DEBUG, tmpTag, " done\n");
- } else {
- dvmCallMethod(self, method, obj, &unused);
- }
- /*
- * Reacquire the heap worker lock in a suspend-friendly way.
- */
- lockMutex(&gDvm.heapWorkerLock);
-
- gDvm.gcHeap->heapWorkerCurrentObject = NULL;
- gDvm.gcHeap->heapWorkerCurrentMethod = NULL;
- gDvm.gcHeap->heapWorkerInterpStartTime = 0LL;
-
- /* Exceptions thrown during these calls interrupt
- * the method, but are otherwise ignored.
- */
- if (dvmCheckException(self)) {
-#if DVM_SHOW_EXCEPTION >= 1
- LOGI("Uncaught exception thrown by finalizer (will be discarded):\n");
- dvmLogExceptionStackTrace();
-#endif
- dvmClearException(self);
- }
-}
-
-/* Process all enqueued heap work, including finalizers and reference
- * enqueueing. Clearing has already been done by the VM.
- *
- * Caller must hold gDvm.heapWorkerLock.
- */
-static void doHeapWork(Thread *self)
-{
- Object *obj;
- size_t numReferencesEnqueued;
-
- assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL);
- numReferencesEnqueued = 0;
- while ((obj = dvmGetNextHeapWorkerObject()) != NULL) {
- /* Make sure the object hasn't been collected since
- * being scheduled.
- */
- assert(dvmIsValidObject(obj));
-
- /* Call the appropriate method(s).
- */
- assert(dvmGetFieldObject(
- obj, gDvm.offJavaLangRefReference_queue) != NULL);
- assert(dvmGetFieldObject(
- obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
- numReferencesEnqueued++;
- callMethod(self, obj, gDvm.methJavaLangRefReference_enqueueInternal);
-
- /* Let the GC collect the object.
- */
- dvmReleaseTrackedAlloc(obj, self);
- }
- LOGV("Enqueued %zd references", numReferencesEnqueued);
-}
-
-/*
- * The heap worker thread sits quietly until the GC tells it there's work
- * to do.
- */
-static void* heapWorkerThreadStart(void* arg)
-{
- Thread *self = dvmThreadSelf();
-
- UNUSED_PARAMETER(arg);
-
- LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId);
-
- /* tell the main thread that we're ready */
- lockMutex(&gDvm.heapWorkerLock);
- gDvm.heapWorkerReady = true;
- dvmSignalCond(&gDvm.heapWorkerCond);
- dvmUnlockMutex(&gDvm.heapWorkerLock);
-
- lockMutex(&gDvm.heapWorkerLock);
- while (!gDvm.haltHeapWorker) {
- struct timespec trimtime;
- bool timedwait = false;
-
- /* We're done running interpreted code for now. */
- dvmChangeStatus(NULL, THREAD_VMWAIT);
-
- /* Signal anyone who wants to know when we're done. */
- dvmBroadcastCond(&gDvm.heapWorkerIdleCond);
-
- /* Trim the heap if we were asked to. */
- trimtime = gDvm.gcHeap->heapWorkerNextTrim;
- if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
- struct timespec now;
-
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
- clock_gettime(CLOCK_MONOTONIC, &now); // relative time
-#else
- struct timeval tvnow;
- gettimeofday(&tvnow, NULL); // absolute time
- now.tv_sec = tvnow.tv_sec;
- now.tv_nsec = tvnow.tv_usec * 1000;
-#endif
-
- if (trimtime.tv_sec < now.tv_sec ||
- (trimtime.tv_sec == now.tv_sec &&
- trimtime.tv_nsec <= now.tv_nsec))
- {
- size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
-
- /*
- * Acquire the gcHeapLock. The requires releasing the
- * heapWorkerLock before the gcHeapLock is acquired.
- * It is possible that the gcHeapLock may be acquired
- * during a concurrent GC in which case heapWorkerLock
- * is held by the GC and we are unable to make forward
- * progress. We avoid deadlock by releasing the
- * gcHeapLock and then waiting to be signaled when the
- * GC completes. There is no guarantee that the next
- * time we are run will coincide with GC inactivity so
- * the check and wait must be performed within a loop.
- */
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- dvmLockHeap();
- dvmWaitForConcurrentGcToComplete();
- dvmLockMutex(&gDvm.heapWorkerLock);
-
- memset(madvisedSizes, 0, sizeof(madvisedSizes));
- dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
-
- dvmUnlockHeap();
-
- trimtime.tv_sec = 0;
- trimtime.tv_nsec = 0;
- gDvm.gcHeap->heapWorkerNextTrim = trimtime;
- } else {
- timedwait = true;
- }
- }
-
- /* sleep until signaled */
- if (timedwait) {
- int cc __attribute__ ((__unused__));
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
- cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond,
- &gDvm.heapWorkerLock, &trimtime);
-#else
- cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
- &gDvm.heapWorkerLock, &trimtime);
-#endif
- assert(cc == 0 || cc == ETIMEDOUT);
- } else {
- dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
- }
-
- /*
- * Return to the running state before doing heap work. This
- * will block if the GC has initiated a suspend. We release
- * the heapWorkerLock beforehand for the GC to make progress
- * and wait to be signaled after the GC completes. There is
- * no guarantee that the next time we are run will coincide
- * with GC inactivity so the check and wait must be performed
- * within a loop.
- */
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- dvmChangeStatus(NULL, THREAD_RUNNING);
- dvmLockHeap();
- dvmWaitForConcurrentGcToComplete();
- dvmLockMutex(&gDvm.heapWorkerLock);
- dvmUnlockHeap();
- LOGV("HeapWorker is awake\n");
-
- /* Process any events in the queue.
- */
- doHeapWork(self);
- }
- dvmUnlockMutex(&gDvm.heapWorkerLock);
-
- if (gDvm.verboseShutdown)
- LOGD("HeapWorker thread shutting down\n");
- return NULL;
-}
-
-/*
- * Wake up the heap worker to let it know that there's work to be done.
- */
-void dvmSignalHeapWorker(bool shouldLock)
-{
- if (shouldLock) {
- dvmLockMutex(&gDvm.heapWorkerLock);
- }
-
- dvmSignalCond(&gDvm.heapWorkerCond);
-
- if (shouldLock) {
- dvmUnlockMutex(&gDvm.heapWorkerLock);
- }
-}
-
-/*
- * Requests that dvmHeapSourceTrim() be called no sooner
- * than timeoutSec seconds from now. If timeoutSec
- * is zero, any pending trim is cancelled.
- *
- * Caller must hold heapWorkerLock.
- */
-void dvmScheduleHeapSourceTrim(size_t timeoutSec)
-{
- GcHeap *gcHeap = gDvm.gcHeap;
- struct timespec timeout;
-
- if (timeoutSec == 0) {
- timeout.tv_sec = 0;
- timeout.tv_nsec = 0;
- /* Don't wake up the thread just to tell it to cancel.
- * If it wakes up naturally, we can avoid the extra
- * context switch.
- */
- } else {
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
- clock_gettime(CLOCK_MONOTONIC, &timeout);
- timeout.tv_sec += timeoutSec;
-#else
- struct timeval now;
- gettimeofday(&now, NULL);
- timeout.tv_sec = now.tv_sec + timeoutSec;
- timeout.tv_nsec = now.tv_usec * 1000;
-#endif
- dvmSignalHeapWorker(false);
- }
- gcHeap->heapWorkerNextTrim = timeout;
-}