Merge "A little bit more of the threads implementation." into dalvik-dev
diff --git a/src/class_linker.cc b/src/class_linker.cc
index b8cd891..f312556 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -642,7 +642,7 @@
return NULL;
}
ObjectLock lock(klass);
- klass->SetClinitThreadId(self->GetId());
+ klass->SetClinitThreadId(self->GetTid());
// Add the newly loaded class to the loaded classes table.
bool success = InsertClass(descriptor, klass); // TODO: just return collision
if (!success) {
@@ -678,7 +678,7 @@
if (!klass->IsLinked() && !klass->IsErroneous()) {
ObjectLock lock(klass);
// Check for circular dependencies between classes.
- if (!klass->IsLinked() && klass->GetClinitThreadId() == self->GetId()) {
+ if (!klass->IsLinked() && klass->GetClinitThreadId() == self->GetTid()) {
self->ThrowNewException("Ljava/lang/ClassCircularityError;", NULL); // TODO: detail
return NULL;
}
@@ -1210,7 +1210,7 @@
while (klass->GetStatus() == Class::kStatusInitializing) {
// we caught somebody else in the act; was it us?
- if (klass->GetClinitThreadId() == self->GetId()) {
+ if (klass->GetClinitThreadId() == self->GetTid()) {
LG << "recursive <clinit>";
return true;
}
@@ -1258,7 +1258,7 @@
DCHECK(klass->GetStatus() < Class::kStatusInitializing);
- klass->SetClinitThreadId(self->GetId());
+ klass->SetClinitThreadId(self->GetTid());
klass->SetStatus(Class::kStatusInitializing);
}
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index ef15e8e..5ea25e8 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -452,7 +452,7 @@
: path_(path),
handle_(handle),
jni_on_load_lock_(Mutex::Create("JNI_OnLoad lock")),
- jni_on_load_tid_(Thread::Current()->GetId()),
+ jni_on_load_thread_id_(Thread::Current()->GetThinLockId()),
jni_on_load_result_(kPending) {
pthread_cond_init(&jni_on_load_cond_, NULL);
}
@@ -475,7 +475,7 @@
*/
bool CheckOnLoadResult(JavaVMExt* vm) {
Thread* self = Thread::Current();
- if (jni_on_load_tid_ == self->GetId()) {
+ if (jni_on_load_thread_id_ == self->GetThinLockId()) {
// Check this so we don't end up waiting for ourselves. We need
// to return "true" so the caller can continue.
LOG(INFO) << *self << " recursive attempt to load library "
@@ -503,7 +503,7 @@
void SetResult(bool result) {
jni_on_load_result_ = result ? kOkay : kFailed;
- jni_on_load_tid_ = 0;
+ jni_on_load_thread_id_ = 0;
// Broadcast a wakeup to anybody sleeping on the condition variable.
MutexLock mu(jni_on_load_lock_);
@@ -535,7 +535,7 @@
// Wait for JNI_OnLoad in other thread.
pthread_cond_t jni_on_load_cond_;
// Recursive invocation guard.
- uint32_t jni_on_load_tid_;
+ uint32_t jni_on_load_thread_id_;
// Result of earlier JNI_OnLoad call.
JNI_OnLoadState jni_on_load_result_;
};
diff --git a/src/object.cc b/src/object.cc
index 8b0c976..4384a24 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -50,7 +50,7 @@
}
Class* Field::GetType() const {
- DCHECK(Runtime::Current() != NULL)
+ DCHECK(Runtime::Current()->IsStarted())
<< "Can't call GetType without an initialized runtime";
// Do full linkage (which sets dex cache value to speed next call)
return Runtime::Current()->GetClassLinker()->ResolveType(GetTypeIdx(), this);
@@ -498,7 +498,7 @@
void Class::SetStatus(Status new_status) {
CHECK(new_status > GetStatus() || new_status == kStatusError ||
- Runtime::Current() == NULL); // no runtime implies we're not initialized
+ !Runtime::Current()->IsStarted());
CHECK(sizeof(Status) == sizeof(uint32_t));
return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_),
new_status, false);
diff --git a/src/object.h b/src/object.h
index 8504612..050f571 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1519,7 +1519,7 @@
Class* GetSuperClass() const {
// Can only get super class for loaded classes (hack for when runtime is
// initializing)
- DCHECK(IsLoaded() || Runtime::Current() == NULL);
+ DCHECK(IsLoaded() || !Runtime::Current()->IsStarted());
return GetFieldObject<Class*>(
OFFSET_OF_OBJECT_MEMBER(Class, super_class_), false);
}
@@ -1899,12 +1899,12 @@
Field* FindDeclaredStaticField(const StringPiece& name, Class* type);
- uint32_t GetClinitThreadId() const {
+ pid_t GetClinitThreadId() const {
DCHECK(IsIdxLoaded());
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_), false);
}
- void SetClinitThreadId(uint32_t new_clinit_thread_id) {
+ void SetClinitThreadId(pid_t new_clinit_thread_id) {
SetField32(OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_),
new_clinit_thread_id, false);
}
@@ -1962,7 +1962,7 @@
const Class* verify_error_class_;
// threadId, used to check for recursive <clinit> invocation
- uint32_t clinit_thread_id_;
+ pid_t clinit_thread_id_;
// Total object size; used when allocating storage on gc heap. (For
// interfaces and abstract classes this will be zero.)
diff --git a/src/runtime.cc b/src/runtime.cc
index 2c2c5e2..c8f9036 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -22,6 +22,19 @@
Runtime* Runtime::instance_ = NULL;
+Runtime::Runtime()
+ : stack_size_(0),
+ thread_list_(NULL),
+ intern_table_(NULL),
+ class_linker_(NULL),
+ signal_catcher_(NULL),
+ java_vm_(NULL),
+ started_(false),
+ vfprintf_(NULL),
+ exit_(NULL),
+ abort_(NULL) {
+}
+
Runtime::~Runtime() {
// TODO: use smart pointers instead. (we'll need the pimpl idiom.)
delete class_linker_;
@@ -313,20 +326,24 @@
if (Runtime::instance_ != NULL) {
return NULL;
}
- UniquePtr<Runtime> runtime(new Runtime());
- bool success = runtime->Init(options, ignore_unrecognized);
- if (!success) {
- return NULL;
+ instance_ = new Runtime;
+ if (!instance_->Init(options, ignore_unrecognized)) {
+ delete instance_;
+ instance_ = NULL;
}
- instance_ = runtime.release();
return instance_;
}
void Runtime::Start() {
+ started_ = true;
instance_->InitLibraries();
instance_->signal_catcher_ = new SignalCatcher;
}
+bool Runtime::IsStarted() {
+ return started_;
+}
+
bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);
@@ -361,7 +378,7 @@
return false;
}
- thread_list_->Register(Thread::Attach(this));
+ thread_list_->Register(Thread::Attach(this, "main", false));
class_linker_ = ClassLinker::Create(options->boot_class_path_,
options->class_path_,
@@ -450,10 +467,7 @@
}
void Runtime::AttachCurrentThread(const char* name, JNIEnv** penv, bool as_daemon) {
- if (as_daemon) {
- UNIMPLEMENTED(WARNING) << "TODO: do something different for daemon threads";
- }
- Thread* t = Thread::Attach(instance_);
+ Thread* t = Thread::Attach(instance_, name, as_daemon);
thread_list_->Register(t);
}
diff --git a/src/runtime.h b/src/runtime.h
index 8a6ffe3..db86a96 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -68,6 +68,8 @@
// Starts a runtime, which may cause threads to be started and code to run.
void Start();
+ bool IsStarted();
+
static Runtime* Current() {
return instance_;
}
@@ -119,8 +121,7 @@
private:
static void PlatformAbort(const char*, int);
- Runtime() : stack_size_(0), thread_list_(NULL), intern_table_(NULL), class_linker_(NULL),
- signal_catcher_(NULL) {}
+ Runtime();
void BlockSignals();
@@ -141,6 +142,8 @@
JavaVMExt* java_vm_;
+ bool started_;
+
// Hooks supported by JNI_CreateJavaVM
jint (*vfprintf_)(FILE* stream, const char* format, va_list ap);
void (*exit_)(jint status);
diff --git a/src/thread.cc b/src/thread.cc
index 97cc8ea..b99e38a 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -6,6 +6,7 @@
#include <sys/mman.h>
#include <algorithm>
+#include <bitset>
#include <cerrno>
#include <iostream>
#include <list>
@@ -216,6 +217,8 @@
}
Thread* Thread::Create(const Runtime* runtime) {
+ UNIMPLEMENTED(FATAL) << "need to pass in a java.lang.Thread";
+
size_t stack_size = runtime->GetStackSize();
Thread* new_thread = new Thread;
@@ -247,18 +250,48 @@
PLOG(FATAL) << "pthread_attr_destroy failed";
}
+ // TODO: get the "daemon" field from the java.lang.Thread.
+ // new_thread->is_daemon_ = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
+
return new_thread;
}
-Thread* Thread::Attach(const Runtime* runtime) {
+static const uint32_t kMaxThreadId = ((1 << 16) - 1);
+std::bitset<kMaxThreadId> gAllocatedThreadIds;
+
+uint32_t AllocThreadId() {
+ Runtime::Current()->GetThreadList()->Lock();
+ for (size_t i = 0; i < gAllocatedThreadIds.size(); ++i) {
+ if (!gAllocatedThreadIds[i]) {
+ gAllocatedThreadIds.set(i);
+ Runtime::Current()->GetThreadList()->Unlock();
+ return i + 1; // Zero is reserved to mean "invalid".
+ }
+ }
+ LOG(FATAL) << "Out of internal thread ids";
+ return 0;
+}
+
+void ReleaseThreadId(uint32_t id) {
+ Runtime::Current()->GetThreadList()->Lock();
+ CHECK(gAllocatedThreadIds[id]);
+ gAllocatedThreadIds.reset(id);
+ Runtime::Current()->GetThreadList()->Unlock();
+}
+
+Thread* Thread::Attach(const Runtime* runtime, const char* name, bool as_daemon) {
Thread* thread = new Thread;
thread->InitCpu();
- thread->handle_ = pthread_self();
+ thread->thin_lock_id_ = AllocThreadId();
thread->tid_ = ::art::GetTid();
+ thread->handle_ = pthread_self();
+ thread->is_daemon_ = as_daemon;
thread->state_ = kRunnable;
+ SetThreadName(name);
+
errno = pthread_setspecific(Thread::pthread_key_self_, thread);
if (errno != 0) {
PLOG(FATAL) << "pthread_setspecific failed";
@@ -283,18 +316,19 @@
*/
os << "TODO: pin Thread before dumping\n";
#if 0
- if (java_thread_ == NULL) {
+ // TODO: dalvikvm had this limitation, but we probably still want to do our best.
+ if (peer_ == NULL) {
LOGI("Can't dump thread %d: threadObj not set", threadId);
return;
}
- dvmAddTrackedAlloc(java_thread_, NULL);
+ dvmAddTrackedAlloc(peer_, NULL);
#endif
DumpState(os);
DumpStack(os);
#if 0
- dvmReleaseTrackedAlloc(java_thread_, NULL);
+ dvmReleaseTrackedAlloc(peer_, NULL);
#endif
}
@@ -326,16 +360,22 @@
void Thread::DumpState(std::ostream& os) const {
std::string thread_name("unknown");
int priority = -1;
- bool is_daemon = false;
+
#if 0 // TODO
nameStr = (StringObject*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
threadName = dvmCreateCstrFromString(nameStr);
priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
- is_daemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
#else
- thread_name = "TODO";
+ {
+ // TODO: this may be truncated; we should use the java.lang.Thread 'name' field instead.
+ std::string stats;
+ if (ReadFileToString(StringPrintf("/proc/self/task/%d/stat", GetTid()).c_str(), &stats)) {
+ size_t start = stats.find('(') + 1;
+ size_t end = stats.find(')') - start;
+ thread_name = stats.substr(start, end);
+ }
+ }
priority = -1;
- is_daemon = false;
#endif
int policy;
@@ -362,20 +402,20 @@
#endif
os << '"' << thread_name << '"';
- if (is_daemon) {
+ if (is_daemon_) {
os << " daemon";
}
os << " prio=" << priority
- << " tid=" << GetId()
+ << " tid=" << GetThinLockId()
<< " " << state_ << "\n";
int suspend_count = 0; // TODO
int debug_suspend_count = 0; // TODO
- void* java_thread_ = NULL; // TODO
+ void* peer_ = NULL; // TODO
os << " | group=\"" << group_name << "\""
<< " sCount=" << suspend_count
<< " dsCount=" << debug_suspend_count
- << " obj=" << reinterpret_cast<void*>(java_thread_)
+ << " obj=" << reinterpret_cast<void*>(peer_)
<< " self=" << reinterpret_cast<const void*>(this) << "\n";
os << " | sysTid=" << GetTid()
<< " nice=" << getpriority(PRIO_PROCESS, GetTid())
@@ -447,6 +487,19 @@
}
}
+Thread::Thread()
+ : thin_lock_id_(0),
+ peer_(NULL),
+ top_of_managed_stack_(),
+ native_to_managed_record_(NULL),
+ top_sirt_(NULL),
+ jni_env_(NULL),
+ exception_(NULL),
+ suspend_count_(0),
+ class_loader_override_(NULL) {
+ InitFunctionPointers();
+}
+
Thread::~Thread() {
delete jni_env_;
}
@@ -747,7 +800,7 @@
os << "Thread[" << &thread
<< ",pthread_t=" << thread.GetImpl()
<< ",tid=" << thread.GetTid()
- << ",id=" << thread.GetId()
+ << ",id=" << thread.GetThinLockId()
<< ",state=" << thread.GetState() << "]";
return os;
}
@@ -785,15 +838,15 @@
typedef std::list<Thread*>::const_iterator It; // TODO: C++0x auto
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
(*it)->Dump(os);
+ os << "\n";
}
- os << "\n";
}
void ThreadList::Register(Thread* thread) {
//LOG(INFO) << "ThreadList::Register() " << *thread;
MutexLock mu(lock_);
CHECK(!Contains(thread));
- list_.push_front(thread);
+ list_.push_back(thread);
}
void ThreadList::Unregister(Thread* thread) {
diff --git a/src/thread.h b/src/thread.h
index 87956b7..6b7e27c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -236,7 +236,7 @@
static Thread* Create(const Runtime* runtime);
// Creates a new thread from the calling thread.
- static Thread* Attach(const Runtime* runtime);
+ static Thread* Attach(const Runtime* runtime, const char* name, bool as_daemon);
static Thread* Current() {
void* thread = pthread_getspecific(Thread::pthread_key_self_);
@@ -260,8 +260,8 @@
return true;
}
- uint32_t GetId() const {
- return id_;
+ uint32_t GetThinLockId() const {
+ return thin_lock_id_;
}
pid_t GetTid() const {
@@ -324,14 +324,14 @@
return ThreadOffset(OFFSETOF_MEMBER(Thread, self_));
}
- // Offset of exception within Thread, used by generated code
+ // Offset of exception_ within Thread, used by generated code
static ThreadOffset ExceptionOffset() {
return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_));
}
- // Offset of id within Thread, used by generated code
+ // Offset of thin_lock_id_ within Thread, used by generated code
static ThreadOffset IdOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, id_));
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_));
}
// Offset of card_table within Thread, used by generated code
@@ -440,18 +440,7 @@
void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
private:
- Thread()
- : id_(1234),
- top_of_managed_stack_(),
- native_to_managed_record_(NULL),
- top_sirt_(NULL),
- jni_env_(NULL),
- exception_(NULL),
- suspend_count_(0),
- class_loader_override_(NULL) {
- InitFunctionPointers();
- }
-
+ Thread();
~Thread();
friend class Runtime; // For ~Thread.
@@ -463,8 +452,12 @@
void WalkStack(StackVisitor* visitor);
- // Managed thread id.
- uint32_t id_;
+ // Thin lock thread id. This is a small integer used by the thin lock implementation.
+ // This is not to be confused with the native thread's tid, nor is it the value returned
+ // by java.lang.Thread.getId --- this is a distinct value, used only for locking. One
+ // important difference between this id and the ids visible to managed code is that these
+ // ones get reused (to ensure that they fit in the number of bits available).
+ uint32_t thin_lock_id_;
// System thread id.
pid_t tid_;
@@ -472,6 +465,11 @@
// Native thread handle.
pthread_t handle_;
+ bool is_daemon_;
+
+ // Our managed peer (an instance of java.lang.Thread).
+ Object* peer_;
+
// FIXME: placeholder for the gc cardTable
uint32_t card_table_;
diff --git a/src/utils.cc b/src/utils.cc
index 90dd842..d890d29 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -12,6 +12,10 @@
#include "object.h"
#include "os.h"
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
namespace art {
bool ReadFileToString(const std::string& file_name, std::string* result) {
@@ -213,6 +217,40 @@
}
}
+void SetThreadName(const char *threadName) {
+ 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;
+ }
+#if defined(HAVE_ANDROID_PTHREAD_SETNAME_NP)
+ /* pthread_setname_np fails rather than truncating long strings */
+ char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
+ strncpy(buf, s, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ errno = pthread_setname_np(pthread_self(), buf);
+ if (errno != 0) {
+ PLOG(WARNING) << "Unable to set the name of current thread to '" << buf << "'";
+ }
+#elif defined(HAVE_PRCTL)
+ prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);
+#else
+#error no implementation for SetThreadName
+#endif
+}
+
} // namespace art
// Neither bionic nor glibc exposes gettid(2).
diff --git a/src/utils.h b/src/utils.h
index 69ca168..78d655e 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -180,6 +180,10 @@
// Returns the calling thread's tid. (The C libraries don't expose this.)
pid_t GetTid();
+// Sets the name of the current thread. The name may be truncated to an
+// implementation-defined limit.
+void SetThreadName(const char* name);
+
} // namespace art
#endif // ART_SRC_UTILS_H_