auto import from //depot/cupcake/@135843
diff --git a/vm/Thread.c b/vm/Thread.c
new file mode 100644
index 0000000..42b527e
--- /dev/null
+++ b/vm/Thread.c
@@ -0,0 +1,3266 @@
+/*
+ * 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.
+ */
+/*
+ * Thread support.
+ */
+#include "Dalvik.h"
+
+#include "utils/threads.h" // need Android thread priorities
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
+/* desktop Linux needs a little help with gettid() */
+#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
+#define __KERNEL__
+# include <linux/unistd.h>
+#ifdef _syscall0
+_syscall0(pid_t,gettid)
+#else
+pid_t gettid() { return syscall(__NR_gettid);}
+#endif
+#undef __KERNEL__
+#endif
+
+// change this to LOGV/LOGD to debug thread activity
+#define LOG_THREAD LOGVV
+
+/*
+Notes on Threading
+
+All threads are native pthreads. All threads, except the JDWP debugger
+thread, are visible to code running in the VM and to the debugger. (We
+don't want the debugger to try to manipulate the thread that listens for
+instructions from the debugger.) Internal VM threads are in the "system"
+ThreadGroup, all others are in the "main" ThreadGroup, per convention.
+
+The GC only runs when all threads have been suspended. Threads are
+expected to suspend themselves, using a "safe point" mechanism. We check
+for a suspend request at certain points in the main interpreter loop,
+and on requests coming in from native code (e.g. all JNI functions).
+Certain debugger events may inspire threads to self-suspend.
+
+Native methods must use JNI calls to modify object references to avoid
+clashes with the GC. JNI doesn't provide a way for native code to access
+arrays of objects as such -- code must always get/set individual entries --
+so it should be possible to fully control access through JNI.
+
+Internal native VM threads, such as the finalizer thread, must explicitly
+check for suspension periodically. In most cases they will be sound
+asleep on a condition variable, and won't notice the suspension anyway.
+
+Threads may be suspended by the GC, debugger, or the SIGQUIT listener
+thread. The debugger may suspend or resume individual threads, while the
+GC always suspends all threads. Each thread has a "suspend count" that
+is incremented on suspend requests and decremented on resume requests.
+When the count is zero, the thread is runnable. This allows us to fulfill
+a debugger requirement: if the debugger suspends a thread, the thread is
+not allowed to run again until the debugger resumes it (or disconnects,
+in which case we must resume all debugger-suspended threads).
+
+Paused threads sleep on a condition variable, and are awoken en masse.
+Certain "slow" VM operations, such as starting up a new thread, will be
+done in a separate "VMWAIT" state, so that the rest of the VM doesn't
+freeze up waiting for the operation to finish. Threads must check for
+pending suspension when leaving VMWAIT.
+
+Because threads suspend themselves while interpreting code or when native
+code makes JNI calls, there is no risk of suspending while holding internal
+VM locks. All threads can enter a suspended (or native-code-only) state.
+Also, we don't have to worry about object references existing solely
+in hardware registers.
+
+We do, however, have to worry about objects that were allocated internally
+and aren't yet visible to anything else in the VM. If we allocate an
+object, and then go to sleep on a mutex after changing to a non-RUNNING
+state (e.g. while trying to allocate a second object), the first object
+could be garbage-collected out from under us while we sleep. To manage
+this, we automatically add all allocated objects to an internal object
+tracking list, and only remove them when we know we won't be suspended
+before the object appears in the GC root set.
+
+The debugger may choose to suspend or resume a single thread, which can
+lead to application-level deadlocks; this is expected behavior. The VM
+will only check for suspension of single threads when the debugger is
+active (the java.lang.Thread calls for this are deprecated and hence are
+not supported). Resumption of a single thread is handled by decrementing
+the thread's suspend count and sending a broadcast signal to the condition
+variable. (This will cause all threads to wake up and immediately go back
+to sleep, which isn't tremendously efficient, but neither is having the
+debugger attached.)
+
+The debugger is not allowed to resume threads suspended by the GC. This
+is trivially enforced by ignoring debugger requests while the GC is running
+(the JDWP thread is suspended during GC).
+
+The VM maintains a Thread struct for every pthread known to the VM. There
+is a java/lang/Thread object associated with every Thread. At present,
+there is no safe way to go from a Thread object to a Thread struct except by
+locking and scanning the list; this is necessary because the lifetimes of
+the two are not closely coupled. We may want to change this behavior,
+though at present the only performance impact is on the debugger (see
+threadObjToThread()). See also notes about dvmDetachCurrentThread().
+*/
+/*
+Alternate implementation (signal-based):
+
+Threads run without safe points -- zero overhead. The VM uses a signal
+(e.g. pthread_kill(SIGUSR1)) to notify threads of suspension or resumption.
+
+The trouble with using signals to suspend threads is that it means a thread
+can be in the middle of an operation when garbage collection starts.
+To prevent some sticky situations, we have to introduce critical sections
+to the VM code.
+
+Critical sections temporarily block suspension for a given thread.
+The thread must move to a non-blocked state (and self-suspend) after
+finishing its current task. If the thread blocks on a resource held
+by a suspended thread, we're hosed.
+
+One approach is to require that no blocking operations, notably
+acquisition of mutexes, can be performed within a critical section.
+This is too limiting. For example, if thread A gets suspended while
+holding the thread list lock, it will prevent the GC or debugger from
+being able to safely access the thread list. We need to wrap the critical
+section around the entire operation (enter critical, get lock, do stuff,
+release lock, exit critical).
+
+A better approach is to declare that certain resources can only be held
+within critical sections. A thread that enters a critical section and
+then gets blocked on the thread list lock knows that the thread it is
+waiting for is also in a critical section, and will release the lock
+before suspending itself. Eventually all threads will complete their
+operations and self-suspend. For this to work, the VM must:
+
+ (1) Determine the set of resources that may be accessed from the GC or
+ debugger threads. The mutexes guarding those go into the "critical
+ resource set" (CRS).
+ (2) Ensure that no resource in the CRS can be acquired outside of a
+ critical section. This can be verified with an assert().
+ (3) Ensure that only resources in the CRS can be held while in a critical
+ section. This is harder to enforce.
+
+If any of these conditions are not met, deadlock can ensue when grabbing
+resources in the GC or debugger (#1) or waiting for threads to suspend
+(#2,#3). (You won't actually deadlock in the GC, because if the semantics
+above are followed you don't need to lock anything in the GC. The risk is
+rather that the GC will access data structures in an intermediate state.)
+
+This approach requires more care and awareness in the VM than
+safe-pointing. Because the GC and debugger are fairly intrusive, there
+really aren't any internal VM resources that aren't shared. Thus, the
+enter/exit critical calls can be added to internal mutex wrappers, which
+makes it easy to get #1 and #2 right.
+
+An ordering should be established for all locks to avoid deadlocks.
+
+Monitor locks, which are also implemented with pthread calls, should not
+cause any problems here. Threads fighting over such locks will not be in
+critical sections and can be suspended freely.
+
+This can get tricky if we ever need exclusive access to VM and non-VM
+resources at the same time. It's not clear if this is a real concern.
+
+There are (at least) two ways to handle the incoming signals:
+
+ (a) Always accept signals. If we're in a critical section, the signal
+ handler just returns without doing anything (the "suspend level"
+ should have been incremented before the signal was sent). Otherwise,
+ if the "suspend level" is nonzero, we go to sleep.
+ (b) Block signals in critical sections. This ensures that we can't be
+ interrupted in a critical section, but requires pthread_sigmask()
+ calls on entry and exit.
+
+This is a choice between blocking the message and blocking the messenger.
+Because UNIX signals are unreliable (you can only know that you have been
+signaled, not whether you were signaled once or 10 times), the choice is
+not significant for correctness. The choice depends on the efficiency
+of pthread_sigmask() and the desire to actually block signals. Either way,
+it is best to ensure that there is only one indication of "blocked";
+having two (i.e. block signals and set a flag, then only send a signal
+if the flag isn't set) can lead to race conditions.
+
+The signal handler must take care to copy registers onto the stack (via
+setjmp), so that stack scans find all references. Because we have to scan
+native stacks, "exact" GC is not possible with this approach.
+
+Some other concerns with flinging signals around:
+ - Odd interactions with some debuggers (e.g. gdb on the Mac)
+ - Restrictions on some standard library calls during GC (e.g. don't
+ use printf on stdout to print GC debug messages)
+*/
+
+#define kMaxThreadId ((1<<15) - 1)
+#define kMainThreadId ((1<<1) | 1)
+
+
+static Thread* allocThread(int interpStackSize);
+static bool prepareThread(Thread* thread);
+static void setThreadSelf(Thread* thread);
+static void unlinkThread(Thread* thread);
+static void freeThread(Thread* thread);
+static void assignThreadId(Thread* thread);
+static bool createFakeEntryFrame(Thread* thread);
+static bool createFakeRunFrame(Thread* thread);
+static void* interpThreadStart(void* arg);
+static void* internalThreadStart(void* arg);
+static void threadExitUncaughtException(Thread* thread, Object* group);
+static void threadExitCheck(void* arg);
+static void waitForThreadSuspend(Thread* self, Thread* thread);
+static int getThreadPriorityFromSystem(void);
+
+
+/*
+ * Initialize thread list and main thread's environment. We need to set
+ * up some basic stuff so that dvmThreadSelf() will work when we start
+ * loading classes (e.g. to check for exceptions).
+ */
+bool dvmThreadStartup(void)
+{
+ Thread* thread;
+
+ /* allocate a TLS slot */
+ if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) {
+ LOGE("ERROR: pthread_key_create failed\n");
+ return false;
+ }
+
+ /* test our pthread lib */
+ if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL)
+ LOGW("WARNING: newly-created pthread TLS slot is not NULL\n");
+
+ /* prep thread-related locks and conditions */
+ dvmInitMutex(&gDvm.threadListLock);
+ pthread_cond_init(&gDvm.threadStartCond, NULL);
+ //dvmInitMutex(&gDvm.vmExitLock);
+ pthread_cond_init(&gDvm.vmExitCond, NULL);
+ dvmInitMutex(&gDvm._threadSuspendLock);
+ dvmInitMutex(&gDvm.threadSuspendCountLock);
+ pthread_cond_init(&gDvm.threadSuspendCountCond, NULL);
+#ifdef WITH_DEADLOCK_PREDICTION
+ dvmInitMutex(&gDvm.deadlockHistoryLock);
+#endif
+
+ /*
+ * Dedicated monitor for Thread.sleep().
+ * TODO: change this to an Object* so we don't have to expose this
+ * call, and we interact better with JDWP monitor calls. Requires
+ * deferring the object creation to much later (e.g. final "main"
+ * thread prep) or until first use.
+ */
+ gDvm.threadSleepMon = dvmCreateMonitor(NULL);
+
+ gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false);
+
+ thread = allocThread(gDvm.stackSize);
+ if (thread == NULL)
+ return false;
+
+ /* switch mode for when we run initializers */
+ thread->status = THREAD_RUNNING;
+
+ /*
+ * We need to assign the threadId early so we can lock/notify
+ * object monitors. We'll set the "threadObj" field later.
+ */
+ prepareThread(thread);
+ gDvm.threadList = thread;
+
+#ifdef COUNT_PRECISE_METHODS
+ gDvm.preciseMethods = dvmPointerSetAlloc(200);
+#endif
+
+ return true;
+}
+
+/*
+ * We're a little farther up now, and can load some basic classes.
+ *
+ * We're far enough along that we can poke at java.lang.Thread and friends,
+ * but should not assume that static initializers have run (or cause them
+ * to do so). That means no object allocations yet.
+ */
+bool dvmThreadObjStartup(void)
+{
+ /*
+ * Cache the locations of these classes. It's likely that we're the
+ * first to reference them, so they're being loaded now.
+ */
+ gDvm.classJavaLangThread =
+ dvmFindSystemClassNoInit("Ljava/lang/Thread;");
+ gDvm.classJavaLangVMThread =
+ dvmFindSystemClassNoInit("Ljava/lang/VMThread;");
+ gDvm.classJavaLangThreadGroup =
+ dvmFindSystemClassNoInit("Ljava/lang/ThreadGroup;");
+ if (gDvm.classJavaLangThread == NULL ||
+ gDvm.classJavaLangThreadGroup == NULL ||
+ gDvm.classJavaLangThreadGroup == NULL)
+ {
+ LOGE("Could not find one or more essential thread classes\n");
+ return false;
+ }
+
+ /*
+ * Cache field offsets. This makes things a little faster, at the
+ * expense of hard-coding non-public field names into the VM.
+ */
+ gDvm.offJavaLangThread_vmThread =
+ dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "vmThread", "Ljava/lang/VMThread;");
+ gDvm.offJavaLangThread_group =
+ dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "group", "Ljava/lang/ThreadGroup;");
+ gDvm.offJavaLangThread_daemon =
+ dvmFindFieldOffset(gDvm.classJavaLangThread, "daemon", "Z");
+ gDvm.offJavaLangThread_name =
+ dvmFindFieldOffset(gDvm.classJavaLangThread,
+ "name", "Ljava/lang/String;");
+ gDvm.offJavaLangThread_priority =
+ dvmFindFieldOffset(gDvm.classJavaLangThread, "priority", "I");
+
+ if (gDvm.offJavaLangThread_vmThread < 0 ||
+ gDvm.offJavaLangThread_group < 0 ||
+ gDvm.offJavaLangThread_daemon < 0 ||
+ gDvm.offJavaLangThread_name < 0 ||
+ gDvm.offJavaLangThread_priority < 0)
+ {
+ LOGE("Unable to find all fields in java.lang.Thread\n");
+ return false;
+ }
+
+ gDvm.offJavaLangVMThread_thread =
+ dvmFindFieldOffset(gDvm.classJavaLangVMThread,
+ "thread", "Ljava/lang/Thread;");
+ gDvm.offJavaLangVMThread_vmData =
+ dvmFindFieldOffset(gDvm.classJavaLangVMThread, "vmData", "I");
+ if (gDvm.offJavaLangVMThread_thread < 0 ||
+ gDvm.offJavaLangVMThread_vmData < 0)
+ {
+ LOGE("Unable to find all fields in java.lang.VMThread\n");
+ return false;
+ }
+
+ /*
+ * Cache the vtable offset for "run()".
+ *
+ * We don't want to keep the Method* because then we won't find see
+ * methods defined in subclasses.
+ */
+ Method* meth;
+ meth = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangThread, "run", "()V");
+ if (meth == NULL) {
+ LOGE("Unable to find run() in java.lang.Thread\n");
+ return false;
+ }
+ gDvm.voffJavaLangThread_run = meth->methodIndex;
+
+ /*
+ * Cache vtable offsets for ThreadGroup methods.
+ */
+ meth = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangThreadGroup,
+ "removeThread", "(Ljava/lang/Thread;)V");
+ if (meth == NULL) {
+ LOGE("Unable to find removeThread(Thread) in java.lang.ThreadGroup\n");
+ return false;
+ }
+ gDvm.voffJavaLangThreadGroup_removeThread = meth->methodIndex;
+
+ return true;
+}
+
+/*
+ * All threads should be stopped by now. Clean up some thread globals.
+ */
+void dvmThreadShutdown(void)
+{
+ if (gDvm.threadList != NULL) {
+ assert(gDvm.threadList->next == NULL);
+ assert(gDvm.threadList->prev == NULL);
+ freeThread(gDvm.threadList);
+ gDvm.threadList = NULL;
+ }
+
+ dvmFreeBitVector(gDvm.threadIdMap);
+
+ dvmFreeMonitorList();
+
+ pthread_key_delete(gDvm.pthreadKeySelf);
+}
+
+
+/*
+ * Grab the suspend count global lock.
+ */
+static inline void lockThreadSuspendCount(void)
+{
+ /*
+ * Don't try to change to VMWAIT here. When we change back to RUNNING
+ * we have to check for a pending suspend, which results in grabbing
+ * this lock recursively. Doesn't work with "fast" pthread mutexes.
+ *
+ * This lock is always held for very brief periods, so as long as
+ * mutex ordering is respected we shouldn't stall.
+ */
+ int cc = pthread_mutex_lock(&gDvm.threadSuspendCountLock);
+ assert(cc == 0);
+}
+
+/*
+ * Release the suspend count global lock.
+ */
+static inline void unlockThreadSuspendCount(void)
+{
+ dvmUnlockMutex(&gDvm.threadSuspendCountLock);
+}
+
+/*
+ * Grab the thread list global lock.
+ *
+ * This is held while "suspend all" is trying to make everybody stop. If
+ * the shutdown is in progress, and somebody tries to grab the lock, they'll
+ * have to wait for the GC to finish. Therefore it's important that the
+ * thread not be in RUNNING mode.
+ *
+ * We don't have to check to see if we should be suspended once we have
+ * the lock. Nobody can suspend all threads without holding the thread list
+ * lock while they do it, so by definition there isn't a GC in progress.
+ */
+void dvmLockThreadList(Thread* self)
+{
+ ThreadStatus oldStatus;
+
+ if (self == NULL) /* try to get it from TLS */
+ self = dvmThreadSelf();
+
+ if (self != NULL) {
+ oldStatus = self->status;
+ self->status = THREAD_VMWAIT;
+ } else {
+ /* happens for JNI AttachCurrentThread [not anymore?] */
+ //LOGW("NULL self in dvmLockThreadList\n");
+ oldStatus = -1; // shut up gcc
+ }
+
+ int cc = pthread_mutex_lock(&gDvm.threadListLock);
+ assert(cc == 0);
+
+ if (self != NULL)
+ self->status = oldStatus;
+}
+
+/*
+ * Release the thread list global lock.
+ */
+void dvmUnlockThreadList(void)
+{
+ int cc = pthread_mutex_unlock(&gDvm.threadListLock);
+ assert(cc == 0);
+}
+
+
+/*
+ * Grab the "thread suspend" lock. This is required to prevent the
+ * GC and the debugger from simultaneously suspending all threads.
+ *
+ * If we fail to get the lock, somebody else is trying to suspend all
+ * threads -- including us. If we go to sleep on the lock we'll deadlock
+ * the VM. Loop until we get it or somebody puts us to sleep.
+ */
+static void lockThreadSuspend(const char* who, SuspendCause why)
+{
+ const int kMaxRetries = 10;
+ const int kSpinSleepTime = 3*1000*1000; /* 3s */
+ u8 startWhen = 0; // init req'd to placate gcc
+ int sleepIter = 0;
+ int cc;
+
+ do {
+ cc = pthread_mutex_trylock(&gDvm._threadSuspendLock);
+ if (cc != 0) {
+ if (!dvmCheckSuspendPending(NULL)) {
+ /*
+ * Could be unusual JNI-attach thing, could be we hit
+ * the window as the suspend or resume was started. Could
+ * also be the debugger telling us to resume at roughly
+ * the same time we're posting an event.
+ */
+ LOGI("threadid=%d ODD: thread-suspend lock held (%s:%d)"
+ " but suspend not pending\n",
+ dvmThreadSelf()->threadId, who, why);
+ }
+
+ /* give the lock-holder a chance to do some work */
+ if (sleepIter == 0)
+ startWhen = dvmGetRelativeTimeUsec();
+ if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) {
+ LOGE("threadid=%d: couldn't get thread-suspend lock (%s:%d),"
+ " bailing\n",
+ dvmThreadSelf()->threadId, who, why);
+ dvmDumpAllThreads(false);
+ dvmAbort();
+ }
+ }
+ } while (cc != 0);
+ assert(cc == 0);
+}
+
+/*
+ * Release the "thread suspend" lock.
+ */
+static inline void unlockThreadSuspend(void)
+{
+ int cc = pthread_mutex_unlock(&gDvm._threadSuspendLock);
+ assert(cc == 0);
+}
+
+
+/*
+ * Kill any daemon threads that still exist. All of ours should be
+ * stopped, so these should be Thread objects or JNI-attached threads
+ * started by the application. Actively-running threads are likely
+ * to crash the process if they continue to execute while the VM
+ * shuts down, so we really need to kill or suspend them. (If we want
+ * the VM to restart within this process, we need to kill them, but that
+ * leaves open the possibility of orphaned resources.)
+ *
+ * Waiting for the thread to suspend may be unwise at this point, but
+ * if one of these is wedged in a critical section then we probably
+ * would've locked up on the last GC attempt.
+ *
+ * It's possible for this function to get called after a failed
+ * initialization, so be careful with assumptions about the environment.
+ */
+void dvmSlayDaemons(void)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* target;
+ Thread* nextTarget;
+
+ if (self == NULL)
+ return;
+
+ //dvmEnterCritical(self);
+ dvmLockThreadList(self);
+
+ target = gDvm.threadList;
+ while (target != NULL) {
+ if (target == self) {
+ target = target->next;
+ continue;
+ }
+
+ if (!dvmGetFieldBoolean(target->threadObj,
+ gDvm.offJavaLangThread_daemon))
+ {
+ LOGW("threadid=%d: non-daemon id=%d still running at shutdown?!\n",
+ self->threadId, target->threadId);
+ target = target->next;
+ continue;
+ }
+
+ LOGI("threadid=%d: killing leftover daemon threadid=%d [TODO]\n",
+ self->threadId, target->threadId);
+ // TODO: suspend and/or kill the thread
+ // (at the very least, we can "rescind their JNI privileges")
+
+ /* remove from list */
+ nextTarget = target->next;
+ unlinkThread(target);
+
+ freeThread(target);
+ target = nextTarget;
+ }
+
+ dvmUnlockThreadList();
+ //dvmExitCritical(self);
+}
+
+
+/*
+ * Finish preparing the parts of the Thread struct required to support
+ * JNI registration.
+ */
+bool dvmPrepMainForJni(JNIEnv* pEnv)
+{
+ Thread* self;
+
+ /* main thread is always first in list at this point */
+ self = gDvm.threadList;
+ assert(self->threadId == kMainThreadId);
+
+ /* create a "fake" JNI frame at the top of the main thread interp stack */
+ if (!createFakeEntryFrame(self))
+ return false;
+
+ /* fill these in, since they weren't ready at dvmCreateJNIEnv time */
+ dvmSetJniEnvThreadId(pEnv, self);
+ dvmSetThreadJNIEnv(self, (JNIEnv*) pEnv);
+
+ return true;
+}
+
+
+/*
+ * Finish preparing the main thread, allocating some objects to represent
+ * it. As part of doing so, we finish initializing Thread and ThreadGroup.
+ */
+bool dvmPrepMainThread(void)
+{
+ Thread* thread;
+ Object* groupObj;
+ Object* threadObj;
+ Object* vmThreadObj;
+ StringObject* threadNameStr;
+ Method* init;
+ JValue unused;
+
+ LOGV("+++ finishing prep on main VM thread\n");
+
+ /* main thread is always first in list at this point */
+ thread = gDvm.threadList;
+ assert(thread->threadId == kMainThreadId);
+
+ /*
+ * Make sure the classes are initialized. We have to do this before
+ * we create an instance of them.
+ */
+ if (!dvmInitClass(gDvm.classJavaLangClass)) {
+ LOGE("'Class' class failed to initialize\n");
+ return false;
+ }
+ if (!dvmInitClass(gDvm.classJavaLangThreadGroup) ||
+ !dvmInitClass(gDvm.classJavaLangThread) ||
+ !dvmInitClass(gDvm.classJavaLangVMThread))
+ {
+ LOGE("thread classes failed to initialize\n");
+ return false;
+ }
+
+ groupObj = dvmGetMainThreadGroup();
+ if (groupObj == NULL)
+ return false;
+
+ /*
+ * Allocate and construct a Thread with the internal-creation
+ * constructor.
+ */
+ threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
+ if (threadObj == NULL) {
+ LOGE("unable to allocate main thread object\n");
+ return false;
+ }
+ dvmReleaseTrackedAlloc(threadObj, NULL);
+
+ threadNameStr = dvmCreateStringFromCstr("main", ALLOC_DEFAULT);
+ if (threadNameStr == NULL)
+ return false;
+ dvmReleaseTrackedAlloc((Object*)threadNameStr, NULL);
+
+ init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+ "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+ assert(init != NULL);
+ dvmCallMethod(thread, init, threadObj, &unused, groupObj, threadNameStr,
+ THREAD_NORM_PRIORITY, false);
+ if (dvmCheckException(thread)) {
+ LOGE("exception thrown while constructing main thread object\n");
+ return false;
+ }
+
+ /*
+ * Allocate and construct a VMThread.
+ */
+ vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+ if (vmThreadObj == NULL) {
+ LOGE("unable to allocate main vmthread object\n");
+ return false;
+ }
+ dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+
+ init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangVMThread, "<init>",
+ "(Ljava/lang/Thread;)V");
+ dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj);
+ if (dvmCheckException(thread)) {
+ LOGE("exception thrown while constructing main vmthread object\n");
+ return false;
+ }
+
+ /* set the VMThread.vmData field to our Thread struct */
+ assert(gDvm.offJavaLangVMThread_vmData != 0);
+ dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)thread);
+
+ /*
+ * Stuff the VMThread back into the Thread. From this point on, other
+ * Threads will see that this Thread is running.
+ */
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread,
+ vmThreadObj);
+
+ thread->threadObj = threadObj;
+
+ /*
+ * Finish our thread prep.
+ */
+
+ /* include self in non-daemon threads (mainly for AttachCurrentThread) */
+ gDvm.nonDaemonThreadCount++;
+
+ return true;
+}
+
+
+/*
+ * Alloc and initialize a Thread struct.
+ *
+ * "threadObj" is the java.lang.Thread object. It will be NULL for the
+ * main VM thread, but non-NULL for everything else.
+ *
+ * Does not create any objects, just stuff on the system (malloc) heap. (If
+ * this changes, we need to use ALLOC_NO_GC. And also verify that we're
+ * ready to load classes at the time this is called.)
+ */
+static Thread* allocThread(int interpStackSize)
+{
+ Thread* thread;
+ u1* stackBottom;
+
+ thread = (Thread*) calloc(1, sizeof(Thread));
+ if (thread == NULL)
+ return NULL;
+
+ assert(interpStackSize >= kMinStackSize && interpStackSize <=kMaxStackSize);
+
+ thread->status = THREAD_INITIALIZING;
+ thread->suspendCount = 0;
+
+#ifdef WITH_ALLOC_LIMITS
+ thread->allocLimit = -1;
+#endif
+
+ /*
+ * Allocate and initialize the interpreted code stack. We essentially
+ * "lose" the alloc pointer, which points at the bottom of the stack,
+ * but we can get it back later because we know how big the stack is.
+ *
+ * The stack must be aligned on a 4-byte boundary.
+ */
+#ifdef MALLOC_INTERP_STACK
+ stackBottom = (u1*) malloc(interpStackSize);
+ if (stackBottom == NULL) {
+ free(thread);
+ return NULL;
+ }
+ memset(stackBottom, 0xc5, interpStackSize); // stop valgrind complaints
+#else
+ stackBottom = mmap(NULL, interpStackSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (stackBottom == MAP_FAILED) {
+ free(thread);
+ return NULL;
+ }
+#endif
+
+ assert(((u4)stackBottom & 0x03) == 0); // looks like our malloc ensures this
+ thread->interpStackSize = interpStackSize;
+ thread->interpStackStart = stackBottom + interpStackSize;
+ thread->interpStackEnd = stackBottom + STACK_OVERFLOW_RESERVE;
+
+ /* give the thread code a chance to set things up */
+ dvmInitInterpStack(thread, interpStackSize);
+
+ return thread;
+}
+
+/*
+ * Get a meaningful thread ID. At present this only has meaning under Linux,
+ * where getpid() and gettid() sometimes agree and sometimes don't depending
+ * on your thread model (try "export LD_ASSUME_KERNEL=2.4.19").
+ */
+pid_t dvmGetSysThreadId(void)
+{
+#ifdef HAVE_GETTID
+ return gettid();
+#else
+ return getpid();
+#endif
+}
+
+/*
+ * Finish initialization of a Thread struct.
+ *
+ * This must be called while executing in the new thread, but before the
+ * thread is added to the thread list.
+ *
+ * *** NOTE: The threadListLock must be held by the caller (needed for
+ * assignThreadId()).
+ */
+static bool prepareThread(Thread* thread)
+{
+ assignThreadId(thread);
+ thread->handle = pthread_self();
+ thread->systemTid = dvmGetSysThreadId();
+
+ //LOGI("SYSTEM TID IS %d (pid is %d)\n", (int) thread->systemTid,
+ // (int) getpid());
+ setThreadSelf(thread);
+
+ LOGV("threadid=%d: interp stack at %p\n",
+ thread->threadId, thread->interpStackStart - thread->interpStackSize);
+
+ /*
+ * Initialize invokeReq.
+ */
+ pthread_mutex_init(&thread->invokeReq.lock, NULL);
+ pthread_cond_init(&thread->invokeReq.cv, NULL);
+
+ /*
+ * Initialize our reference tracking tables.
+ *
+ * The JNI local ref table *must* be fixed-size because we keep pointers
+ * into the table in our stack frames.
+ *
+ * Most threads won't use jniMonitorRefTable, so we clear out the
+ * structure but don't call the init function (which allocs storage).
+ */
+ if (!dvmInitReferenceTable(&thread->jniLocalRefTable,
+ kJniLocalRefMax, kJniLocalRefMax))
+ return false;
+ if (!dvmInitReferenceTable(&thread->internalLocalRefTable,
+ kInternalRefDefault, kInternalRefMax))
+ return false;
+
+ memset(&thread->jniMonitorRefTable, 0, sizeof(thread->jniMonitorRefTable));
+
+ return true;
+}
+
+/*
+ * Remove a thread from the internal list.
+ * Clear out the links to make it obvious that the thread is
+ * no longer on the list. Caller must hold gDvm.threadListLock.
+ */
+static void unlinkThread(Thread* thread)
+{
+ LOG_THREAD("threadid=%d: removing from list\n", thread->threadId);
+ if (thread == gDvm.threadList) {
+ assert(thread->prev == NULL);
+ gDvm.threadList = thread->next;
+ } else {
+ assert(thread->prev != NULL);
+ thread->prev->next = thread->next;
+ }
+ if (thread->next != NULL)
+ thread->next->prev = thread->prev;
+ thread->prev = thread->next = NULL;
+}
+
+/*
+ * Free a Thread struct, and all the stuff allocated within.
+ */
+static void freeThread(Thread* thread)
+{
+ if (thread == NULL)
+ return;
+
+ /* thread->threadId is zero at this point */
+ LOGVV("threadid=%d: freeing\n", thread->threadId);
+
+ if (thread->interpStackStart != NULL) {
+ u1* interpStackBottom;
+
+ interpStackBottom = thread->interpStackStart;
+ interpStackBottom -= thread->interpStackSize;
+#ifdef MALLOC_INTERP_STACK
+ free(interpStackBottom);
+#else
+ if (munmap(interpStackBottom, thread->interpStackSize) != 0)
+ LOGW("munmap(thread stack) failed\n");
+#endif
+ }
+
+ dvmClearReferenceTable(&thread->jniLocalRefTable);
+ dvmClearReferenceTable(&thread->internalLocalRefTable);
+ if (&thread->jniMonitorRefTable.table != NULL)
+ dvmClearReferenceTable(&thread->jniMonitorRefTable);
+
+ free(thread);
+}
+
+/*
+ * Like pthread_self(), but on a Thread*.
+ */
+Thread* dvmThreadSelf(void)
+{
+ return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);
+}
+
+/*
+ * Explore our sense of self. Stuffs the thread pointer into TLS.
+ */
+static void setThreadSelf(Thread* thread)
+{
+ int cc;
+
+ cc = pthread_setspecific(gDvm.pthreadKeySelf, thread);
+ if (cc != 0) {
+ /*
+ * Sometimes this fails under Bionic with EINVAL during shutdown.
+ * This can happen if the timing is just right, e.g. a thread
+ * fails to attach during shutdown, but the "fail" path calls
+ * here to ensure we clean up after ourselves.
+ */
+ if (thread != NULL) {
+ LOGE("pthread_setspecific(%p) failed, err=%d\n", thread, cc);
+ dvmAbort(); /* the world is fundamentally hosed */
+ }
+ }
+}
+
+/*
+ * This is associated with the pthreadKeySelf key. It's called by the
+ * pthread library when a thread is exiting and the "self" pointer in TLS
+ * is non-NULL, meaning the VM hasn't had a chance to clean up. In normal
+ * operation this should never be called.
+ *
+ * This is mainly of use to ensure that we don't leak resources if, for
+ * example, a thread attaches itself to us with AttachCurrentThread and
+ * then exits without notifying the VM.
+ */
+static void threadExitCheck(void* arg)
+{
+ Thread* thread = (Thread*) arg;
+
+ LOGI("In threadExitCheck %p\n", arg);
+ assert(thread != NULL);
+
+ if (thread->status != THREAD_ZOMBIE) {
+ /* TODO: instead of failing, we could call dvmDetachCurrentThread() */
+ LOGE("Native thread exited without telling us\n");
+ dvmAbort();
+ }
+}
+
+
+/*
+ * Assign the threadId. This needs to be a small integer so that our
+ * "thin" locks fit in a small number of bits.
+ *
+ * We reserve zero for use as an invalid ID.
+ *
+ * This must be called with threadListLock held (unless we're still
+ * initializing the system).
+ */
+static void assignThreadId(Thread* thread)
+{
+ /* Find a small unique integer. threadIdMap is a vector of
+ * kMaxThreadId bits; dvmAllocBit() returns the index of a
+ * bit, meaning that it will always be < kMaxThreadId.
+ *
+ * The thin locking magic requires that the low bit is always
+ * set, so we do it once, here.
+ */
+ int num = dvmAllocBit(gDvm.threadIdMap);
+ if (num < 0) {
+ LOGE("Ran out of thread IDs\n");
+ dvmAbort(); // TODO: make this a non-fatal error result
+ }
+
+ thread->threadId = ((num + 1) << 1) | 1;
+
+ assert(thread->threadId != 0);
+ assert(thread->threadId != DVM_LOCK_INITIAL_THIN_VALUE);
+}
+
+/*
+ * Give back the thread ID.
+ */
+static void releaseThreadId(Thread* thread)
+{
+ assert(thread->threadId > 0);
+ dvmClearBit(gDvm.threadIdMap, (thread->threadId >> 1) - 1);
+ thread->threadId = 0;
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native code in the main
+ * thread was originally invoked from interpreted code. This gives us a
+ * place to hang JNI local references. The VM spec says (v2 5.2) that the
+ * VM begins by executing "main" in a class, so in a way this brings us
+ * closer to the spec.
+ */
+static bool createFakeEntryFrame(Thread* thread)
+{
+ assert(thread->threadId == kMainThreadId); // main thread only
+
+ /* find the method on first use */
+ if (gDvm.methFakeNativeEntry == NULL) {
+ ClassObject* nativeStart;
+ Method* mainMeth;
+
+ nativeStart = dvmFindSystemClassNoInit(
+ "Ldalvik/system/NativeStart;");
+ if (nativeStart == NULL) {
+ LOGE("Unable to find dalvik.system.NativeStart class\n");
+ return false;
+ }
+
+ /*
+ * Because we are creating a frame that represents application code, we
+ * want to stuff the application class loader into the method's class
+ * loader field, even though we're using the system class loader to
+ * load it. This makes life easier over in JNI FindClass (though it
+ * could bite us in other ways).
+ *
+ * Unfortunately this is occurring too early in the initialization,
+ * of necessity coming before JNI is initialized, and we're not quite
+ * ready to set up the application class loader.
+ *
+ * So we save a pointer to the method in gDvm.methFakeNativeEntry
+ * and check it in FindClass. The method is private so nobody else
+ * can call it.
+ */
+ //nativeStart->classLoader = dvmGetSystemClassLoader();
+
+ mainMeth = dvmFindDirectMethodByDescriptor(nativeStart,
+ "main", "([Ljava/lang/String;)V");
+ if (mainMeth == NULL) {
+ LOGE("Unable to find 'main' in dalvik.system.NativeStart\n");
+ return false;
+ }
+
+ gDvm.methFakeNativeEntry = mainMeth;
+ }
+
+ return dvmPushJNIFrame(thread, gDvm.methFakeNativeEntry);
+}
+
+
+/*
+ * Add a stack frame that makes it look like the native thread has been
+ * executing interpreted code. This gives us a place to hang JNI local
+ * references.
+ */
+static bool createFakeRunFrame(Thread* thread)
+{
+ ClassObject* nativeStart;
+ Method* runMeth;
+
+ assert(thread->threadId != 1); // not for main thread
+
+ nativeStart =
+ dvmFindSystemClassNoInit("Ldalvik/system/NativeStart;");
+ if (nativeStart == NULL) {
+ LOGE("Unable to find dalvik.system.NativeStart class\n");
+ return false;
+ }
+
+ runMeth = dvmFindVirtualMethodByDescriptor(nativeStart, "run", "()V");
+ if (runMeth == NULL) {
+ LOGE("Unable to find 'run' in dalvik.system.NativeStart\n");
+ return false;
+ }
+
+ return dvmPushJNIFrame(thread, runMeth);
+}
+
+/*
+ * Helper function to set the name of the current thread
+ */
+static void setThreadName(const char *threadName)
+{
+#if defined(HAVE_PRCTL)
+ int hasAt = 0;
+ int hasDot = 0;
+ const char *s = threadName;
+ while (*s) {
+ if (*s == '.') hasDot = 1;
+ else if (*s == '@') hasAt = 1;
+ s++;
+ }
+ int len = s - threadName;
+ if (len < 15 || hasAt || !hasDot) {
+ s = threadName;
+ } else {
+ s = threadName + len - 15;
+ }
+ prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#endif
+}
+
+/*
+ * Create a thread as a result of java.lang.Thread.start().
+ *
+ * We do have to worry about some concurrency problems, e.g. programs
+ * that try to call Thread.start() on the same object from multiple threads.
+ * (This will fail for all but one, but we have to make sure that it succeeds
+ * for exactly one.)
+ *
+ * Some of the complexity here arises from our desire to mimic the
+ * Thread vs. VMThread class decomposition we inherited. We've been given
+ * a Thread, and now we need to create a VMThread and then populate both
+ * objects. We also need to create one of our internal Thread objects.
+ *
+ * Pass in a stack size of 0 to get the default.
+ */
+bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
+{
+ pthread_attr_t threadAttr;
+ pthread_t threadHandle;
+ Thread* self;
+ Thread* newThread = NULL;
+ Object* vmThreadObj = NULL;
+ int stackSize;
+
+ assert(threadObj != NULL);
+
+ if(gDvm.zygote) {
+ dvmThrowException("Ljava/lang/IllegalStateException;",
+ "No new threads in -Xzygote mode");
+
+ goto fail;
+ }
+
+ self = dvmThreadSelf();
+ if (reqStackSize == 0)
+ stackSize = gDvm.stackSize;
+ else if (reqStackSize < kMinStackSize)
+ stackSize = kMinStackSize;
+ else if (reqStackSize > kMaxStackSize)
+ stackSize = kMaxStackSize;
+ else
+ stackSize = reqStackSize;
+
+ pthread_attr_init(&threadAttr);
+ pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+
+ /*
+ * To minimize the time spent in the critical section, we allocate the
+ * vmThread object here.
+ */
+ vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
+ if (vmThreadObj == NULL)
+ goto fail;
+
+ newThread = allocThread(stackSize);
+ if (newThread == NULL)
+ goto fail;
+ newThread->threadObj = threadObj;
+
+ assert(newThread->status == THREAD_INITIALIZING);
+
+ /*
+ * We need to lock out other threads while we test and set the
+ * "vmThread" field in java.lang.Thread, because we use that to determine
+ * if this thread has been started before. We use the thread list lock
+ * because it's handy and we're going to need to grab it again soon
+ * anyway.
+ */
+ dvmLockThreadList(self);
+
+ if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+ dvmUnlockThreadList();
+ dvmThrowException("Ljava/lang/IllegalThreadStateException;",
+ "thread has already been started");
+ goto fail;
+ }
+
+ /*
+ * There are actually three data structures: Thread (object), VMThread
+ * (object), and Thread (C struct). All of them point to at least one
+ * other.
+ *
+ * As soon as "VMThread.vmData" is assigned, other threads can start
+ * making calls into us (e.g. setPriority).
+ */
+ dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+ /*
+ * Thread creation might take a while, so release the lock.
+ */
+ dvmUnlockThreadList();
+
+ if (pthread_create(&threadHandle, &threadAttr, interpThreadStart,
+ newThread) != 0)
+ {
+ /*
+ * Failure generally indicates that we have exceeded system
+ * resource limits. VirtualMachineError is probably too severe,
+ * so use OutOfMemoryError.
+ */
+ LOGE("Thread creation failed (err=%s)\n", strerror(errno));
+
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+ dvmThrowException("Ljava/lang/OutOfMemoryError;",
+ "thread creation failed");
+ goto fail;
+ }
+
+ /*
+ * We need to wait for the thread to start. Otherwise, depending on
+ * the whims of the OS scheduler, we could return and the code in our
+ * thread could try to do operations on the new thread before it had
+ * finished starting.
+ *
+ * The new thread will lock the thread list, change its state to
+ * THREAD_STARTING, broadcast to gDvm.threadStartCond, and then sleep
+ * on gDvm.threadStartCond (which uses the thread list lock). This
+ * thread (the parent) will either see that the thread is already ready
+ * after we grab the thread list lock, or will be awakened from the
+ * condition variable on the broadcast.
+ *
+ * We don't want to stall the rest of the VM while the new thread
+ * starts, which can happen if the GC wakes up at the wrong moment.
+ * So, we change our own status to VMWAIT, and self-suspend if
+ * necessary after we finish adding the new thread.
+ *
+ *
+ * We have to deal with an odd race with the GC/debugger suspension
+ * mechanism when creating a new thread. The information about whether
+ * or not a thread should be suspended is contained entirely within
+ * the Thread struct; this is usually cleaner to deal with than having
+ * one or more globally-visible suspension flags. The trouble is that
+ * we could create the thread while the VM is trying to suspend all
+ * threads. The suspend-count won't be nonzero for the new thread,
+ * so dvmChangeStatus(THREAD_RUNNING) won't cause a suspension.
+ *
+ * The easiest way to deal with this is to prevent the new thread from
+ * running until the parent says it's okay. This results in the
+ * following sequence of events for a "badly timed" GC:
+ *
+ * - call pthread_create()
+ * - lock thread list
+ * - put self into THREAD_VMWAIT so GC doesn't wait for us
+ * - sleep on condition var (mutex = thread list lock) until child starts
+ * + GC triggered by another thread
+ * + thread list locked; suspend counts updated; thread list unlocked
+ * + loop waiting for all runnable threads to suspend
+ * + success, start GC
+ * o child thread wakes, signals condition var to wake parent
+ * o child waits for parent ack on condition variable
+ * - we wake up, locking thread list
+ * - add child to thread list
+ * - unlock thread list
+ * - change our state back to THREAD_RUNNING; GC causes us to suspend
+ * + GC finishes; all threads in thread list are resumed
+ * - lock thread list
+ * - set child to THREAD_VMWAIT, and signal it to start
+ * - unlock thread list
+ * o child resumes
+ * o child changes state to THREAD_RUNNING
+ *
+ * The above shows the GC starting up during thread creation, but if
+ * it starts anywhere after VMThread.create() is called it will
+ * produce the same series of events.
+ *
+ * Once the child is in the thread list, it will be suspended and
+ * resumed like any other thread. In the above scenario the resume-all
+ * code will try to resume the new thread, which was never actually
+ * suspended, and try to decrement the child's thread suspend count to -1.
+ * We can catch this in the resume-all code.
+ *
+ * Bouncing back and forth between threads like this adds a small amount
+ * of scheduler overhead to thread startup.
+ *
+ * One alternative to having the child wait for the parent would be
+ * to have the child inherit the parents' suspension count. This
+ * would work for a GC, since we can safely assume that the parent
+ * thread didn't cause it, but we must only do so if the parent suspension
+ * was caused by a suspend-all. If the parent was being asked to
+ * suspend singly by the debugger, the child should not inherit the value.
+ *
+ * We could also have a global "new thread suspend count" that gets
+ * picked up by new threads before changing state to THREAD_RUNNING.
+ * This would be protected by the thread list lock and set by a
+ * suspend-all.
+ */
+ dvmLockThreadList(self);
+ assert(self->status == THREAD_RUNNING);
+ self->status = THREAD_VMWAIT;
+ while (newThread->status != THREAD_STARTING)
+ pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+ LOG_THREAD("threadid=%d: adding to list\n", newThread->threadId);
+ newThread->next = gDvm.threadList->next;
+ if (newThread->next != NULL)
+ newThread->next->prev = newThread;
+ newThread->prev = gDvm.threadList;
+ gDvm.threadList->next = newThread;
+
+ if (!dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon))
+ gDvm.nonDaemonThreadCount++; // guarded by thread list lock
+
+ dvmUnlockThreadList();
+
+ /* change status back to RUNNING, self-suspending if necessary */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ /*
+ * Tell the new thread to start.
+ *
+ * We must hold the thread list lock before messing with another thread.
+ * In the general case we would also need to verify that newThread was
+ * still in the thread list, but in our case the thread has not started
+ * executing user code and therefore has not had a chance to exit.
+ *
+ * We move it to VMWAIT, and it then shifts itself to RUNNING, which
+ * comes with a suspend-pending check.
+ */
+ dvmLockThreadList(self);
+
+ assert(newThread->status == THREAD_STARTING);
+ newThread->status = THREAD_VMWAIT;
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+
+ dvmUnlockThreadList();
+
+ dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+ return true;
+
+fail:
+ freeThread(newThread);
+ dvmReleaseTrackedAlloc(vmThreadObj, NULL);
+ return false;
+}
+
+/*
+ * pthread entry function for threads started from interpreted code.
+ */
+static void* interpThreadStart(void* arg)
+{
+ Thread* self = (Thread*) arg;
+
+ char *threadName = dvmGetThreadName(self);
+ setThreadName(threadName);
+ free(threadName);
+
+ /*
+ * Finish initializing the Thread struct.
+ */
+ prepareThread(self);
+
+ LOG_THREAD("threadid=%d: created from interp\n", self->threadId);
+
+ /*
+ * Change our status and wake our parent, who will add us to the
+ * thread list and advance our state to VMWAIT.
+ */
+ dvmLockThreadList(self);
+ self->status = THREAD_STARTING;
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+
+ /*
+ * Wait until the parent says we can go. Assuming there wasn't a
+ * suspend pending, this will happen immediately. When it completes,
+ * we're full-fledged citizens of the VM.
+ *
+ * We have to use THREAD_VMWAIT here rather than THREAD_RUNNING
+ * because the pthread_cond_wait below needs to reacquire a lock that
+ * suspend-all is also interested in. If we get unlucky, the parent could
+ * change us to THREAD_RUNNING, then a GC could start before we get
+ * signaled, and suspend-all will grab the thread list lock and then
+ * wait for us to suspend. We'll be in the tail end of pthread_cond_wait
+ * trying to get the lock.
+ */
+ while (self->status != THREAD_VMWAIT)
+ pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+ dvmUnlockThreadList();
+
+ /*
+ * Add a JNI context.
+ */
+ self->jniEnv = dvmCreateJNIEnv(self);
+
+ /*
+ * Change our state so the GC will wait for us from now on. If a GC is
+ * in progress this call will suspend us.
+ */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ /*
+ * Notify the debugger & DDM. The debugger notification may cause
+ * us to suspend ourselves (and others).
+ */
+ if (gDvm.debuggerConnected)
+ dvmDbgPostThreadStart(self);
+
+ /*
+ * Set the system thread priority according to the Thread object's
+ * priority level. We don't usually need to do this, because both the
+ * Thread object and system thread priorities inherit from parents. The
+ * tricky case is when somebody creates a Thread object, calls
+ * setPriority(), and then starts the thread. We could manage this with
+ * a "needs priority update" flag to avoid the redundant call.
+ */
+ int priority = dvmGetFieldBoolean(self->threadObj,
+ gDvm.offJavaLangThread_priority);
+ dvmChangeThreadPriority(self, priority);
+
+ /*
+ * Execute the "run" method.
+ *
+ * At this point our stack is empty, so somebody who comes looking for
+ * stack traces right now won't have much to look at. This is normal.
+ */
+ Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
+ JValue unused;
+
+ LOGV("threadid=%d: calling run()\n", self->threadId);
+ assert(strcmp(run->name, "run") == 0);
+ dvmCallMethod(self, run, self->threadObj, &unused);
+ LOGV("threadid=%d: exiting\n", self->threadId);
+
+ /*
+ * Remove the thread from various lists, report its death, and free
+ * its resources.
+ */
+ dvmDetachCurrentThread();
+
+ return NULL;
+}
+
+/*
+ * The current thread is exiting with an uncaught exception. The
+ * Java programming language allows the application to provide a
+ * thread-exit-uncaught-exception handler for the VM, for a specific
+ * Thread, and for all threads in a ThreadGroup.
+ *
+ * Version 1.5 added the per-thread handler. We need to call
+ * "uncaughtException" in the handler object, which is either the
+ * ThreadGroup object or the Thread-specific handler.
+ */
+static void threadExitUncaughtException(Thread* self, Object* group)
+{
+ Object* exception;
+ Object* handlerObj;
+ ClassObject* throwable;
+ Method* uncaughtHandler = NULL;
+ InstField* threadHandler;
+
+ LOGW("threadid=%d: thread exiting with uncaught exception (group=%p)\n",
+ self->threadId, group);
+ assert(group != NULL);
+
+ /*
+ * Get a pointer to the exception, then clear out the one in the
+ * thread. We don't want to have it set when executing interpreted code.
+ */
+ exception = dvmGetException(self);
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ /*
+ * Get the Thread's "uncaughtHandler" object. Use it if non-NULL;
+ * else use "group" (which is an instance of UncaughtExceptionHandler).
+ */
+ threadHandler = dvmFindInstanceField(gDvm.classJavaLangThread,
+ "uncaughtHandler", "Ljava/lang/Thread$UncaughtExceptionHandler;");
+ if (threadHandler == NULL) {
+ LOGW("WARNING: no 'uncaughtHandler' field in java/lang/Thread\n");
+ goto bail;
+ }
+ handlerObj = dvmGetFieldObject(self->threadObj, threadHandler->byteOffset);
+ if (handlerObj == NULL)
+ handlerObj = group;
+
+ /*
+ * Find the "uncaughtHandler" field in this object.
+ */
+ uncaughtHandler = dvmFindVirtualMethodHierByDescriptor(handlerObj->clazz,
+ "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+ if (uncaughtHandler != NULL) {
+ //LOGI("+++ calling %s.uncaughtException\n",
+ // handlerObj->clazz->descriptor);
+ JValue unused;
+ dvmCallMethod(self, uncaughtHandler, handlerObj, &unused,
+ self->threadObj, exception);
+ } else {
+ /* restore it and dump a stack trace */
+ LOGW("WARNING: no 'uncaughtException' method in class %s\n",
+ handlerObj->clazz->descriptor);
+ dvmSetException(self, exception);
+ dvmLogExceptionStackTrace();
+ }
+
+bail:
+ dvmReleaseTrackedAlloc(exception, self);
+}
+
+
+/*
+ * Create an internal VM thread, for things like JDWP and finalizers.
+ *
+ * The easiest way to do this is create a new thread and then use the
+ * JNI AttachCurrentThread implementation.
+ *
+ * This does not return until after the new thread has begun executing.
+ */
+bool dvmCreateInternalThread(pthread_t* pHandle, const char* name,
+ InternalThreadStart func, void* funcArg)
+{
+ InternalStartArgs* pArgs;
+ Object* systemGroup;
+ pthread_attr_t threadAttr;
+ volatile Thread* newThread = NULL;
+ volatile int createStatus = 0;
+
+ systemGroup = dvmGetSystemThreadGroup();
+ if (systemGroup == NULL)
+ return false;
+
+ pArgs = (InternalStartArgs*) malloc(sizeof(*pArgs));
+ pArgs->func = func;
+ pArgs->funcArg = funcArg;
+ pArgs->name = strdup(name); // storage will be owned by new thread
+ pArgs->group = systemGroup;
+ pArgs->isDaemon = true;
+ pArgs->pThread = &newThread;
+ pArgs->pCreateStatus = &createStatus;
+
+ pthread_attr_init(&threadAttr);
+ //pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
+
+ if (pthread_create(pHandle, &threadAttr, internalThreadStart,
+ pArgs) != 0)
+ {
+ LOGE("internal thread creation failed\n");
+ free(pArgs->name);
+ free(pArgs);
+ return false;
+ }
+
+ /*
+ * Wait for the child to start. This gives us an opportunity to make
+ * sure that the thread started correctly, and allows our caller to
+ * assume that the thread has started running.
+ *
+ * Because we aren't holding a lock across the thread creation, it's
+ * possible that the child will already have completed its
+ * initialization. Because the child only adjusts "createStatus" while
+ * holding the thread list lock, the initial condition on the "while"
+ * loop will correctly avoid the wait if this occurs.
+ *
+ * It's also possible that we'll have to wait for the thread to finish
+ * being created, and as part of allocating a Thread object it might
+ * need to initiate a GC. We switch to VMWAIT while we pause.
+ */
+ Thread* self = dvmThreadSelf();
+ int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmLockThreadList(self);
+ while (createStatus == 0)
+ pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
+
+ if (newThread == NULL) {
+ LOGW("internal thread create failed (createStatus=%d)\n", createStatus);
+ assert(createStatus < 0);
+ /* don't free pArgs -- if pthread_create succeeded, child owns it */
+ dvmUnlockThreadList();
+ dvmChangeStatus(self, oldStatus);
+ return false;
+ }
+
+ /* thread could be in any state now (except early init states) */
+ //assert(newThread->status == THREAD_RUNNING);
+
+ dvmUnlockThreadList();
+ dvmChangeStatus(self, oldStatus);
+
+ return true;
+}
+
+/*
+ * pthread entry function for internally-created threads.
+ *
+ * We are expected to free "arg" and its contents. If we're a daemon
+ * thread, and we get cancelled abruptly when the VM shuts down, the
+ * storage won't be freed. If this becomes a concern we can make a copy
+ * on the stack.
+ */
+static void* internalThreadStart(void* arg)
+{
+ InternalStartArgs* pArgs = (InternalStartArgs*) arg;
+ JavaVMAttachArgs jniArgs;
+
+ jniArgs.version = JNI_VERSION_1_2;
+ jniArgs.name = pArgs->name;
+ jniArgs.group = pArgs->group;
+
+ setThreadName(pArgs->name);
+
+ /* use local jniArgs as stack top */
+ if (dvmAttachCurrentThread(&jniArgs, pArgs->isDaemon)) {
+ /*
+ * Tell the parent of our success.
+ *
+ * threadListLock is the mutex for threadStartCond.
+ */
+ dvmLockThreadList(dvmThreadSelf());
+ *pArgs->pCreateStatus = 1;
+ *pArgs->pThread = dvmThreadSelf();
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+ dvmUnlockThreadList();
+
+ LOG_THREAD("threadid=%d: internal '%s'\n",
+ dvmThreadSelf()->threadId, pArgs->name);
+
+ /* execute */
+ (*pArgs->func)(pArgs->funcArg);
+
+ /* detach ourselves */
+ dvmDetachCurrentThread();
+ } else {
+ /*
+ * Tell the parent of our failure. We don't have a Thread struct,
+ * so we can't be suspended, so we don't need to enter a critical
+ * section.
+ */
+ dvmLockThreadList(dvmThreadSelf());
+ *pArgs->pCreateStatus = -1;
+ assert(*pArgs->pThread == NULL);
+ pthread_cond_broadcast(&gDvm.threadStartCond);
+ dvmUnlockThreadList();
+
+ assert(*pArgs->pThread == NULL);
+ }
+
+ free(pArgs->name);
+ free(pArgs);
+ return NULL;
+}
+
+/*
+ * Attach the current thread to the VM.
+ *
+ * Used for internally-created threads and JNI's AttachCurrentThread.
+ */
+bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon)
+{
+ Thread* self = NULL;
+ Object* threadObj = NULL;
+ Object* vmThreadObj = NULL;
+ StringObject* threadNameStr = NULL;
+ Method* init;
+ bool ok, ret;
+
+ /* establish a basic sense of self */
+ self = allocThread(gDvm.stackSize);
+ if (self == NULL)
+ goto fail;
+ setThreadSelf(self);
+
+ /*
+ * Create Thread and VMThread objects. We have to use ALLOC_NO_GC
+ * because this thread is not yet visible to the VM. We could also
+ * just grab the GC lock earlier, but that leaves us executing
+ * interpreted code with the lock held, which is not prudent.
+ *
+ * The alloc calls will block if a GC is in progress, so we don't need
+ * to check for global suspension here.
+ *
+ * It's also possible for the allocation calls to *cause* a GC.
+ */
+ //BUG: deadlock if a GC happens here during HeapWorker creation
+ threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_NO_GC);
+ if (threadObj == NULL)
+ goto fail;
+ vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_NO_GC);
+ if (vmThreadObj == NULL)
+ goto fail;
+
+ self->threadObj = threadObj;
+ dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)self);
+
+ /*
+ * Do some java.lang.Thread constructor prep before we lock stuff down.
+ */
+ if (pArgs->name != NULL) {
+ threadNameStr = dvmCreateStringFromCstr(pArgs->name, ALLOC_NO_GC);
+ if (threadNameStr == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto fail;
+ }
+ }
+
+ init = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangThread, "<init>",
+ "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+ if (init == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto fail;
+ }
+
+ /*
+ * Finish our thread prep. We need to do this before invoking any
+ * interpreted code. prepareThread() requires that we hold the thread
+ * list lock.
+ */
+ dvmLockThreadList(self);
+ ok = prepareThread(self);
+ dvmUnlockThreadList();
+ if (!ok)
+ goto fail;
+
+ self->jniEnv = dvmCreateJNIEnv(self);
+ if (self->jniEnv == NULL)
+ goto fail;
+
+ /*
+ * Create a "fake" JNI frame at the top of the main thread interp stack.
+ * It isn't really necessary for the internal threads, but it gives
+ * the debugger something to show. It is essential for the JNI-attached
+ * threads.
+ */
+ if (!createFakeRunFrame(self))
+ goto fail;
+
+ /*
+ * The native side of the thread is ready; add it to the list.
+ */
+ LOG_THREAD("threadid=%d: adding to list (attached)\n", self->threadId);
+
+ /* Start off in VMWAIT, because we may be about to block
+ * on the heap lock, and we don't want any suspensions
+ * to wait for us.
+ */
+ self->status = THREAD_VMWAIT;
+
+ /*
+ * Add ourselves to the thread list. Once we finish here we are
+ * visible to the debugger and the GC.
+ */
+ dvmLockThreadList(self);
+
+ self->next = gDvm.threadList->next;
+ if (self->next != NULL)
+ self->next->prev = self;
+ self->prev = gDvm.threadList;
+ gDvm.threadList->next = self;
+ if (!isDaemon)
+ gDvm.nonDaemonThreadCount++;
+
+ dvmUnlockThreadList();
+
+ /*
+ * It's possible that a GC is currently running. Our thread
+ * wasn't in the list when the GC started, so it's not properly
+ * suspended in that case. Synchronize on the heap lock (held
+ * when a GC is happening) to guarantee that any GCs from here
+ * on will see this thread in the list.
+ */
+ dvmLockMutex(&gDvm.gcHeapLock);
+ dvmUnlockMutex(&gDvm.gcHeapLock);
+
+ /*
+ * Switch to the running state now that we're ready for
+ * suspensions. This call may suspend.
+ */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ /*
+ * Now we're ready to run some interpreted code.
+ *
+ * We need to construct the Thread object and set the VMThread field.
+ * Setting VMThread tells interpreted code that we're alive.
+ *
+ * Call the (group, name, priority, daemon) constructor on the Thread.
+ * This sets the thread's name and adds it to the specified group, and
+ * provides values for priority and daemon (which are normally inherited
+ * from the current thread).
+ */
+ JValue unused;
+ dvmCallMethod(self, init, threadObj, &unused, (Object*)pArgs->group,
+ threadNameStr, getThreadPriorityFromSystem(), isDaemon);
+ if (dvmCheckException(self)) {
+ LOGE("exception thrown while constructing attached thread object\n");
+ goto fail_unlink;
+ }
+ //if (isDaemon)
+ // dvmSetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon, true);
+
+ /*
+ * Set the VMThread field, which tells interpreted code that we're alive.
+ *
+ * The risk of a thread start collision here is very low; somebody
+ * would have to be deliberately polling the ThreadGroup list and
+ * trying to start threads against anything it sees, which would
+ * generally cause problems for all thread creation. However, for
+ * correctness we test "vmThread" before setting it.
+ */
+ if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
+ dvmThrowException("Ljava/lang/IllegalThreadStateException;",
+ "thread has already been started");
+ /* We don't want to free anything associated with the thread
+ * because someone is obviously interested in it. Just let
+ * it go and hope it will clean itself up when its finished.
+ * This case should never happen anyway.
+ *
+ * Since we're letting it live, we need to finish setting it up.
+ * We just have to let the caller know that the intended operation
+ * has failed.
+ *
+ * [ This seems strange -- stepping on the vmThread object that's
+ * already present seems like a bad idea. TODO: figure this out. ]
+ */
+ ret = false;
+ } else
+ ret = true;
+ dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
+
+ /* These are now reachable from the thread groups. */
+ dvmClearAllocFlags(threadObj, ALLOC_NO_GC);
+ dvmClearAllocFlags(vmThreadObj, ALLOC_NO_GC);
+
+ /*
+ * The thread is ready to go; let the debugger see it.
+ */
+ self->threadObj = threadObj;
+
+ LOG_THREAD("threadid=%d: attached from native, name=%s\n",
+ self->threadId, pArgs->name);
+
+ /* tell the debugger & DDM */
+ if (gDvm.debuggerConnected)
+ dvmDbgPostThreadStart(self);
+
+ return ret;
+
+fail_unlink:
+ dvmLockThreadList(self);
+ unlinkThread(self);
+ if (!isDaemon)
+ gDvm.nonDaemonThreadCount--;
+ dvmUnlockThreadList();
+ /* fall through to "fail" */
+fail:
+ dvmClearAllocFlags(threadObj, ALLOC_NO_GC);
+ dvmClearAllocFlags(vmThreadObj, ALLOC_NO_GC);
+ if (self != NULL) {
+ if (self->jniEnv != NULL) {
+ dvmDestroyJNIEnv(self->jniEnv);
+ self->jniEnv = NULL;
+ }
+ freeThread(self);
+ }
+ setThreadSelf(NULL);
+ return false;
+}
+
+/*
+ * Detach the thread from the various data structures, notify other threads
+ * that are waiting to "join" it, and free up all heap-allocated storage.
+ *
+ * Used for all threads.
+ *
+ * When we get here the interpreted stack should be empty. The JNI 1.6 spec
+ * requires us to enforce this for the DetachCurrentThread call, probably
+ * because it also says that DetachCurrentThread causes all monitors
+ * associated with the thread to be released. (Because the stack is empty,
+ * we only have to worry about explicit JNI calls to MonitorEnter.)
+ *
+ * THOUGHT:
+ * We might want to avoid freeing our internal Thread structure until the
+ * associated Thread/VMThread objects get GCed. Our Thread is impossible to
+ * get to once the thread shuts down, but there is a small possibility of
+ * an operation starting in another thread before this thread halts, and
+ * finishing much later (perhaps the thread got stalled by a weird OS bug).
+ * We don't want something like Thread.isInterrupted() crawling through
+ * freed storage. Can do with a Thread finalizer, or by creating a
+ * dedicated ThreadObject class for java/lang/Thread and moving all of our
+ * state into that.
+ */
+void dvmDetachCurrentThread(void)
+{
+ Thread* self = dvmThreadSelf();
+ Object* vmThread;
+ Object* group;
+
+ /*
+ * Make sure we're not detaching a thread that's still running. (This
+ * could happen with an explicit JNI detach call.)
+ *
+ * A thread created by interpreted code will finish with a depth of
+ * zero, while a JNI-attached thread will have the synthetic "stack
+ * starter" native method at the top.
+ */
+ int curDepth = dvmComputeExactFrameDepth(self->curFrame);
+ if (curDepth != 0) {
+ bool topIsNative = false;
+
+ if (curDepth == 1) {
+ /* not expecting a lingering break frame; just look at curFrame */
+ assert(!dvmIsBreakFrame(self->curFrame));
+ StackSaveArea* ssa = SAVEAREA_FROM_FP(self->curFrame);
+ if (dvmIsNativeMethod(ssa->method))
+ topIsNative = true;
+ }
+
+ if (!topIsNative) {
+ LOGE("ERROR: detaching thread with interp frames (count=%d)\n",
+ curDepth);
+ dvmDumpThread(self, false);
+ dvmAbort();
+ }
+ }
+
+ group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group);
+ LOG_THREAD("threadid=%d: detach (group=%p)\n", self->threadId, group);
+
+ /*
+ * Release any held monitors. Since there are no interpreted stack
+ * frames, the only thing left are the monitors held by JNI MonitorEnter
+ * calls.
+ */
+ dvmReleaseJniMonitors(self);
+
+ /*
+ * Do some thread-exit uncaught exception processing if necessary.
+ */
+ if (dvmCheckException(self))
+ threadExitUncaughtException(self, group);
+
+ /*
+ * Remove the thread from the thread group.
+ */
+ if (group != NULL) {
+ Method* removeThread =
+ group->clazz->vtable[gDvm.voffJavaLangThreadGroup_removeThread];
+ JValue unused;
+ dvmCallMethod(self, removeThread, group, &unused, self->threadObj);
+ }
+
+ /*
+ * Clear the vmThread reference in the Thread object. Interpreted code
+ * will now see that this Thread is not running. As this may be the
+ * only reference to the VMThread object that the VM knows about, we
+ * have to create an internal reference to it first.
+ */
+ vmThread = dvmGetFieldObject(self->threadObj,
+ gDvm.offJavaLangThread_vmThread);
+ dvmAddTrackedAlloc(vmThread, self);
+ dvmSetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread, NULL);
+
+ /* clear out our struct Thread pointer, since it's going away */
+ dvmSetFieldObject(vmThread, gDvm.offJavaLangVMThread_vmData, NULL);
+
+ /*
+ * Tell the debugger & DDM. This may cause the current thread or all
+ * threads to suspend.
+ *
+ * The JDWP spec is somewhat vague about when this happens, other than
+ * that it's issued by the dying thread, which may still appear in
+ * an "all threads" listing.
+ */
+ if (gDvm.debuggerConnected)
+ dvmDbgPostThreadDeath(self);
+
+ /*
+ * Thread.join() is implemented as an Object.wait() on the VMThread
+ * object. Signal anyone who is waiting.
+ */
+ dvmLockObject(self, vmThread);
+ dvmObjectNotifyAll(self, vmThread);
+ dvmUnlockObject(self, vmThread);
+
+ dvmReleaseTrackedAlloc(vmThread, self);
+ vmThread = NULL;
+
+ /*
+ * We're done manipulating objects, so it's okay if the GC runs in
+ * parallel with us from here out. It's important to do this if
+ * profiling is enabled, since we can wait indefinitely.
+ */
+ self->status = THREAD_VMWAIT;
+
+#ifdef WITH_PROFILER
+ /*
+ * If we're doing method trace profiling, we don't want threads to exit,
+ * because if they do we'll end up reusing thread IDs. This complicates
+ * analysis and makes it impossible to have reasonable output in the
+ * "threads" section of the "key" file.
+ *
+ * We need to do this after Thread.join() completes, or other threads
+ * could get wedged. Since self->threadObj is still valid, the Thread
+ * object will not get GCed even though we're no longer in the ThreadGroup
+ * list (which is important since the profiling thread needs to get
+ * the thread's name).
+ */
+ MethodTraceState* traceState = &gDvm.methodTrace;
+
+ dvmLockMutex(&traceState->startStopLock);
+ if (traceState->traceEnabled) {
+ LOGI("threadid=%d: waiting for method trace to finish\n",
+ self->threadId);
+ while (traceState->traceEnabled) {
+ int cc;
+ cc = pthread_cond_wait(&traceState->threadExitCond,
+ &traceState->startStopLock);
+ assert(cc == 0);
+ }
+ }
+ dvmUnlockMutex(&traceState->startStopLock);
+#endif
+
+ dvmLockThreadList(self);
+
+ /*
+ * Lose the JNI context.
+ */
+ dvmDestroyJNIEnv(self->jniEnv);
+ self->jniEnv = NULL;
+
+ self->status = THREAD_ZOMBIE;
+
+ /*
+ * Remove ourselves from the internal thread list.
+ */
+ unlinkThread(self);
+
+ /*
+ * If we're the last one standing, signal anybody waiting in
+ * DestroyJavaVM that it's okay to exit.
+ */
+ if (!dvmGetFieldBoolean(self->threadObj, gDvm.offJavaLangThread_daemon)) {
+ gDvm.nonDaemonThreadCount--; // guarded by thread list lock
+
+ if (gDvm.nonDaemonThreadCount == 0) {
+ int cc;
+
+ LOGV("threadid=%d: last non-daemon thread\n", self->threadId);
+ //dvmDumpAllThreads(false);
+ // cond var guarded by threadListLock, which we already hold
+ cc = pthread_cond_signal(&gDvm.vmExitCond);
+ assert(cc == 0);
+ }
+ }
+
+ LOGV("threadid=%d: bye!\n", self->threadId);
+ releaseThreadId(self);
+ dvmUnlockThreadList();
+
+ setThreadSelf(NULL);
+ freeThread(self);
+}
+
+
+/*
+ * Suspend a single thread. Do not use to suspend yourself.
+ *
+ * This is used primarily for debugger/DDMS activity. Does not return
+ * until the thread has suspended or is in a "safe" state (e.g. executing
+ * native code outside the VM).
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmSuspendThread(Thread* thread)
+{
+ assert(thread != NULL);
+ assert(thread != dvmThreadSelf());
+ //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+ lockThreadSuspendCount();
+ thread->suspendCount++;
+ thread->dbgSuspendCount++;
+
+ LOG_THREAD("threadid=%d: suspend++, now=%d\n",
+ thread->threadId, thread->suspendCount);
+ unlockThreadSuspendCount();
+
+ waitForThreadSuspend(dvmThreadSelf(), thread);
+}
+
+/*
+ * Reduce the suspend count of a thread. If it hits zero, tell it to
+ * resume.
+ *
+ * Used primarily for debugger/DDMS activity. The thread in question
+ * might have been suspended singly or as part of a suspend-all operation.
+ *
+ * The thread list lock should be held before calling here -- it's not
+ * entirely safe to hang on to a Thread* from another thread otherwise.
+ * (We'd need to grab it here anyway to avoid clashing with a suspend-all.)
+ */
+void dvmResumeThread(Thread* thread)
+{
+ assert(thread != NULL);
+ assert(thread != dvmThreadSelf());
+ //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+
+ lockThreadSuspendCount();
+ if (thread->suspendCount > 0) {
+ thread->suspendCount--;
+ thread->dbgSuspendCount--;
+ } else {
+ LOG_THREAD("threadid=%d: suspendCount already zero\n",
+ thread->threadId);
+ }
+
+ LOG_THREAD("threadid=%d: suspend--, now=%d\n",
+ thread->threadId, thread->suspendCount);
+
+ if (thread->suspendCount == 0) {
+ int cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+ assert(cc == 0);
+ }
+
+ unlockThreadSuspendCount();
+}
+
+/*
+ * Suspend yourself, as a result of debugger activity.
+ */
+void dvmSuspendSelf(bool jdwpActivity)
+{
+ Thread* self = dvmThreadSelf();
+
+ /* debugger thread may not suspend itself due to debugger activity! */
+ assert(gDvm.jdwpState != NULL);
+ if (self->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+ assert(false);
+ return;
+ }
+
+ /*
+ * Collisions with other suspends aren't really interesting. We want
+ * to ensure that we're the only one fiddling with the suspend count
+ * though.
+ */
+ lockThreadSuspendCount();
+ self->suspendCount++;
+ self->dbgSuspendCount++;
+
+ /*
+ * Suspend ourselves.
+ */
+ assert(self->suspendCount > 0);
+ self->isSuspended = true;
+ LOG_THREAD("threadid=%d: self-suspending (dbg)\n", self->threadId);
+
+ /*
+ * Tell JDWP that we've completed suspension. The JDWP thread can't
+ * tell us to resume before we're fully asleep because we hold the
+ * suspend count lock.
+ *
+ * If we got here via waitForDebugger(), don't do this part.
+ */
+ if (jdwpActivity) {
+ //LOGI("threadid=%d: clearing wait-for-event (my handle=%08x)\n",
+ // self->threadId, (int) self->handle);
+ dvmJdwpClearWaitForEventThread(gDvm.jdwpState);
+ }
+
+ while (self->suspendCount != 0) {
+ int cc;
+ cc = pthread_cond_wait(&gDvm.threadSuspendCountCond,
+ &gDvm.threadSuspendCountLock);
+ assert(cc == 0);
+ if (self->suspendCount != 0) {
+ LOGD("threadid=%d: still suspended after undo (s=%d d=%d)\n",
+ self->threadId, self->suspendCount, self->dbgSuspendCount);
+ }
+ }
+ assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+ self->isSuspended = false;
+ LOG_THREAD("threadid=%d: self-reviving (dbg), status=%d\n",
+ self->threadId, self->status);
+
+ unlockThreadSuspendCount();
+}
+
+
+#ifdef HAVE_GLIBC
+# define NUM_FRAMES 20
+# include <execinfo.h>
+/*
+ * glibc-only stack dump function. Requires link with "--export-dynamic".
+ *
+ * TODO: move this into libs/cutils and make it work for all platforms.
+ */
+static void printBackTrace(void)
+{
+ void* array[NUM_FRAMES];
+ size_t size;
+ char** strings;
+ size_t i;
+
+ size = backtrace(array, NUM_FRAMES);
+ strings = backtrace_symbols(array, size);
+
+ LOGW("Obtained %zd stack frames.\n", size);
+
+ for (i = 0; i < size; i++)
+ LOGW("%s\n", strings[i]);
+
+ free(strings);
+}
+#else
+static void printBackTrace(void) {}
+#endif
+
+/*
+ * Dump the state of the current thread and that of another thread that
+ * we think is wedged.
+ */
+static void dumpWedgedThread(Thread* thread)
+{
+ char exePath[1024];
+
+ /*
+ * The "executablepath" function in libutils is host-side only.
+ */
+ strcpy(exePath, "-");
+#ifdef HAVE_GLIBC
+ {
+ char proc[100];
+ sprintf(proc, "/proc/%d/exe", getpid());
+ int len;
+
+ len = readlink(proc, exePath, sizeof(exePath)-1);
+ exePath[len] = '\0';
+ }
+#endif
+
+ LOGW("dumping state: process %s %d\n", exePath, getpid());
+ dvmDumpThread(dvmThreadSelf(), false);
+ printBackTrace();
+
+ // dumping a running thread is risky, but could be useful
+ dvmDumpThread(thread, true);
+
+
+ // stop now and get a core dump
+ //abort();
+}
+
+
+/*
+ * Wait for another thread to see the pending suspension and stop running.
+ * It can either suspend itself or go into a non-running state such as
+ * VMWAIT or NATIVE in which it cannot interact with the GC.
+ *
+ * If we're running at a higher priority, sched_yield() may not do anything,
+ * so we need to sleep for "long enough" to guarantee that the other
+ * thread has a chance to finish what it's doing. Sleeping for too short
+ * a period (e.g. less than the resolution of the sleep clock) might cause
+ * the scheduler to return immediately, so we want to start with a
+ * "reasonable" value and expand.
+ *
+ * This does not return until the other thread has stopped running.
+ * Eventually we time out and the VM aborts.
+ *
+ * This does not try to detect the situation where two threads are
+ * waiting for each other to suspend. In normal use this is part of a
+ * suspend-all, which implies that the suspend-all lock is held, or as
+ * part of a debugger action in which the JDWP thread is always the one
+ * doing the suspending. (We may need to re-evaluate this now that
+ * getThreadStackTrace is implemented as suspend-snapshot-resume.)
+ *
+ * TODO: track basic stats about time required to suspend VM.
+ */
+static void waitForThreadSuspend(Thread* self, Thread* thread)
+{
+ const int kMaxRetries = 10;
+ const int kSpinSleepTime = 750*1000; /* 0.75s */
+
+ int sleepIter = 0;
+ int retryCount = 0;
+ u8 startWhen = 0; // init req'd to placate gcc
+
+ while (thread->status == THREAD_RUNNING && !thread->isSuspended) {
+ if (sleepIter == 0) // get current time on first iteration
+ startWhen = dvmGetRelativeTimeUsec();
+
+ if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) {
+ LOGW("threadid=%d (h=%d): spin on suspend threadid=%d (handle=%d)\n",
+ self->threadId, (int)self->handle,
+ thread->threadId, (int)thread->handle);
+ dumpWedgedThread(thread);
+
+ // keep going; could be slow due to valgrind
+ sleepIter = 0;
+
+ if (retryCount++ == kMaxRetries) {
+ LOGE("threadid=%d: stuck on threadid=%d, giving up\n",
+ self->threadId, thread->threadId);
+ dvmDumpAllThreads(false);
+ dvmAbort();
+ }
+ }
+ }
+}
+
+/*
+ * Suspend all threads except the current one. This is used by the GC,
+ * the debugger, and by any thread that hits a "suspend all threads"
+ * debugger event (e.g. breakpoint or exception).
+ *
+ * If thread N hits a "suspend all threads" breakpoint, we don't want it
+ * to suspend the JDWP thread. For the GC, we do, because the debugger can
+ * create objects and even execute arbitrary code. The "why" argument
+ * allows the caller to say why the suspension is taking place.
+ *
+ * This can be called when a global suspend has already happened, due to
+ * various debugger gymnastics, so keeping an "everybody is suspended" flag
+ * doesn't work.
+ *
+ * DO NOT grab any locks before calling here. We grab & release the thread
+ * lock and suspend lock here (and we're not using recursive threads), and
+ * we might have to self-suspend if somebody else beats us here.
+ *
+ * The current thread may not be attached to the VM. This can happen if
+ * we happen to GC as the result of an allocation of a Thread object.
+ */
+void dvmSuspendAllThreads(SuspendCause why)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+
+ assert(why != 0);
+
+ /*
+ * Start by grabbing the thread suspend lock. If we can't get it, most
+ * likely somebody else is in the process of performing a suspend or
+ * resume, so lockThreadSuspend() will cause us to self-suspend.
+ *
+ * We keep the lock until all other threads are suspended.
+ */
+ lockThreadSuspend("susp-all", why);
+
+ LOG_THREAD("threadid=%d: SuspendAll starting\n", self->threadId);
+
+ /*
+ * This is possible if the current thread was in VMWAIT mode when a
+ * suspend-all happened, and then decided to do its own suspend-all.
+ * This can happen when a couple of threads have simultaneous events
+ * of interest to the debugger.
+ */
+ //assert(self->suspendCount == 0);
+
+ /*
+ * Increment everybody's suspend count (except our own).
+ */
+ dvmLockThreadList(self);
+
+ lockThreadSuspendCount();
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+ thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ continue;
+
+ thread->suspendCount++;
+ if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT)
+ thread->dbgSuspendCount++;
+ }
+ unlockThreadSuspendCount();
+
+ /*
+ * Wait for everybody in THREAD_RUNNING state to stop. Other states
+ * indicate the code is either running natively or sleeping quietly.
+ * Any attempt to transition back to THREAD_RUNNING will cause a check
+ * for suspension, so it should be impossible for anything to execute
+ * interpreted code or modify objects (assuming native code plays nicely).
+ *
+ * It's also okay if the thread transitions to a non-RUNNING state.
+ *
+ * Note we released the threadSuspendCountLock before getting here,
+ * so if another thread is fiddling with its suspend count (perhaps
+ * self-suspending for the debugger) it won't block while we're waiting
+ * in here.
+ */
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+ thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ continue;
+
+ /* wait for the other thread to see the pending suspend */
+ waitForThreadSuspend(self, thread);
+
+ LOG_THREAD("threadid=%d: threadid=%d status=%d c=%d dc=%d isSusp=%d\n",
+ self->threadId,
+ thread->threadId, thread->status, thread->suspendCount,
+ thread->dbgSuspendCount, thread->isSuspended);
+ }
+
+ dvmUnlockThreadList();
+ unlockThreadSuspend();
+
+ LOG_THREAD("threadid=%d: SuspendAll complete\n", self->threadId);
+}
+
+/*
+ * Resume all threads that are currently suspended.
+ *
+ * The "why" must match with the previous suspend.
+ */
+void dvmResumeAllThreads(SuspendCause why)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+ int cc;
+
+ lockThreadSuspend("res-all", why); /* one suspend/resume at a time */
+ LOG_THREAD("threadid=%d: ResumeAll starting\n", self->threadId);
+
+ /*
+ * Decrement the suspend counts for all threads. No need for atomic
+ * writes, since nobody should be moving until we decrement the count.
+ * We do need to hold the thread list because of JNI attaches.
+ */
+ dvmLockThreadList(self);
+ lockThreadSuspendCount();
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if ((why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) &&
+ thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
+ continue;
+
+ if (thread->suspendCount > 0) {
+ thread->suspendCount--;
+ if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT)
+ thread->dbgSuspendCount--;
+ } else {
+ LOG_THREAD("threadid=%d: suspendCount already zero\n",
+ thread->threadId);
+ }
+ }
+ unlockThreadSuspendCount();
+ dvmUnlockThreadList();
+
+ /*
+ * Broadcast a notification to all suspended threads, some or all of
+ * which may choose to wake up. No need to wait for them.
+ */
+ lockThreadSuspendCount();
+ cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+ assert(cc == 0);
+ unlockThreadSuspendCount();
+
+ unlockThreadSuspend();
+
+ LOG_THREAD("threadid=%d: ResumeAll complete\n", self->threadId);
+}
+
+/*
+ * Undo any debugger suspensions. This is called when the debugger
+ * disconnects.
+ */
+void dvmUndoDebuggerSuspensions(void)
+{
+ Thread* self = dvmThreadSelf();
+ Thread* thread;
+ int cc;
+
+ lockThreadSuspend("undo", SUSPEND_FOR_DEBUG);
+ LOG_THREAD("threadid=%d: UndoDebuggerSusp starting\n", self->threadId);
+
+ /*
+ * Decrement the suspend counts for all threads. No need for atomic
+ * writes, since nobody should be moving until we decrement the count.
+ * We do need to hold the thread list because of JNI attaches.
+ */
+ dvmLockThreadList(self);
+ lockThreadSuspendCount();
+ for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+ if (thread == self)
+ continue;
+
+ /* debugger events don't suspend JDWP thread */
+ if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) {
+ assert(thread->dbgSuspendCount == 0);
+ continue;
+ }
+
+ assert(thread->suspendCount >= thread->dbgSuspendCount);
+ thread->suspendCount -= thread->dbgSuspendCount;
+ thread->dbgSuspendCount = 0;
+ }
+ unlockThreadSuspendCount();
+ dvmUnlockThreadList();
+
+ /*
+ * Broadcast a notification to all suspended threads, some or all of
+ * which may choose to wake up. No need to wait for them.
+ */
+ lockThreadSuspendCount();
+ cc = pthread_cond_broadcast(&gDvm.threadSuspendCountCond);
+ assert(cc == 0);
+ unlockThreadSuspendCount();
+
+ unlockThreadSuspend();
+
+ LOG_THREAD("threadid=%d: UndoDebuggerSusp complete\n", self->threadId);
+}
+
+/*
+ * Determine if a thread is suspended.
+ *
+ * As with all operations on foreign threads, the caller should hold
+ * the thread list lock before calling.
+ */
+bool dvmIsSuspended(Thread* thread)
+{
+ /*
+ * The thread could be:
+ * (1) Running happily. status is RUNNING, isSuspended is false,
+ * suspendCount is zero. Return "false".
+ * (2) Pending suspend. status is RUNNING, isSuspended is false,
+ * suspendCount is nonzero. Return "false".
+ * (3) Suspended. suspendCount is nonzero, and either (status is
+ * RUNNING and isSuspended is true) OR (status is !RUNNING).
+ * Return "true".
+ * (4) Waking up. suspendCount is zero, status is RUNNING and
+ * isSuspended is true. Return "false" (since it could change
+ * out from under us, unless we hold suspendCountLock).
+ */
+
+ return (thread->suspendCount != 0 &&
+ ((thread->status == THREAD_RUNNING && thread->isSuspended) ||
+ (thread->status != THREAD_RUNNING)));
+}
+
+/*
+ * Wait until another thread self-suspends. This is specifically for
+ * synchronization between the JDWP thread and a thread that has decided
+ * to suspend itself after sending an event to the debugger.
+ *
+ * Threads that encounter "suspend all" events work as well -- the thread
+ * in question suspends everybody else and then itself.
+ *
+ * We can't hold a thread lock here or in the caller, because we could
+ * get here just before the to-be-waited-for-thread issues a "suspend all".
+ * There's an opportunity for badness if the thread we're waiting for exits
+ * and gets cleaned up, but since the thread in question is processing a
+ * debugger event, that's not really a possibility. (To avoid deadlock,
+ * it's important that we not be in THREAD_RUNNING while we wait.)
+ */
+void dvmWaitForSuspend(Thread* thread)
+{
+ Thread* self = dvmThreadSelf();
+
+ LOG_THREAD("threadid=%d: waiting for threadid=%d to sleep\n",
+ self->threadId, thread->threadId);
+
+ assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState));
+ assert(thread != self);
+ assert(self->status != THREAD_RUNNING);
+
+ waitForThreadSuspend(self, thread);
+
+ LOG_THREAD("threadid=%d: threadid=%d is now asleep\n",
+ self->threadId, thread->threadId);
+}
+
+/*
+ * Check to see if we need to suspend ourselves. If so, go to sleep on
+ * a condition variable.
+ *
+ * Takes "self" as an argument as an optimization. Pass in NULL to have
+ * it do the lookup.
+ *
+ * Returns "true" if we suspended ourselves.
+ */
+bool dvmCheckSuspendPending(Thread* self)
+{
+ bool didSuspend;
+
+ if (self == NULL)
+ self = dvmThreadSelf();
+
+ /* fast path: if count is zero, bail immediately */
+ if (self->suspendCount == 0)
+ return false;
+
+ lockThreadSuspendCount(); /* grab gDvm.threadSuspendCountLock */
+
+ assert(self->suspendCount >= 0); /* XXX: valid? useful? */
+
+ didSuspend = (self->suspendCount != 0);
+ self->isSuspended = true;
+ LOG_THREAD("threadid=%d: self-suspending\n", self->threadId);
+ while (self->suspendCount != 0) {
+ /* wait for wakeup signal; releases lock */
+ int cc;
+ cc = pthread_cond_wait(&gDvm.threadSuspendCountCond,
+ &gDvm.threadSuspendCountLock);
+ assert(cc == 0);
+ }
+ assert(self->suspendCount == 0 && self->dbgSuspendCount == 0);
+ self->isSuspended = false;
+ LOG_THREAD("threadid=%d: self-reviving, status=%d\n",
+ self->threadId, self->status);
+
+ unlockThreadSuspendCount();
+
+ return didSuspend;
+}
+
+/*
+ * Update our status.
+ *
+ * The "self" argument, which may be NULL, is accepted as an optimization.
+ *
+ * Returns the old status.
+ */
+ThreadStatus dvmChangeStatus(Thread* self, ThreadStatus newStatus)
+{
+ ThreadStatus oldStatus;
+
+ if (self == NULL)
+ self = dvmThreadSelf();
+
+ LOGVV("threadid=%d: (status %d -> %d)\n",
+ self->threadId, self->status, newStatus);
+
+ oldStatus = self->status;
+
+ if (newStatus == THREAD_RUNNING) {
+ /*
+ * Change our status to THREAD_RUNNING. The transition requires
+ * that we check for pending suspension, because the VM considers
+ * us to be "asleep" in all other states.
+ *
+ * We need to do the "suspend pending" check FIRST, because it grabs
+ * a lock that could be held by something that wants us to suspend.
+ * If we're in RUNNING it will wait for us, and we'll be waiting
+ * for the lock it holds.
+ */
+ assert(self->status != THREAD_RUNNING);
+
+ dvmCheckSuspendPending(self);
+ self->status = THREAD_RUNNING;
+ } else {
+ /*
+ * Change from one state to another, neither of which is
+ * THREAD_RUNNING. This is most common during system or thread
+ * initialization.
+ */
+ self->status = newStatus;
+ }
+
+ return oldStatus;
+}
+
+/*
+ * Get a statically defined thread group from a field in the ThreadGroup
+ * Class object. Expected arguments are "mMain" and "mSystem".
+ */
+static Object* getStaticThreadGroup(const char* fieldName)
+{
+ StaticField* groupField;
+ Object* groupObj;
+
+ groupField = dvmFindStaticField(gDvm.classJavaLangThreadGroup,
+ fieldName, "Ljava/lang/ThreadGroup;");
+ if (groupField == NULL) {
+ LOGE("java.lang.ThreadGroup does not have an '%s' field\n", fieldName);
+ dvmThrowException("Ljava/lang/IncompatibleClassChangeError;", NULL);
+ return NULL;
+ }
+ groupObj = dvmGetStaticFieldObject(groupField);
+ if (groupObj == NULL) {
+ LOGE("java.lang.ThreadGroup.%s not initialized\n", fieldName);
+ dvmThrowException("Ljava/lang/InternalError;", NULL);
+ return NULL;
+ }
+
+ return groupObj;
+}
+Object* dvmGetSystemThreadGroup(void)
+{
+ return getStaticThreadGroup("mSystem");
+}
+Object* dvmGetMainThreadGroup(void)
+{
+ return getStaticThreadGroup("mMain");
+}
+
+/*
+ * Given a VMThread object, return the associated Thread*.
+ *
+ * NOTE: if the thread detaches, the struct Thread will disappear, and
+ * we will be touching invalid data. For safety, lock the thread list
+ * before calling this.
+ */
+Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj)
+{
+ int vmData;
+
+ vmData = dvmGetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData);
+ return (Thread*) vmData;
+}
+
+
+/*
+ * Conversion map for "nice" values.
+ *
+ * We use Android thread priority constants to be consistent with the rest
+ * of the system. In some cases adjacent entries may overlap.
+ */
+static const int kNiceValues[10] = {
+ ANDROID_PRIORITY_LOWEST, /* 1 (MIN_PRIORITY) */
+ ANDROID_PRIORITY_BACKGROUND + 6,
+ ANDROID_PRIORITY_BACKGROUND + 3,
+ ANDROID_PRIORITY_BACKGROUND,
+ ANDROID_PRIORITY_NORMAL, /* 5 (NORM_PRIORITY) */
+ ANDROID_PRIORITY_NORMAL - 2,
+ ANDROID_PRIORITY_NORMAL - 4,
+ ANDROID_PRIORITY_URGENT_DISPLAY + 3,
+ ANDROID_PRIORITY_URGENT_DISPLAY + 2,
+ ANDROID_PRIORITY_URGENT_DISPLAY /* 10 (MAX_PRIORITY) */
+};
+
+/*
+ * Change the priority of a system thread to match that of the Thread object.
+ *
+ * We map a priority value from 1-10 to Linux "nice" values, where lower
+ * numbers indicate higher priority.
+ */
+void dvmChangeThreadPriority(Thread* thread, int newPriority)
+{
+ pid_t pid = thread->systemTid;
+ int newNice;
+
+ if (newPriority < 1 || newPriority > 10) {
+ LOGW("bad priority %d\n", newPriority);
+ newPriority = 5;
+ }
+ newNice = kNiceValues[newPriority-1];
+
+ if (setpriority(PRIO_PROCESS, pid, newNice) != 0) {
+ char* str = dvmGetThreadName(thread);
+ LOGI("setPriority(%d) '%s' to prio=%d(n=%d) failed: %s\n",
+ pid, str, newPriority, newNice, strerror(errno));
+ free(str);
+ } else {
+ LOGV("setPriority(%d) to prio=%d(n=%d)\n",
+ pid, newPriority, newNice);
+ }
+}
+
+/*
+ * Get the thread priority for the current thread by querying the system.
+ * This is useful when attaching a thread through JNI.
+ *
+ * Returns a value from 1 to 10 (compatible with java.lang.Thread values).
+ */
+static int getThreadPriorityFromSystem(void)
+{
+ int i, sysprio, jprio;
+
+ errno = 0;
+ sysprio = getpriority(PRIO_PROCESS, 0);
+ if (sysprio == -1 && errno != 0) {
+ LOGW("getpriority() failed: %s\n", strerror(errno));
+ return THREAD_NORM_PRIORITY;
+ }
+
+ jprio = THREAD_MIN_PRIORITY;
+ for (i = 0; i < NELEM(kNiceValues); i++) {
+ if (sysprio >= kNiceValues[i])
+ break;
+ jprio++;
+ }
+ if (jprio > THREAD_MAX_PRIORITY)
+ jprio = THREAD_MAX_PRIORITY;
+
+ return jprio;
+}
+
+
+/*
+ * Return true if the thread is on gDvm.threadList.
+ * Caller should not hold gDvm.threadListLock.
+ */
+bool dvmIsOnThreadList(const Thread* thread)
+{
+ bool ret = false;
+
+ dvmLockThreadList(NULL);
+ if (thread == gDvm.threadList) {
+ ret = true;
+ } else {
+ ret = thread->prev != NULL || thread->next != NULL;
+ }
+ dvmUnlockThreadList();
+
+ return ret;
+}
+
+/*
+ * Dump a thread to the log file -- just calls dvmDumpThreadEx() with an
+ * output target.
+ */
+void dvmDumpThread(Thread* thread, bool isRunning)
+{
+ DebugOutputTarget target;
+
+ dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+ dvmDumpThreadEx(&target, thread, isRunning);
+}
+
+/*
+ * Print information about the specified thread.
+ *
+ * Works best when the thread in question is "self" or has been suspended.
+ * When dumping a separate thread that's still running, set "isRunning" to
+ * use a more cautious thread dump function.
+ */
+void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread,
+ bool isRunning)
+{
+ /* tied to ThreadStatus enum */
+ static const char* kStatusNames[] = {
+ "ZOMBIE", "RUNNABLE", "TIMED_WAIT", "MONITOR", "WAIT",
+ "INITIALIZING", "STARTING", "NATIVE", "VMWAIT"
+ };
+ Object* threadObj;
+ Object* groupObj;
+ StringObject* nameStr;
+ char* threadName = NULL;
+ char* groupName = NULL;
+ bool isDaemon;
+ int priority; // java.lang.Thread priority
+ int policy; // pthread policy
+ struct sched_param sp; // pthread scheduling parameters
+
+ threadObj = thread->threadObj;
+ if (threadObj == NULL) {
+ LOGW("Can't dump thread %d: threadObj not set\n", thread->threadId);
+ return;
+ }
+ nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+ gDvm.offJavaLangThread_name);
+ threadName = dvmCreateCstrFromString(nameStr);
+
+ priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
+ isDaemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
+
+ if (pthread_getschedparam(pthread_self(), &policy, &sp) != 0) {
+ LOGW("Warning: pthread_getschedparam failed\n");
+ policy = -1;
+ sp.sched_priority = -1;
+ }
+
+ /* a null value for group is not expected, but deal with it anyway */
+ groupObj = (Object*) dvmGetFieldObject(threadObj,
+ gDvm.offJavaLangThread_group);
+ if (groupObj != NULL) {
+ int offset = dvmFindFieldOffset(gDvm.classJavaLangThreadGroup,
+ "name", "Ljava/lang/String;");
+ if (offset < 0) {
+ LOGW("Unable to find 'name' field in ThreadGroup\n");
+ } else {
+ nameStr = (StringObject*) dvmGetFieldObject(groupObj, offset);
+ groupName = dvmCreateCstrFromString(nameStr);
+ }
+ }
+ if (groupName == NULL)
+ groupName = strdup("(BOGUS GROUP)");
+
+ assert(thread->status < NELEM(kStatusNames));
+ dvmPrintDebugMessage(target,
+ "\"%s\"%s prio=%d tid=%d %s\n",
+ threadName, isDaemon ? " daemon" : "",
+ priority, thread->threadId, kStatusNames[thread->status]);
+ dvmPrintDebugMessage(target,
+ " | group=\"%s\" sCount=%d dsCount=%d s=%d obj=%p\n",
+ groupName, thread->suspendCount, thread->dbgSuspendCount,
+ thread->isSuspended, thread->threadObj);
+ dvmPrintDebugMessage(target,
+ " | sysTid=%d nice=%d sched=%d/%d handle=%d\n",
+ thread->systemTid, getpriority(PRIO_PROCESS, thread->systemTid),
+ policy, sp.sched_priority, (int)thread->handle);
+
+#ifdef WITH_MONITOR_TRACKING
+ if (!isRunning) {
+ LockedObjectData* lod = thread->pLockedObjects;
+ if (lod != NULL)
+ dvmPrintDebugMessage(target, " | monitors held:\n");
+ else
+ dvmPrintDebugMessage(target, " | monitors held: <none>\n");
+ while (lod != NULL) {
+ dvmPrintDebugMessage(target, " > %p[%d] (%s)\n",
+ lod->obj, lod->recursionCount, lod->obj->clazz->descriptor);
+ lod = lod->next;
+ }
+ }
+#endif
+
+ if (isRunning)
+ dvmDumpRunningThreadStack(target, thread);
+ else
+ dvmDumpThreadStack(target, thread);
+
+ free(threadName);
+ free(groupName);
+
+}
+
+/*
+ * Get the name of a thread.
+ *
+ * For correctness, the caller should hold the thread list lock to ensure
+ * that the thread doesn't go away mid-call.
+ *
+ * Returns a newly-allocated string, or NULL if the Thread doesn't have a name.
+ */
+char* dvmGetThreadName(Thread* thread)
+{
+ StringObject* nameObj;
+
+ if (thread->threadObj == NULL) {
+ LOGW("threadObj is NULL, name not available\n");
+ return strdup("-unknown-");
+ }
+
+ nameObj = (StringObject*)
+ dvmGetFieldObject(thread->threadObj, gDvm.offJavaLangThread_name);
+ return dvmCreateCstrFromString(nameObj);
+}
+
+/*
+ * Dump all threads to the log file -- just calls dvmDumpAllThreadsEx() with
+ * an output target.
+ */
+void dvmDumpAllThreads(bool grabLock)
+{
+ DebugOutputTarget target;
+
+ dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+ dvmDumpAllThreadsEx(&target, grabLock);
+}
+
+/*
+ * Print information about all known threads. Assumes they have been
+ * suspended (or are in a non-interpreting state, e.g. WAIT or NATIVE).
+ *
+ * If "grabLock" is true, we grab the thread lock list. This is important
+ * to do unless the caller already holds the lock.
+ */
+void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock)
+{
+ Thread* thread;
+
+ dvmPrintDebugMessage(target, "DALVIK THREADS:\n");
+
+ if (grabLock)
+ dvmLockThreadList(dvmThreadSelf());
+
+ thread = gDvm.threadList;
+ while (thread != NULL) {
+ dvmDumpThreadEx(target, thread, false);
+
+ /* verify link */
+ assert(thread->next == NULL || thread->next->prev == thread);
+
+ thread = thread->next;
+ }
+
+ if (grabLock)
+ dvmUnlockThreadList();
+}
+
+#ifdef WITH_MONITOR_TRACKING
+/*
+ * Count up the #of locked objects in the current thread.
+ */
+static int getThreadObjectCount(const Thread* self)
+{
+ LockedObjectData* lod;
+ int count = 0;
+
+ lod = self->pLockedObjects;
+ while (lod != NULL) {
+ count++;
+ lod = lod->next;
+ }
+ return count;
+}
+
+/*
+ * Add the object to the thread's locked object list if it doesn't already
+ * exist. The most recently added object is the most likely to be released
+ * next, so we insert at the head of the list.
+ *
+ * If it already exists, we increase the recursive lock count.
+ *
+ * The object's lock may be thin or fat.
+ */
+void dvmAddToMonitorList(Thread* self, Object* obj, bool withTrace)
+{
+ LockedObjectData* newLod;
+ LockedObjectData* lod;
+ int* trace;
+ int depth;
+
+ lod = self->pLockedObjects;
+ while (lod != NULL) {
+ if (lod->obj == obj) {
+ lod->recursionCount++;
+ LOGV("+++ +recursive lock %p -> %d\n", obj, lod->recursionCount);
+ return;
+ }
+ lod = lod->next;
+ }
+
+ newLod = (LockedObjectData*) calloc(1, sizeof(LockedObjectData));
+ if (newLod == NULL) {
+ LOGE("malloc failed on %d bytes\n", sizeof(LockedObjectData));
+ return;
+ }
+ newLod->obj = obj;
+ newLod->recursionCount = 0;
+
+ if (withTrace) {
+ trace = dvmFillInStackTraceRaw(self, &depth);
+ newLod->rawStackTrace = trace;
+ newLod->stackDepth = depth;
+ }
+
+ newLod->next = self->pLockedObjects;
+ self->pLockedObjects = newLod;
+
+ LOGV("+++ threadid=%d: added %p, now %d\n",
+ self->threadId, newLod, getThreadObjectCount(self));
+}
+
+/*
+ * Remove the object from the thread's locked object list. If the entry
+ * has a nonzero recursion count, we just decrement the count instead.
+ */
+void dvmRemoveFromMonitorList(Thread* self, Object* obj)
+{
+ LockedObjectData* lod;
+ LockedObjectData* prevLod;
+
+ lod = self->pLockedObjects;
+ prevLod = NULL;
+ while (lod != NULL) {
+ if (lod->obj == obj) {
+ if (lod->recursionCount > 0) {
+ lod->recursionCount--;
+ LOGV("+++ -recursive lock %p -> %d\n",
+ obj, lod->recursionCount);
+ return;
+ } else {
+ break;
+ }
+ }
+ prevLod = lod;
+ lod = lod->next;
+ }
+
+ if (lod == NULL) {
+ LOGW("BUG: object %p not found in thread's lock list\n", obj);
+ return;
+ }
+ if (prevLod == NULL) {
+ /* first item in list */
+ assert(self->pLockedObjects == lod);
+ self->pLockedObjects = lod->next;
+ } else {
+ /* middle/end of list */
+ prevLod->next = lod->next;
+ }
+
+ LOGV("+++ threadid=%d: removed %p, now %d\n",
+ self->threadId, lod, getThreadObjectCount(self));
+ free(lod->rawStackTrace);
+ free(lod);
+}
+
+/*
+ * If the specified object is already in the thread's locked object list,
+ * return the LockedObjectData struct. Otherwise return NULL.
+ */
+LockedObjectData* dvmFindInMonitorList(const Thread* self, const Object* obj)
+{
+ LockedObjectData* lod;
+
+ lod = self->pLockedObjects;
+ while (lod != NULL) {
+ if (lod->obj == obj)
+ return lod;
+ lod = lod->next;
+ }
+ return NULL;
+}
+#endif /*WITH_MONITOR_TRACKING*/
+
+
+/*
+ * GC helper functions
+ */
+
+static void gcScanInterpStackReferences(Thread *thread)
+{
+ const u4 *framePtr;
+
+ framePtr = (const u4 *)thread->curFrame;
+ while (framePtr != NULL) {
+ const StackSaveArea *saveArea;
+ const Method *method;
+
+ saveArea = SAVEAREA_FROM_FP(framePtr);
+ method = saveArea->method;
+ if (method != NULL) {
+#ifdef COUNT_PRECISE_METHODS
+ /* the GC is running, so no lock required */
+ if (!dvmIsNativeMethod(method)) {
+ if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
+ LOGI("Added %s.%s %p\n",
+ method->clazz->descriptor, method->name, method);
+ }
+#endif
+ int i;
+ for (i = method->registersSize - 1; i >= 0; i--) {
+ u4 rval = *framePtr++;
+//TODO: wrap markifobject in a macro that does pointer checks
+ if (rval != 0 && (rval & 0x3) == 0) {
+ dvmMarkIfObject((Object *)rval);
+ }
+ }
+ }
+ /* else this is a break frame; nothing to mark.
+ */
+
+ /* Don't fall into an infinite loop if things get corrupted.
+ */
+ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
+ saveArea->prevFrame == NULL);
+ framePtr = saveArea->prevFrame;
+ }
+}
+
+static void gcScanReferenceTable(ReferenceTable *refTable)
+{
+ Object **op;
+
+ //TODO: these asserts are overkill; turn them off when things stablize.
+ assert(refTable != NULL);
+ assert(refTable->table != NULL);
+ assert(refTable->nextEntry != NULL);
+ assert((uintptr_t)refTable->nextEntry >= (uintptr_t)refTable->table);
+ assert(refTable->nextEntry - refTable->table <= refTable->maxEntries);
+
+ op = refTable->table;
+ while ((uintptr_t)op < (uintptr_t)refTable->nextEntry) {
+ dvmMarkObjectNonNull(*(op++));
+ }
+}
+
+/*
+ * Scan a Thread and mark any objects it references.
+ */
+static void gcScanThread(Thread *thread)
+{
+ assert(thread != NULL);
+
+ /*
+ * The target thread must be suspended or in a state where it can't do
+ * any harm (e.g. in Object.wait()). The only exception is the current
+ * thread, which will still be active and in the "running" state.
+ *
+ * (Newly-created threads shouldn't be able to shift themselves to
+ * RUNNING without a suspend-pending check, so this shouldn't cause
+ * a false-positive.)
+ */
+ assert(thread->status != THREAD_RUNNING || thread->isSuspended ||
+ thread == dvmThreadSelf());
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_THREAD_OBJECT, thread->threadId);
+
+ dvmMarkObject(thread->threadObj); // could be NULL, when constructing
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_NATIVE_STACK, thread->threadId);
+
+ dvmMarkObject(thread->exception); // usually NULL
+ gcScanReferenceTable(&thread->internalLocalRefTable);
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_LOCAL, thread->threadId);
+
+ gcScanReferenceTable(&thread->jniLocalRefTable);
+
+ if (thread->jniMonitorRefTable.table != NULL) {
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_MONITOR, thread->threadId);
+
+ gcScanReferenceTable(&thread->jniMonitorRefTable);
+ }
+
+ HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JAVA_FRAME, thread->threadId);
+
+ gcScanInterpStackReferences(thread);
+
+ HPROF_CLEAR_GC_SCAN_STATE();
+}
+
+static void gcScanAllThreads()
+{
+ Thread *thread;
+
+ /* Lock the thread list so we can safely use the
+ * next/prev pointers.
+ */
+ dvmLockThreadList(dvmThreadSelf());
+
+ for (thread = gDvm.threadList; thread != NULL;
+ thread = thread->next)
+ {
+ /* We need to scan our own stack, so don't special-case
+ * the current thread.
+ */
+ gcScanThread(thread);
+ }
+
+ dvmUnlockThreadList();
+}
+
+void dvmGcScanRootThreadGroups()
+{
+ /* We scan the VM's list of threads instead of going
+ * through the actual ThreadGroups, but it should be
+ * equivalent.
+ *
+ * This assumes that the ThreadGroup class object is in
+ * the root set, which should always be true; it's
+ * loaded by the built-in class loader, which is part
+ * of the root set.
+ */
+ gcScanAllThreads();
+}
+