Sketch out detaching threads (with partial implementation).

Change-Id: Iafe5f91c8170d4f90155f509ba9e2e3d921af66f
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