Sketch out detaching threads (with partial implementation).
Change-Id: Iafe5f91c8170d4f90155f509ba9e2e3d921af66f
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 5ea25e8..8e24e47 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2183,7 +2183,7 @@
static jint MonitorExit(JNIEnv* env, jobject java_object) {
ScopedJniThreadState ts(env);
- Decode<Object*>(ts, java_object)->MonitorEnter();
+ Decode<Object*>(ts, java_object)->MonitorExit();
return ts.Self()->IsExceptionPending() ? JNI_ERR : JNI_OK;
}
diff --git a/src/jni_internal.h b/src/jni_internal.h
index ed66e64..a0fe8fc 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -97,7 +97,7 @@
int critical;
// Entered JNI monitors, for bulk exit on thread detach.
- ReferenceTable monitors;
+ ReferenceTable monitors;
// JNI local references.
IndirectReferenceTable locals;
diff --git a/src/runtime.cc b/src/runtime.cc
index c8f9036..2acf2ec 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -472,9 +472,7 @@
}
void Runtime::DetachCurrentThread() {
- Thread* self = Thread::Current();
- thread_list_->Unregister(self);
- delete self;
+ thread_list_->Unregister();
}
void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
diff --git a/src/thread.cc b/src/thread.cc
index b99e38a..f7938f1 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -187,12 +187,16 @@
}
void Mutex::Unlock() {
- CHECK(GetOwner() == Thread::Current());
+ DCHECK(HaveLock());
int result = pthread_mutex_unlock(&lock_impl_);
CHECK_EQ(result, 0);
SetOwner(NULL);
}
+bool Mutex::HaveLock() {
+ return owner_ == Thread::Current();
+}
+
void Frame::Next() {
byte* next_sp = reinterpret_cast<byte*>(sp_) +
GetMethod()->GetFrameSizeInBytes();
@@ -256,34 +260,10 @@
return new_thread;
}
-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->thin_lock_id_ = AllocThreadId();
thread->tid_ = ::art::GetTid();
thread->handle_ = pthread_self();
thread->is_daemon_ = as_daemon;
@@ -488,8 +468,7 @@
}
Thread::Thread()
- : thin_lock_id_(0),
- peer_(NULL),
+ : peer_(NULL),
top_of_managed_stack_(),
native_to_managed_record_(NULL),
top_sirt_(NULL),
@@ -497,11 +476,50 @@
exception_(NULL),
suspend_count_(0),
class_loader_override_(NULL) {
+ {
+ ThreadListLock mu;
+ thin_lock_id_ = Runtime::Current()->GetThreadList()->AllocThreadId();
+ }
InitFunctionPointers();
}
+void MonitorExitVisitor(const Object* object, void*) {
+ Object* entered_monitor = const_cast<Object*>(object);
+ entered_monitor->MonitorExit();;
+}
+
Thread::~Thread() {
+ // TODO: check we're not calling the JNI DetachCurrentThread function from
+ // a call stack that includes managed frames. (It's only valid if the stack is all-native.)
+
+ // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
+ jni_env_->monitors.VisitRoots(MonitorExitVisitor, NULL);
+
+ if (IsExceptionPending()) {
+ UNIMPLEMENTED(FATAL) << "threadExitUncaughtException()";
+ }
+
+ // TODO: ThreadGroup.removeThread(this);
+
+ // TODO: this.vmData = 0;
+
+ // TODO: say "bye" to the debugger.
+ //if (gDvm.debuggerConnected) {
+ // dvmDbgPostThreadDeath(self);
+ //}
+
+ // Thread.join() is implemented as an Object.wait() on the Thread.lock
+ // object. Signal anyone who is waiting.
+ //Object* lock = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_lock);
+ //dvmLockObject(self, lock);
+ //dvmObjectNotifyAll(self, lock);
+ //dvmUnlockObject(self, lock);
+ //lock = NULL;
+
delete jni_env_;
+ jni_env_ = NULL;
+
+ SetState(Thread::kTerminated);
}
size_t Thread::NumSirtReferences() {
@@ -849,11 +867,15 @@
list_.push_back(thread);
}
-void ThreadList::Unregister(Thread* thread) {
- //LOG(INFO) << "ThreadList::Unregister() " << *thread;
+void ThreadList::Unregister() {
+ //LOG(INFO) << "ThreadList::Unregister() " << *Thread::Current();
MutexLock mu(lock_);
- CHECK(Contains(thread));
- list_.remove(thread);
+ Thread* self = Thread::Current();
+ CHECK(Contains(self));
+ list_.remove(self);
+ uint32_t thin_lock_id = self->thin_lock_id_;
+ delete self;
+ ReleaseThreadId(thin_lock_id);
}
void ThreadList::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
@@ -864,4 +886,23 @@
}
}
+uint32_t ThreadList::AllocThreadId() {
+ DCHECK(lock_->HaveLock());
+ for (size_t i = 0; i < allocated_ids_.size(); ++i) {
+ if (!allocated_ids_[i]) {
+ allocated_ids_.set(i);
+ return i + 1; // Zero is reserved to mean "invalid".
+ }
+ }
+ LOG(FATAL) << "Out of internal thread ids";
+ return 0;
+}
+
+void ThreadList::ReleaseThreadId(uint32_t id) {
+ DCHECK(lock_->HaveLock());
+ --id; // Zero is reserved to mean "invalid".
+ DCHECK(allocated_ids_[id]) << id;
+ allocated_ids_.reset(id);
+}
+
} // namespace
diff --git a/src/thread.h b/src/thread.h
index 6b7e27c..a527deb 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -5,6 +5,7 @@
#include <pthread.h>
+#include <bitset>
#include <iosfwd>
#include <list>
@@ -49,6 +50,8 @@
Thread* GetOwner() { return owner_; }
+ bool HaveLock();
+
static Mutex* Create(const char* name);
// TODO: only needed because we lack a condition variable abstraction.
@@ -442,7 +445,7 @@
private:
Thread();
~Thread();
- friend class Runtime; // For ~Thread.
+ friend class ThreadList; // For ~Thread.
void DumpState(std::ostream& os) const;
void DumpStack(std::ostream& os) const;
@@ -524,7 +527,7 @@
class ThreadList {
public:
- static const int kMaxId = 0xFFFF;
+ static const int kMaxThreadId = 0xFFFF;
static const int kInvalidId = 0;
static const int kMainId = 1;
@@ -536,57 +539,62 @@
void Register(Thread* thread);
- void Unregister(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();
- };
-
- void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
-
- private:
- ThreadList();
-
- std::list<Thread*> list_;
+ }
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(ThreadList* thread_list, Thread* current_thread)
- : thread_list_(thread_list) {
- if (current_thread == NULL) { // try to get it from TLS
- current_thread = Thread::Current();
+ ThreadListLock(Thread* self = NULL) {
+ if (self == NULL) {
+ // Try to get it from TLS.
+ self = Thread::Current();
}
Thread::State old_state;
- if (current_thread != NULL) {
- old_state = current_thread->SetState(Thread::kWaiting); // TODO: VMWAIT
+ if (self != NULL) {
+ old_state = self->SetState(Thread::kWaiting); // TODO: VMWAIT
} else {
- // happens during VM shutdown
- old_state = Thread::kUnknown; // TODO: something else
+ // This happens during VM shutdown.
+ old_state = Thread::kUnknown;
}
- thread_list_->Lock();
- if (current_thread != NULL) {
- current_thread->SetState(old_state);
+ Runtime::Current()->GetThreadList()->Lock();
+ if (self != NULL) {
+ self->SetState(old_state);
}
}
~ThreadListLock() {
- thread_list_->Unlock();
+ Runtime::Current()->GetThreadList()->Unlock();
}
private:
- ThreadList* thread_list_;
-
DISALLOW_COPY_AND_ASSIGN(ThreadListLock);
};