/* Block until the queue length is 0, or there is a pending suspend request */ void dvmCompilerDrainQueue(void) { Thread *self = dvmThreadSelf(); dvmLockMutex(&gDvmJit.compilerLock); while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread && self->suspendCount == 0) { /* * Use timed wait here - more than one mutator threads may be blocked * but the compiler thread will only signal once when the queue is * emptied. Furthermore, the compiler thread may have been shutdown * so the blocked thread may never get the wakeup signal. */ dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock, 1000, 0); } dvmUnlockMutex(&gDvmJit.compilerLock); }
/** * @brief 丢弃编译队列 */ void dvmCompilerDrainQueue(void) { Thread *self = dvmThreadSelf(); dvmLockMutex(&gDvmJit.compilerLock); /* 遍历整个队列,并不停的检测编译线程是否终止,并且自身线程没有被挂起 */ while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread && self->suspendCount == 0) { /* * Use timed wait here - more than one mutator threads may be blocked * but the compiler thread will only signal once when the queue is * emptied. Furthermore, the compiler thread may have been shutdown * so the blocked thread may never get the wakeup signal. */ /* 使用等待时间 - 超过一个mutator线程被阻塞但是编译器线程将 * 仅有一条一次当队列被提交,编译器线程不关闭,阻塞线程将永远不会唤醒*/ dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock, 1000, 0); } dvmUnlockMutex(&gDvmJit.compilerLock); }
static void *compilerThreadStart(void *arg) { dvmChangeStatus(NULL, THREAD_VMWAIT); /* * If we're not running stand-alone, wait a little before * recieving translation requests on the assumption that process start * up code isn't worth compiling. We'll resume when the framework * signals us that the first screen draw has happened, or the timer * below expires (to catch daemons). * * There is a theoretical race between the callback to * VMRuntime.startJitCompiation and when the compiler thread reaches this * point. In case the callback happens earlier, in order not to permanently * hold the system_server (which is not using the timed wait) in * interpreter-only mode we bypass the delay here. */ if (gDvmJit.runningInAndroidFramework && !gDvmJit.alreadyEnabledViaFramework) { /* * If the current VM instance is the system server (detected by having * 0 in gDvm.systemServerPid), we will use the indefinite wait on the * conditional variable to determine whether to start the JIT or not. * If the system server detects that the whole system is booted in * safe mode, the conditional variable will never be signaled and the * system server will remain in the interpreter-only mode. All * subsequent apps will be started with the --enable-safemode flag * explicitly appended. */ if (gDvm.systemServerPid == 0) { dvmLockMutex(&gDvmJit.compilerLock); pthread_cond_wait(&gDvmJit.compilerQueueActivity, &gDvmJit.compilerLock); dvmUnlockMutex(&gDvmJit.compilerLock); ALOGD("JIT started for system_server"); } else { dvmLockMutex(&gDvmJit.compilerLock); /* * TUNING: experiment with the delay & perhaps make it * target-specific */ dvmRelativeCondWait(&gDvmJit.compilerQueueActivity, &gDvmJit.compilerLock, 3000, 0); dvmUnlockMutex(&gDvmJit.compilerLock); } if (gDvmJit.haltCompilerThread) { return NULL; } } compilerThreadStartup(); dvmLockMutex(&gDvmJit.compilerLock); /* * Since the compiler thread will not touch any objects on the heap once * being created, we just fake its state as VMWAIT so that it can be a * bit late when there is suspend request pending. */ while (!gDvmJit.haltCompilerThread) { if (workQueueLength() == 0) { int cc; cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty); assert(cc == 0); pthread_cond_wait(&gDvmJit.compilerQueueActivity, &gDvmJit.compilerLock); continue; } else { do { CompilerWorkOrder work = workDequeue(); dvmUnlockMutex(&gDvmJit.compilerLock); #if defined(WITH_JIT_TUNING) /* * This is live across setjmp(). Mark it volatile to suppress * a gcc warning. We should not need this since it is assigned * only once but gcc is not smart enough. */ volatile u8 startTime = dvmGetRelativeTimeUsec(); #endif /* * Check whether there is a suspend request on me. This * is necessary to allow a clean shutdown. * * However, in the blocking stress testing mode, let the * compiler thread continue doing compilations to unblock * other requesting threads. This may occasionally cause * shutdown from proceeding cleanly in the standalone invocation * of the vm but this should be acceptable. */ if (!gDvmJit.blockingMode) dvmCheckSuspendPending(dvmThreadSelf()); /* Is JitTable filling up? */ if (gDvmJit.jitTableEntriesUsed > (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) { bool resizeFail = dvmJitResizeJitTable(gDvmJit.jitTableSize * 2); /* * If the jit table is full, consider it's time to reset * the code cache too. */ gDvmJit.codeCacheFull |= resizeFail; } if (gDvmJit.haltCompilerThread) { ALOGD("Compiler shutdown in progress - discarding request"); } else if (!gDvmJit.codeCacheFull) { jmp_buf jmpBuf; work.bailPtr = &jmpBuf; bool aborted = setjmp(jmpBuf); if (!aborted) { bool codeCompiled = dvmCompilerDoWork(&work); /* * Make sure we are still operating with the * same translation cache version. See * Issue 4271784 for details. */ dvmLockMutex(&gDvmJit.compilerLock); if ((work.result.cacheVersion == gDvmJit.cacheVersion) && codeCompiled && !work.result.discardResult && work.result.codeAddress) { dvmJitSetCodeAddr(work.pc, work.result.codeAddress, work.result.instructionSet, false, /* not method entry */ work.result.profileCodeSize); } dvmUnlockMutex(&gDvmJit.compilerLock); } dvmCompilerArenaReset(); } free(work.info); #if defined(WITH_JIT_TUNING) gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime; #endif dvmLockMutex(&gDvmJit.compilerLock); } while (workQueueLength() != 0); } } pthread_cond_signal(&gDvmJit.compilerQueueEmpty); dvmUnlockMutex(&gDvmJit.compilerLock); /* * As part of detaching the thread we need to call into Java code to update * the ThreadGroup, and we should not be in VMWAIT state while executing * interpreted code. */ dvmChangeStatus(NULL, THREAD_RUNNING); if (gDvm.verboseShutdown) ALOGD("Compiler thread shutting down"); return NULL; }
static void resetCodeCache(void) { Thread* thread; u8 startTime = dvmGetRelativeTimeUsec(); int inJit = 0; int byteUsed = gDvmJit.codeCacheByteUsed; /* If any thread is found stuck in the JIT state, don't reset the cache */ dvmLockThreadList(NULL); for (thread = gDvm.threadList; thread != NULL; thread = thread->next) { /* * Crawl the stack to wipe out the returnAddr field so that * 1) the soon-to-be-deleted code in the JIT cache won't be used * 2) or the thread stuck in the JIT land will soon return * to the interpreter land */ crawlDalvikStack(thread, false); if (thread->inJitCodeCache) { inJit++; } /* Cancel any ongoing trace selection */ dvmDisableSubMode(thread, kSubModeJitTraceBuild); } dvmUnlockThreadList(); if (inJit) { ALOGD("JIT code cache reset delayed (%d bytes %d/%d)", gDvmJit.codeCacheByteUsed, gDvmJit.numCodeCacheReset, ++gDvmJit.numCodeCacheResetDelayed); return; } /* Lock the mutex to clean up the work queue */ dvmLockMutex(&gDvmJit.compilerLock); /* Update the translation cache version */ gDvmJit.cacheVersion++; /* Drain the work queue to free the work orders */ while (workQueueLength()) { CompilerWorkOrder work = workDequeue(); free(work.info); } /* Reset the JitEntry table contents to the initial unpopulated state */ dvmJitResetTable(); UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed); /* * Wipe out the code cache content to force immediate crashes if * stale JIT'ed code is invoked. */ dvmCompilerCacheClear((char *) gDvmJit.codeCache + gDvmJit.templateSize, gDvmJit.codeCacheByteUsed - gDvmJit.templateSize); dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache, (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed); PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed); /* Reset the current mark of used bytes to the end of template code */ gDvmJit.codeCacheByteUsed = gDvmJit.templateSize; gDvmJit.numCompilations = 0; /* Reset the work queue */ memset(gDvmJit.compilerWorkQueue, 0, sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE); gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0; gDvmJit.compilerQueueLength = 0; /* Reset the IC patch work queue */ dvmLockMutex(&gDvmJit.compilerICPatchLock); gDvmJit.compilerICPatchIndex = 0; dvmUnlockMutex(&gDvmJit.compilerICPatchLock); /* * Reset the inflight compilation address (can only be done in safe points * or by the compiler thread when its thread state is RUNNING). */ gDvmJit.inflightBaseAddr = NULL; /* All clear now */ gDvmJit.codeCacheFull = false; dvmUnlockMutex(&gDvmJit.compilerLock); ALOGD("JIT code cache reset in %lld ms (%d bytes %d/%d)", (dvmGetRelativeTimeUsec() - startTime) / 1000, byteUsed, ++gDvmJit.numCodeCacheReset, gDvmJit.numCodeCacheResetDelayed); }