Add a heap HWM to the Thread*, for compiled code.

Also fix a bug in thread detach, and implement the thread exit callback.

Destroy our pthread_mutex_t instances, and check for success. (This will
catch us deleting locked Mutex instances.)

Change-Id: I26cf8117b825234f6c790e0cf70b2c025a743f84
diff --git a/src/heap.cc b/src/heap.cc
index fc3fce0..d80f437 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -50,7 +50,7 @@
   }
 };
 
-bool Heap::Init(size_t initial_size, size_t maximum_size,
+void Heap::Init(size_t initial_size, size_t maximum_size,
                 const char* boot_image_file_name,
                 std::vector<const char*>& image_file_names) {
   Space* boot_space;
@@ -61,8 +61,7 @@
   } else {
     boot_space = Space::Create(boot_image_file_name);
     if (boot_space == NULL) {
-      LOG(WARNING) << "Failed to create space from " << boot_image_file_name;
-      return false;
+      LOG(FATAL) << "Failed to create space from " << boot_image_file_name;
     }
     spaces_.push_back(boot_space);
     requested_base = boot_space->GetBase() + RoundUp(boot_space->Size(), kPageSize);
@@ -72,8 +71,7 @@
   for (size_t i = 0; i < image_file_names.size(); i++) {
     Space* space = Space::Create(image_file_names[i]);
     if (space == NULL) {
-      LOG(WARNING) << "Failed to create space from " << image_file_names[i];
-      return false;
+      LOG(FATAL) << "Failed to create space from " << image_file_names[i];
     }
     image_spaces.push_back(space);
     spaces_.push_back(space);
@@ -82,8 +80,7 @@
 
   Space* space = Space::Create(initial_size, maximum_size, requested_base);
   if (space == NULL) {
-    LOG(WARNING) << "Failed to create alloc space";
-    return false;
+    LOG(FATAL) << "Failed to create alloc space";
   }
 
   if (boot_space == NULL) {
@@ -97,15 +94,13 @@
   // Allocate the initial live bitmap.
   UniquePtr<HeapBitmap> live_bitmap(HeapBitmap::Create(base, num_bytes));
   if (live_bitmap.get() == NULL) {
-    LOG(WARNING) << "Failed to create live bitmap";
-    return false;
+    LOG(FATAL) << "Failed to create live bitmap";
   }
 
   // Allocate the initial mark bitmap.
   UniquePtr<HeapBitmap> mark_bitmap(HeapBitmap::Create(base, num_bytes));
   if (mark_bitmap.get() == NULL) {
-    LOG(WARNING) << "Failed to create mark bitmap";
-    return false;
+    LOG(FATAL) << "Failed to create mark bitmap";
   }
 
   alloc_space_ = space;
@@ -132,8 +127,6 @@
   // but we can create the heap lock now. We don't create it earlier to
   // make it clear that you can't use locks during heap initialization.
   lock_ = Mutex::Create("Heap lock");
-
-  return true;
 }
 
 void Heap::Destroy() {
diff --git a/src/heap.h b/src/heap.h
index 296266a..52c1f10 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -30,7 +30,7 @@
   // Create a heap with the requested sizes. The optional boot image may
   // be NULL, otherwise it is an image filename created by ImageWriter.
   // image_file_names specifies application images to load.
-  static bool Init(size_t starting_size, size_t maximum_size,
+  static void Init(size_t starting_size, size_t maximum_size,
                    const char* boot_image_file_name,
                    std::vector<const char*>& image_file_names);
 
diff --git a/src/runtime.cc b/src/runtime.cc
index c7ae9c1..00dd832 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -23,7 +23,7 @@
 Runtime* Runtime::instance_ = NULL;
 
 Runtime::Runtime()
-    : stack_size_(0),
+    : default_stack_size_(Thread::kDefaultStackSize),
       thread_list_(NULL),
       intern_table_(NULL),
       class_linker_(NULL),
@@ -362,27 +362,19 @@
   exit_ = options->hook_exit_;
   abort_ = options->hook_abort_;
 
-  stack_size_ = options->stack_size_;
+  default_stack_size_ = options->stack_size_;
   thread_list_ = ThreadList::Create();
 
   intern_table_ = new InternTable;
 
-  if (!Heap::Init(options->heap_initial_size_,
-                  options->heap_maximum_size_,
-                  options->boot_image_,
-                  options->images_)) {
-    LOG(WARNING) << "Failed to create heap";
-    return false;
-  }
+  Heap::Init(options->heap_initial_size_, options->heap_maximum_size_,
+      options->boot_image_, options->images_);
 
   BlockSignals();
 
   java_vm_ = new JavaVMExt(this, options.get());
 
-  if (!Thread::Startup()) {
-    LOG(WARNING) << "Failed to startup threads";
-    return false;
-  }
+  Thread::Startup();
 
   // ClassLinker needs an attached thread, but we can't fully attach a thread
   // without creating objects.
diff --git a/src/runtime.h b/src/runtime.h
index 96513e6..99caa29 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -96,8 +96,8 @@
 
   ~Runtime();
 
-  size_t GetStackSize() const {
-    return stack_size_;
+  size_t GetDefaultStackSize() const {
+    return default_stack_size_;
   }
 
   ClassLinker* GetClassLinker() const {
@@ -130,7 +130,7 @@
   void RegisterRuntimeNativeMethods(JNIEnv*);
 
   // The default stack size for managed threads created by the runtime.
-  size_t stack_size_;
+  size_t default_stack_size_;
 
   ThreadList* thread_list_;
 
diff --git a/src/thread.cc b/src/thread.cc
index ae926ad..c771c5b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -191,6 +191,13 @@
   pDebugMe = DebugMe;
 }
 
+Mutex::~Mutex() {
+  errno = pthread_mutex_destroy(&mutex_);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_mutex_destroy failed";
+  }
+}
+
 Mutex* Mutex::Create(const char* name) {
   Mutex* mu = new Mutex(name);
 #ifndef NDEBUG
@@ -203,7 +210,7 @@
   if (errno != 0) {
     PLOG(FATAL) << "pthread_mutexattr_settype failed";
   }
-  errno = pthread_mutex_init(&mu->lock_impl_, &debug_attributes);
+  errno = pthread_mutex_init(&mu->mutex_, &debug_attributes);
   if (errno != 0) {
     PLOG(FATAL) << "pthread_mutex_init failed";
   }
@@ -212,7 +219,7 @@
     PLOG(FATAL) << "pthread_mutexattr_destroy failed";
   }
 #else
-  errno = pthread_mutex_init(&mu->lock_impl_, NULL);
+  errno = pthread_mutex_init(&mu->mutex_, NULL);
   if (errno != 0) {
     PLOG(FATAL) << "pthread_mutex_init failed";
   }
@@ -221,7 +228,7 @@
 }
 
 void Mutex::Lock() {
-  int result = pthread_mutex_lock(&lock_impl_);
+  int result = pthread_mutex_lock(&mutex_);
   if (result != 0) {
     errno = result;
     PLOG(FATAL) << "pthread_mutex_lock failed";
@@ -229,7 +236,7 @@
 }
 
 bool Mutex::TryLock() {
-  int result = pthread_mutex_trylock(&lock_impl_);
+  int result = pthread_mutex_trylock(&mutex_);
   if (result == EBUSY) {
     return false;
   }
@@ -241,7 +248,7 @@
 }
 
 void Mutex::Unlock() {
-  int result = pthread_mutex_unlock(&lock_impl_);
+  int result = pthread_mutex_unlock(&mutex_);
   if (result != 0) {
     errno = result;
     PLOG(FATAL) << "pthread_mutex_unlock failed";
@@ -274,7 +281,7 @@
 Thread* Thread::Create(const Runtime* runtime) {
   UNIMPLEMENTED(FATAL) << "need to pass in a java.lang.Thread";
 
-  size_t stack_size = runtime->GetStackSize();
+  size_t stack_size = runtime->GetDefaultStackSize();
 
   Thread* new_thread = new Thread;
 
@@ -294,7 +301,7 @@
     PLOG(FATAL) << "pthread_attr_setstacksize(" << stack_size << ") failed";
   }
 
-  errno = pthread_create(&new_thread->handle_, &attr, ThreadStart, new_thread);
+  errno = pthread_create(&new_thread->pthread_, &attr, ThreadStart, new_thread);
   if (errno != 0) {
     PLOG(FATAL) << "pthread_create failed";
   }
@@ -314,9 +321,11 @@
   Thread* self = new Thread;
 
   self->tid_ = ::art::GetTid();
-  self->handle_ = pthread_self();
+  self->pthread_ = pthread_self();
   self->is_daemon_ = as_daemon;
 
+  self->InitStackHwm();
+
   self->state_ = kRunnable;
 
   SetThreadName(name);
@@ -350,15 +359,40 @@
   jboolean thread_is_daemon = as_daemon;
 
   jclass c = env->FindClass("java/lang/Thread");
-  LOG(INFO) << "java/lang/Thread=" << (void*)c;
   jmethodID mid = env->GetMethodID(c, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
-  LOG(INFO) << "java/lang/Thread.<init>=" << (void*)mid;
   jobject o = env->NewObject(c, mid, thread_group, thread_name, thread_priority, thread_is_daemon);
   LOG(INFO) << "Created new java.lang.Thread " << (void*) o << " decoded=" << (void*) DecodeJObject(o);
 
   peer_ = DecodeJObject(o);
 }
 
+void Thread::InitStackHwm() {
+  pthread_attr_t attributes;
+  errno = pthread_getattr_np(pthread_, &attributes);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_getattr_np failed";
+  }
+
+  // stack_base is the "lowest addressable byte" of the stack.
+  void* stack_base;
+  size_t stack_size;
+  errno = pthread_attr_getstack(&attributes, &stack_base, &stack_size);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_attr_getstack failed";
+  }
+
+  const size_t kStackOverflowReservedBytes = 1024; // Space to throw a StackOverflowError in.
+  if (stack_size <= kStackOverflowReservedBytes) {
+    LOG(FATAL) << "attempt to attach a thread with a too-small stack (" << stack_size << " bytes)";
+  }
+  stack_hwm_ = reinterpret_cast<byte*>(stack_base) + stack_size - kStackOverflowReservedBytes;
+
+  errno = pthread_attr_destroy(&attributes);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_attr_destroy failed";
+  }
+}
+
 void Thread::Dump(std::ostream& os) const {
   /*
    * Get the java.lang.Thread object.  This function gets called from
@@ -437,7 +471,7 @@
 
   int policy;
   sched_param sp;
-  errno = pthread_getschedparam(handle_, &policy, &sp);
+  errno = pthread_getschedparam(pthread_, &policy, &sp);
   if (errno != 0) {
     PLOG(FATAL) << "pthread_getschedparam failed";
   }
@@ -514,27 +548,24 @@
   os << "UNIMPLEMENTED: Thread::DumpStack\n";
 }
 
-static void ThreadExitCheck(void* arg) {
-  LG << "Thread exit check";
+void Thread::ThreadExitCallback(void* arg) {
+  Thread* self = reinterpret_cast<Thread*>(arg);
+  LOG(FATAL) << "Native thread exited without calling DetachCurrentThread: " << *self;
 }
 
-bool Thread::Startup() {
+void Thread::Startup() {
   // Allocate a TLS slot.
-  errno = pthread_key_create(&Thread::pthread_key_self_, ThreadExitCheck);
+  errno = pthread_key_create(&Thread::pthread_key_self_, Thread::ThreadExitCallback);
   if (errno != 0) {
-    PLOG(WARNING) << "pthread_key_create failed";
-    return false;
+    PLOG(FATAL) << "pthread_key_create failed";
   }
 
   // Double-check the TLS slot allocation.
   if (pthread_getspecific(pthread_key_self_) != NULL) {
-    LOG(WARNING) << "newly-created pthread TLS slot is not NULL";
-    return false;
+    LOG(FATAL) << "newly-created pthread TLS slot is not NULL";
   }
 
   // TODO: initialize other locks and condition variables
-
-  return true;
 }
 
 void Thread::Shutdown() {
@@ -939,21 +970,33 @@
 }
 
 void ThreadList::Register(Thread* thread) {
-  LOG(INFO) << "ThreadList::Register() " << *thread;
+  //LOG(INFO) << "ThreadList::Register() " << *thread;
   MutexLock mu(lock_);
   CHECK(!Contains(thread));
   list_.push_back(thread);
 }
 
 void ThreadList::Unregister() {
-  LOG(INFO) << "ThreadList::Unregister() " << *Thread::Current();
-  MutexLock mu(lock_);
   Thread* self = Thread::Current();
+
+  //LOG(INFO) << "ThreadList::Unregister() " << self;
+  MutexLock mu(lock_);
+
+  // Remove this thread from the list.
   CHECK(Contains(self));
   list_.remove(self);
+
+  // Delete the Thread* and release the thin lock id.
   uint32_t thin_lock_id = self->thin_lock_id_;
   delete self;
   ReleaseThreadId(thin_lock_id);
+
+  // Clear the TLS data, so that thread is recognizably detached.
+  // (It may wish to reattach later.)
+  errno = pthread_setspecific(Thread::pthread_key_self_, NULL);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_setspecific failed";
+  }
 }
 
 void ThreadList::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
diff --git a/src/thread.h b/src/thread.h
index d50b45b..b9df711 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -38,7 +38,7 @@
 
 class Mutex {
  public:
-  virtual ~Mutex() {}
+  ~Mutex();
 
   void Lock();
 
@@ -50,14 +50,14 @@
 
   static Mutex* Create(const char* name);
 
-  pthread_mutex_t* GetImpl() { return &lock_impl_; }
+  pthread_mutex_t* GetImpl() { return &mutex_; }
 
  private:
   explicit Mutex(const char* name) : name_(name) {}
 
   const char* name_;
 
-  pthread_mutex_t lock_impl_;
+  pthread_mutex_t mutex_;
 
   DISALLOW_COPY_AND_ASSIGN(Mutex);
 };
@@ -266,7 +266,7 @@
   }
 
   pthread_t GetImpl() const {
-    return handle_;
+    return pthread_;
   }
 
   // Returns the Method* for the current method.
@@ -317,24 +317,6 @@
                                      void* throw_pc,
                                      const DexFile& dex_file,
                                      ClassLinker* class_linker);
-  static ThreadOffset SelfOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, self_));
-  }
-
-  // Offset of exception_ within Thread, used by generated code
-  static ThreadOffset ExceptionOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_));
-  }
-
-  // Offset of thin_lock_id_ within Thread, used by generated code
-  static ThreadOffset IdOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_));
-  }
-
-  // Offset of card_table within Thread, used by generated code
-  static ThreadOffset CardTableOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, card_table_));
-  }
 
   void SetName(const char* name);
 
@@ -344,40 +326,14 @@
 
   void Resume();
 
-  static bool Startup();
+  static void Startup();
   static void Shutdown();
 
-  static ThreadOffset SuspendCountOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_));
-  }
-
-  // Offset of state within Thread, used by generated code
-  static ThreadOffset StateOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, state_));
-  }
-
   // JNI methods
   JNIEnvExt* GetJniEnv() const {
     return jni_env_;
   }
 
-  // Offset of JNI environment within Thread, used by generated code
-  static ThreadOffset JniEnvOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, jni_env_));
-  }
-
-  // Offset of top of managed stack address, used by generated code
-  static ThreadOffset TopOfManagedStackOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_) +
-                        OFFSETOF_MEMBER(Frame, sp_));
-  }
-
-  // Offset of top stack indirect reference table within Thread, used by
-  // generated code
-  static ThreadOffset TopSirtOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_));
-  }
-
   // Number of references allocated in SIRTs on this thread
   size_t NumSirtReferences();
 
@@ -387,20 +343,10 @@
   // Convert a jobject into a Object*
   Object* DecodeJObject(jobject obj);
 
-  // Offset of exception_entry_point_ within Thread, used by generated code
-  static ThreadOffset ExceptionEntryPointOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_entry_point_));
-  }
-
   void RegisterExceptionEntryPoint(void (*handler)(Method**)) {
     exception_entry_point_ = handler;
   }
 
-  // Offset of suspend_count_entry_point_ within Thread, used by generated code
-  static ThreadOffset SuspendCountEntryPointOffset() {
-    return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_entry_point_));
-  }
-
   void RegisterSuspendCountEntryPoint(void (*handler)(Method**)) {
     suspend_count_entry_point_ = handler;
   }
@@ -436,6 +382,59 @@
 
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
 
+  //
+  // Offsets of various members of native Thread class, used by compiled code.
+  //
+
+  static ThreadOffset SelfOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, self_));
+  }
+
+  static ThreadOffset ExceptionOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_));
+  }
+
+  static ThreadOffset IdOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_));
+  }
+
+  static ThreadOffset CardTableOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, card_table_));
+  }
+
+  static ThreadOffset SuspendCountOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_));
+  }
+
+  static ThreadOffset StateOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, state_));
+  }
+
+  static ThreadOffset StackHwmOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, stack_hwm_));
+  }
+
+  static ThreadOffset JniEnvOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, jni_env_));
+  }
+
+  static ThreadOffset TopOfManagedStackOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_) +
+        OFFSETOF_MEMBER(Frame, sp_));
+  }
+
+  static ThreadOffset TopSirtOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, top_sirt_));
+  }
+
+  static ThreadOffset ExceptionEntryPointOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_entry_point_));
+  }
+
+  static ThreadOffset SuspendCountEntryPointOffset() {
+    return ThreadOffset(OFFSETOF_MEMBER(Thread, suspend_count_entry_point_));
+  }
+
  private:
   Thread();
   ~Thread();
@@ -449,6 +448,9 @@
 
   void InitCpu();
   void InitFunctionPointers();
+  void InitStackHwm();
+
+  static void ThreadExitCallback(void* arg);
 
   void WalkStack(StackVisitor* visitor);
 
@@ -463,7 +465,7 @@
   pid_t tid_;
 
   // Native thread handle.
-  pthread_t handle_;
+  pthread_t pthread_;
 
   bool is_daemon_;
 
@@ -473,6 +475,9 @@
   // FIXME: placeholder for the gc cardTable
   uint32_t card_table_;
 
+  // The high water mark for this thread's stack.
+  byte* stack_hwm_;
+
   // Top of the managed stack, written out prior to the state transition from
   // kRunnable to kNative. Uses include to give the starting point for scanning
   // a managed stack when a thread is in native code.