More threads work.
Adds stubs (and sometimes implementations) for dalvik.system.VMStack and
java.lang.Thread native methods. There was a bug in the dalvik
thread priority setting code, where the current thread and the passed-in
thread were confused.
I've also pulled Mutex and ThreadList out into their own files, and
moved some functionality around (with the aim of having more stuff
private, especially locks).
Change-Id: Ieb0f22669cac3df44ca34f7868f8e7d4dfa09ab6
diff --git a/src/thread.h b/src/thread.h
index 4d27151..3f6228a 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -8,12 +8,14 @@
#include <bitset>
#include <iosfwd>
#include <list>
+#include <string>
#include "dex_file.h"
#include "globals.h"
#include "jni_internal.h"
#include "logging.h"
#include "macros.h"
+#include "mutex.h"
#include "mem_map.h"
#include "offsets.h"
@@ -24,6 +26,7 @@
class ClassLinker;
class ClassLoader;
class Method;
+class Monitor;
class Object;
class Runtime;
class Thread;
@@ -36,43 +39,6 @@
template<class T> class PrimitiveArray;
typedef PrimitiveArray<int32_t> IntArray;
-class Mutex {
- public:
- ~Mutex();
-
- void Lock();
-
- bool TryLock();
-
- void Unlock();
-
- const char* GetName() { return name_; }
-
- static Mutex* Create(const char* name);
-
- pthread_mutex_t* GetImpl() { return &mutex_; }
-
- private:
- explicit Mutex(const char* name) : name_(name) {}
-
- const char* name_;
-
- pthread_mutex_t mutex_;
-
- DISALLOW_COPY_AND_ASSIGN(Mutex);
-};
-
-class MutexLock {
- public:
- explicit MutexLock(Mutex *mu) : mu_(mu) {
- mu_->Lock();
- }
- ~MutexLock() { mu_->Unlock(); }
- private:
- Mutex* const mu_;
- DISALLOW_COPY_AND_ASSIGN(MutexLock);
-};
-
// Stack allocated indirect reference table, allocated within the bridge frame
// between managed and native code.
class StackIndirectReferenceTable {
@@ -155,6 +121,12 @@
class Thread {
public:
+ /* thread priorities, from java.lang.Thread */
+ enum Priority {
+ kMinPriority = 1,
+ kNormPriority = 5,
+ kMaxPriority = 10,
+ };
enum State {
kUnknown = -1,
kNew,
@@ -250,6 +222,13 @@
return reinterpret_cast<Thread*>(thread);
}
+ static Thread* FromManagedThread(JNIEnv* env, jobject thread) {
+ // TODO: make these more generally available, and cached.
+ jclass java_lang_Thread = env->FindClass("java/lang/Thread");
+ jfieldID fid = env->GetFieldID(java_lang_Thread, "vmData", "I");
+ return reinterpret_cast<Thread*>(static_cast<uintptr_t>(env->GetIntField(thread, fid)));
+ }
+
void Dump(std::ostream& os) const;
State GetState() const {
@@ -262,6 +241,22 @@
return old_state;
}
+ /*
+ * Changes the priority of this thread to match that of the java.lang.Thread object.
+ *
+ * We map a priority value from 1-10 to Linux "nice" values, where lower
+ * numbers indicate higher priority.
+ */
+ void SetNativePriority(int newPriority);
+
+ /*
+ * Returns 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 GetNativePriority();
+
bool CanAccessDirectReferences() const {
// TODO: when we have a moving collector, we'll need: return state_ == kRunnable;
return true;
@@ -279,6 +274,10 @@
return pthread_;
}
+ jobject GetPeer() const {
+ return peer_;
+ }
+
// Returns the Method* for the current method.
// This is used by the JNI implementation for logging and diagnostic purposes.
const Method* GetCurrentMethod() const {
@@ -353,6 +352,20 @@
// Convert a jobject into a Object*
Object* DecodeJObject(jobject obj);
+ // Implements java.lang.Thread.interrupted.
+ bool Interrupted() {
+ MutexLock mu(wait_mutex_);
+ bool interrupted = interrupted_;
+ interrupted_ = false;
+ return interrupted;
+ }
+
+ // Implements java.lang.Thread.isInterrupted.
+ bool IsInterrupted() {
+ MutexLock mu(wait_mutex_);
+ return interrupted_;
+ }
+
void RegisterExceptionEntryPoint(void (*handler)(Method**)) {
exception_entry_point_ = handler;
}
@@ -480,7 +493,14 @@
bool is_daemon_;
// Our managed peer (an instance of java.lang.Thread).
- Object* peer_;
+ jobject peer_;
+
+ // Guards the 'interrupted_' and 'wait_monitor_' members.
+ mutable Mutex wait_mutex_;
+ // Pointer to the monitor lock we're currently waiting on (or NULL), guarded by wait_mutex_.
+ Monitor* wait_monitor_;
+ // Thread "interrupted" status; stays raised until queried or thrown, guarded by wait_mutex_.
+ bool interrupted_;
// FIXME: placeholder for the gc cardTable
uint32_t card_table_;
@@ -538,79 +558,6 @@
std::ostream& operator<<(std::ostream& os, const Thread& thread);
std::ostream& operator<<(std::ostream& os, const Thread::State& state);
-class ThreadList {
- public:
- static const uint32_t kMaxThreadId = 0xFFFF;
- static const uint32_t kInvalidId = 0;
- static const uint32_t kMainId = 1;
-
- static ThreadList* Create();
-
- ~ThreadList();
-
- void Dump(std::ostream& os);
-
- void Register(Thread* thread);
-
- void Unregister();
-
- bool Contains(Thread* thread);
-
- void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
-
- private:
- ThreadList();
-
- uint32_t AllocThreadId();
- void ReleaseThreadId(uint32_t id);
-
- void Lock() {
- lock_->Lock();
- }
-
- void Unlock() {
- lock_->Unlock();
- }
-
- Mutex* lock_;
-
- std::bitset<kMaxThreadId> allocated_ids_;
- std::list<Thread*> list_;
-
- friend class Thread;
- friend class ThreadListLock;
-
- DISALLOW_COPY_AND_ASSIGN(ThreadList);
-};
-
-class ThreadListLock {
- public:
- ThreadListLock(Thread* self = NULL) {
- if (self == NULL) {
- // Try to get it from TLS.
- self = Thread::Current();
- }
- Thread::State old_state;
- if (self != NULL) {
- old_state = self->SetState(Thread::kWaiting); // TODO: VMWAIT
- } else {
- // This happens during VM shutdown.
- old_state = Thread::kUnknown;
- }
- Runtime::Current()->GetThreadList()->Lock();
- if (self != NULL) {
- self->SetState(old_state);
- }
- }
-
- ~ThreadListLock() {
- Runtime::Current()->GetThreadList()->Unlock();
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ThreadListLock);
-};
-
class ScopedThreadStateChange {
public:
ScopedThreadStateChange(Thread* thread, Thread::State new_state) : thread_(thread) {