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);
 };