Add locking to the heap.

Now tsan is happy with us too, at least on the host.

Change-Id: Ib4657f56be2014de832dff8886b63843a40ea788
diff --git a/src/heap.cc b/src/heap.cc
index 86d19a8..fc3fce0 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -37,6 +37,19 @@
 MemberOffset Heap::reference_pendingNext_offset_ = MemberOffset(0);
 MemberOffset Heap::finalizer_reference_zombie_offset_ = MemberOffset(0);
 
+Mutex* Heap::lock_ = NULL;
+
+class ScopedHeapLock {
+ public:
+  ScopedHeapLock() {
+    Heap::Lock();
+  }
+
+  ~ScopedHeapLock() {
+    Heap::Unlock();
+  }
+};
+
 bool Heap::Init(size_t initial_size, size_t maximum_size,
                 const char* boot_image_file_name,
                 std::vector<const char*>& image_file_names) {
@@ -115,10 +128,16 @@
     RecordImageAllocations(image_spaces[i]);
   }
 
+  // It's still to early to take a lock because there are no threads yet,
+  // 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() {
+  ScopedHeapLock lock;
   STLDeleteElements(&spaces_);
   if (mark_bitmap_ != NULL) {
     delete mark_bitmap_;
@@ -131,12 +150,13 @@
 }
 
 Object* Heap::AllocObject(Class* klass, size_t num_bytes) {
+  ScopedHeapLock lock;
   DCHECK(klass == NULL
          || klass->GetDescriptor() == NULL
          || (klass->IsClassClass() && num_bytes >= sizeof(Class))
          || (klass->IsVariableSize() || klass->GetObjectSize() == num_bytes));
   DCHECK(num_bytes >= sizeof(Object));
-  Object* obj = Allocate(num_bytes);
+  Object* obj = AllocateLocked(num_bytes);
   if (obj != NULL) {
     obj->SetClass(klass);
   }
@@ -144,6 +164,8 @@
 }
 
 bool Heap::IsHeapAddress(const Object* obj) {
+  // Note: we deliberately don't take the lock here, and mustn't test anything that would
+  // require taking the lock.
   if (!IsAligned(obj, kObjectAlignment)) {
     return false;
   }
@@ -155,6 +177,13 @@
 
 #if VERIFY_OBJECT_ENABLED
 void Heap::VerifyObject(const Object* obj) {
+  ScopedHeapLock lock;
+  Heap::VerifyObjectLocked(obj);
+}
+#endif
+
+void Heap::VerifyObjectLocked(const Object* obj) {
+  DCHECK_LOCK_HELD(lock_);
   if (obj != NULL && !verify_object_disabled_) {
     if (!IsAligned(obj, kObjectAlignment)) {
       LOG(FATAL) << "Object isn't aligned: " << obj;
@@ -188,18 +217,23 @@
     }
   }
 }
-#endif
 
-static void HeapVerifyCallback(Object* obj, void *arg) {
+void Heap::VerificationCallback(Object* obj, void *arg) {
   DCHECK(obj != NULL);
-  Heap::VerifyObject(obj);
+  Heap::VerifyObjectLocked(obj);
 }
 
 void Heap::VerifyHeap() {
-  live_bitmap_->Walk(HeapVerifyCallback, NULL);
+  ScopedHeapLock lock;
+  live_bitmap_->Walk(Heap::VerificationCallback, NULL);
 }
 
-void Heap::RecordAllocation(Space* space, const Object* obj) {
+void Heap::RecordAllocationLocked(Space* space, const Object* obj) {
+#ifndef NDEBUG
+  if (Runtime::Current()->IsStarted()) {
+    DCHECK_LOCK_HELD(lock_);
+  }
+#endif
   size_t size = space->AllocationSize(obj);
   DCHECK_NE(size, 0u);
   num_bytes_allocated_ += size;
@@ -207,7 +241,8 @@
   live_bitmap_->Set(obj);
 }
 
-void Heap::RecordFree(Space* space, const Object* obj) {
+void Heap::RecordFreeLocked(Space* space, const Object* obj) {
+  DCHECK_LOCK_HELD(lock_);
   size_t size = space->AllocationSize(obj);
   DCHECK_NE(size, 0u);
   if (size < num_bytes_allocated_) {
@@ -222,6 +257,7 @@
 }
 
 void Heap::RecordImageAllocations(Space* space) {
+  DCHECK(!Runtime::Current()->IsStarted());
   CHECK(space != NULL);
   CHECK(live_bitmap_ != NULL);
   byte* current = space->GetBase() + RoundUp(sizeof(ImageHeader), kObjectAlignment);
@@ -233,17 +269,20 @@
   }
 }
 
-Object* Heap::Allocate(size_t size) {
+Object* Heap::AllocateLocked(size_t size) {
+  DCHECK_LOCK_HELD(lock_);
   DCHECK(alloc_space_ != NULL);
   Space* space = alloc_space_;
-  Object* obj = Allocate(space, size);
+  Object* obj = AllocateLocked(space, size);
   if (obj != NULL) {
-    RecordAllocation(space, obj);
+    RecordAllocationLocked(space, obj);
   }
   return obj;
 }
 
-Object* Heap::Allocate(Space* space, size_t size) {
+Object* Heap::AllocateLocked(Space* space, size_t size) {
+  DCHECK_LOCK_HELD(lock_);
+
   // Fail impossible allocations.  TODO: collect soft references.
   if (size > maximum_size_) {
     return NULL;
@@ -328,11 +367,12 @@
 }
 
 void Heap::CollectGarbage() {
+  ScopedHeapLock lock;
   CollectGarbageInternal();
 }
 
 void Heap::CollectGarbageInternal() {
-  // TODO: check that heap lock is held
+  DCHECK_LOCK_HELD(lock_);
 
   // TODO: Suspend all threads
   {
@@ -369,13 +409,25 @@
 }
 
 void Heap::WaitForConcurrentGcToComplete() {
+  DCHECK_LOCK_HELD(lock_);
 }
 
 // Given the current contents of the active heap, increase the allowed
 // heap footprint to match the target utilization ratio.  This should
 // only be called immediately after a full garbage collection.
 void Heap::GrowForUtilization() {
+  DCHECK_LOCK_HELD(lock_);
   UNIMPLEMENTED(ERROR);
 }
 
+void Heap::Lock() {
+  // TODO: grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
+  // we're going to have to wait on the mutex.
+  lock_->Lock();
+}
+
+void Heap::Unlock() {
+  lock_->Unlock();
+}
+
 }  // namespace art
diff --git a/src/heap.h b/src/heap.h
index 3dc612a..296266a 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -67,9 +67,9 @@
   // Blocks the caller until the garbage collector becomes idle.
   static void WaitForConcurrentGcToComplete();
 
-  static Mutex* GetLock() {
-    return lock_;
-  }
+  static void Lock();
+
+  static void Unlock();
 
   static const std::vector<Space*>& GetSpaces() {
     return spaces_;
@@ -134,20 +134,25 @@
     verify_object_disabled_ = true;
   }
 
-  static void RecordFree(Space* space, const Object* object);
+  // Callers must hold the heap lock.
+  static void RecordFreeLocked(Space* space, const Object* object);
 
  private:
   // Allocates uninitialized storage.
-  static Object* Allocate(size_t num_bytes);
-  static Object* Allocate(Space* space, size_t num_bytes);
+  static Object* AllocateLocked(size_t num_bytes);
+  static Object* AllocateLocked(Space* space, size_t num_bytes);
 
-  static void RecordAllocation(Space* space, const Object* object);
+  static void RecordAllocationLocked(Space* space, const Object* object);
   static void RecordImageAllocations(Space* space);
 
   static void CollectGarbageInternal();
 
   static void GrowForUtilization();
 
+  static void VerifyObjectLocked(const Object *obj);
+
+  static void VerificationCallback(Object* obj, void *arg);
+
   static Mutex* lock_;
 
   static std::vector<Space*> spaces_;
diff --git a/src/logging.h b/src/logging.h
index 43596b5..66190ac 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -72,8 +72,20 @@
 #define DCHECK_STREQ(s1, s2) CHECK_STREQ(s1, s2)
 #define DCHECK_STRNE(s1, s2) CHECK_STRNE(s1, s2)
 
+  // These require "utils.h" and only work with bionic (not glibc).
+#ifdef __BIONIC__
+#define DCHECK_LOCK_HELD(l) CHECK_EQ(art::GetOwner(l->GetImpl()), art::GetTid())
+#define DCHECK_LOCK_NOT_HELD(l) CHECK_NE(art::GetOwner(l->GetImpl()), art::GetTid())
+#else
+#define DCHECK_LOCK_HELD(l)
+#define DCHECK_LOCK_NOT_HELD(l)
+#endif
+
 #else  // NDEBUG
 
+#define DCHECK_LOCK_HELD(l)
+#define DCHECK_LOCK_NOT_HELD(l)
+
 #define DCHECK(condition) \
   while (false) \
     CHECK(condition)
diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc
index 307a804..c947da3 100644
--- a/src/mark_sweep.cc
+++ b/src/mark_sweep.cc
@@ -151,7 +151,7 @@
   Space* space = static_cast<Space*>(arg);
   for (size_t i = 0; i < num_ptrs; ++i) {
     Object* obj = static_cast<Object*>(ptrs[i]);
-    Heap::RecordFree(space, obj);
+    Heap::RecordFreeLocked(space, obj);
     space->Free(obj);
   }
   // TODO, unlock heap if concurrent
diff --git a/src/thread.cc b/src/thread.cc
index 6109825..ae926ad 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -193,53 +193,59 @@
 
 Mutex* Mutex::Create(const char* name) {
   Mutex* mu = new Mutex(name);
-  int result = pthread_mutex_init(&mu->lock_impl_, NULL);
-  CHECK_EQ(result, 0);
+#ifndef NDEBUG
+  pthread_mutexattr_t debug_attributes;
+  errno = pthread_mutexattr_init(&debug_attributes);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_mutexattr_init failed";
+  }
+  errno = pthread_mutexattr_settype(&debug_attributes, PTHREAD_MUTEX_ERRORCHECK);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_mutexattr_settype failed";
+  }
+  errno = pthread_mutex_init(&mu->lock_impl_, &debug_attributes);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_mutex_init failed";
+  }
+  errno = pthread_mutexattr_destroy(&debug_attributes);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_mutexattr_destroy failed";
+  }
+#else
+  errno = pthread_mutex_init(&mu->lock_impl_, NULL);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_mutex_init failed";
+  }
+#endif
   return mu;
 }
 
 void Mutex::Lock() {
   int result = pthread_mutex_lock(&lock_impl_);
-  CHECK_EQ(result, 0);
-  SetOwner(Thread::Current());
+  if (result != 0) {
+    errno = result;
+    PLOG(FATAL) << "pthread_mutex_lock failed";
+  }
 }
 
 bool Mutex::TryLock() {
-  int result = pthread_mutex_lock(&lock_impl_);
+  int result = pthread_mutex_trylock(&lock_impl_);
   if (result == EBUSY) {
     return false;
-  } else {
-    CHECK_EQ(result, 0);
-    SetOwner(Thread::Current());
-    return true;
   }
+  if (result != 0) {
+    errno = result;
+    PLOG(FATAL) << "pthread_mutex_trylock failed";
+  }
+  return true;
 }
 
 void Mutex::Unlock() {
-#ifndef NDEBUG
-  Thread* self = Thread::Current();
-  std::stringstream os;
-  os << "owner=";
-  if (owner_ != NULL) {
-    os << *owner_;
-  } else {
-    os << "NULL";
-  }
-  os << " self=";
-  if (self != NULL) {
-    os << *self;
-  } else {
-    os << "NULL";
-  }
-  DCHECK(HaveLock()) << os.str();
-#endif
-  SetOwner(NULL);
   int result = pthread_mutex_unlock(&lock_impl_);
-  CHECK_EQ(result, 0);
-}
-
-bool Mutex::HaveLock() {
-  return owner_ == Thread::Current();
+  if (result != 0) {
+    errno = result;
+    PLOG(FATAL) << "pthread_mutex_unlock failed";
+  }
 }
 
 void Frame::Next() {
@@ -933,14 +939,14 @@
 }
 
 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();
+  LOG(INFO) << "ThreadList::Unregister() " << *Thread::Current();
   MutexLock mu(lock_);
   Thread* self = Thread::Current();
   CHECK(Contains(self));
@@ -959,7 +965,7 @@
 }
 
 uint32_t ThreadList::AllocThreadId() {
-  DCHECK(lock_->HaveLock());
+  DCHECK_LOCK_HELD(lock_);
   for (size_t i = 0; i < allocated_ids_.size(); ++i) {
     if (!allocated_ids_[i]) {
       allocated_ids_.set(i);
@@ -971,7 +977,7 @@
 }
 
 void ThreadList::ReleaseThreadId(uint32_t id) {
-  DCHECK(lock_->HaveLock());
+  DCHECK_LOCK_HELD(lock_);
   --id; // Zero is reserved to mean "invalid".
   DCHECK(allocated_ids_[id]) << id;
   allocated_ids_.reset(id);
diff --git a/src/thread.h b/src/thread.h
index ba5d6ef..d50b45b 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -48,24 +48,15 @@
 
   const char* GetName() { return name_; }
 
-  Thread* GetOwner() { return owner_; }
-
-  bool HaveLock();
-
   static Mutex* Create(const char* name);
 
-  // TODO: only needed because we lack a condition variable abstraction.
   pthread_mutex_t* GetImpl() { return &lock_impl_; }
 
  private:
-  explicit Mutex(const char* name) : name_(name), owner_(NULL) {}
-
-  void SetOwner(Thread* thread) { owner_ = thread; }
+  explicit Mutex(const char* name) : name_(name) {}
 
   const char* name_;
 
-  Thread* owner_;
-
   pthread_mutex_t lock_impl_;
 
   DISALLOW_COPY_AND_ASSIGN(Mutex);
diff --git a/src/utils.cc b/src/utils.cc
index d890d29..fed8a5e 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -3,6 +3,7 @@
 
 #include "utils.h"
 
+#include <pthread.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -251,6 +252,15 @@
 #endif
 }
 
+pid_t GetOwner(pthread_mutex_t* mutex) {
+#ifdef __BIONIC__
+  return static_cast<pid_t>(((mutex)->value >> 16) & 0xffff);
+#else
+  UNIMPLEMENTED(FATAL);
+  return 0;
+#endif
+}
+
 }  // namespace art
 
 // Neither bionic nor glibc exposes gettid(2).
diff --git a/src/utils.h b/src/utils.h
index 78d655e..135ea59 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -8,6 +8,7 @@
 #include "stringpiece.h"
 #include "stringprintf.h"
 
+#include <pthread.h>
 #include <string>
 #include <vector>
 
@@ -180,6 +181,9 @@
 // Returns the calling thread's tid. (The C libraries don't expose this.)
 pid_t GetTid();
 
+// Returns the tid of the thread that owns the given pthread mutex, or 0.
+pid_t GetOwner(pthread_mutex_t* mutex);
+
 // Sets the name of the current thread. The name may be truncated to an
 // implementation-defined limit.
 void SetThreadName(const char* name);