Manually manage thread pool stacks.

We now allocate the thread pool worker stack using a MemMap. This
enables us to name the maps so that we get more descriptive output
for debugging leaks.

Appears to fix the mips build 5/5 successful clean-oat and builds.
This is probably since glibc caches up to 40 MB of thread stacks
before releasing them.

Change-Id: I1df2de50cb95838aa0d272a09807021404ba410c
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 4af492b..d74383e 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -505,7 +505,7 @@
                                 const std::vector<const DexFile*>& dex_files,
                                 base::TimingLogger& timings) {
   DCHECK(!Runtime::Current()->IsStarted());
-  UniquePtr<ThreadPool> thread_pool(new ThreadPool(thread_count_ - 1));
+  UniquePtr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
   PreCompile(class_loader, dex_files, *thread_pool.get(), timings);
   Compile(class_loader, dex_files, *thread_pool.get(), timings);
   if (dump_stats_) {
@@ -568,7 +568,7 @@
   std::vector<const DexFile*> dex_files;
   dex_files.push_back(dex_file);
 
-  UniquePtr<ThreadPool> thread_pool(new ThreadPool(0U));
+  UniquePtr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", 0U));
   PreCompile(jclass_loader, dex_files, *thread_pool.get(), timings);
 
   uint32_t method_idx = method->GetDexMethodIndex();
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index 298ae56..91fc143 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -66,7 +66,7 @@
 // Check that barrier wait and barrier increment work.
 TEST_F(BarrierTest, CheckWait) {
   Thread* self = Thread::Current();
-  ThreadPool thread_pool(num_threads);
+  ThreadPool thread_pool("Barrier test thread pool", num_threads);
   Barrier barrier(0);
   AtomicInteger count1(0);
   AtomicInteger count2(0);
@@ -121,7 +121,7 @@
 // Check that barrier pass through works.
 TEST_F(BarrierTest, CheckPass) {
   Thread* self = Thread::Current();
-  ThreadPool thread_pool(num_threads);
+  ThreadPool thread_pool("Barrier test thread pool", num_threads);
   Barrier barrier(0);
   AtomicInteger count(0);
   const int32_t num_tasks = num_threads * 4;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 69ca620..70df3d3 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -309,7 +309,7 @@
 void Heap::CreateThreadPool() {
   const size_t num_threads = std::max(parallel_gc_threads_, conc_gc_threads_);
   if (num_threads != 0) {
-    thread_pool_.reset(new ThreadPool(num_threads));
+    thread_pool_.reset(new ThreadPool("Heap thread pool", num_threads));
   }
 }
 
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index bb6c475..aca0561 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -28,12 +28,15 @@
 ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name,
                                    size_t stack_size)
     : thread_pool_(thread_pool),
-      name_(name),
-      stack_size_(stack_size) {
+      name_(name) {
+  std::string error_msg;
+  stack_.reset(MemMap::MapAnonymous(name.c_str(), nullptr, stack_size, PROT_READ | PROT_WRITE,
+                                    &error_msg));
+  CHECK(stack_.get() != nullptr) << error_msg;
   const char* reason = "new thread pool worker thread";
   pthread_attr_t attr;
   CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), reason);
-  CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), reason);
+  CHECK_PTHREAD_CALL(pthread_attr_setstack, (&attr, stack_->Begin(), stack_->Size()), reason);
   CHECK_PTHREAD_CALL(pthread_create, (&pthread_, &attr, &Callback, this), reason);
   CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), reason);
 }
@@ -71,8 +74,9 @@
   }
 }
 
-ThreadPool::ThreadPool(size_t num_threads)
-  : task_queue_lock_("task queue lock"),
+ThreadPool::ThreadPool(const char* name, size_t num_threads)
+  : name_(name),
+    task_queue_lock_("task queue lock"),
     task_queue_condition_("task queue condition", task_queue_lock_),
     completion_condition_("task completion condition", task_queue_lock_),
     started_(false),
@@ -85,7 +89,7 @@
     max_active_workers_(num_threads) {
   Thread* self = Thread::Current();
   while (GetThreadCount() < num_threads) {
-    const std::string name = StringPrintf("Thread pool worker %zu", GetThreadCount());
+    const std::string name = StringPrintf("%s worker thread %zu", name_.c_str(), GetThreadCount());
     threads_.push_back(new ThreadPoolWorker(this, name, ThreadPoolWorker::kDefaultStackSize));
   }
   // Wait for all of the threads to attach.
@@ -270,8 +274,8 @@
 
 WorkStealingWorker::~WorkStealingWorker() {}
 
-WorkStealingThreadPool::WorkStealingThreadPool(size_t num_threads)
-    : ThreadPool(0),
+WorkStealingThreadPool::WorkStealingThreadPool(const char* name, size_t num_threads)
+    : ThreadPool(name, 0),
       work_steal_lock_("work stealing lock"),
       steal_index_(0) {
   while (GetThreadCount() < num_threads) {
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index b9a97a1..e8f9afe 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -24,6 +24,7 @@
 #include "base/mutex.h"
 #include "closure.h"
 #include "locks.h"
+#include "mem_map.h"
 
 namespace art {
 
@@ -40,7 +41,8 @@
   static const size_t kDefaultStackSize = 1 * MB;
 
   size_t GetStackSize() const {
-    return stack_size_;
+    DCHECK(stack_.get() != nullptr);
+    return stack_->Size();
   }
 
   virtual ~ThreadPoolWorker();
@@ -52,7 +54,7 @@
 
   ThreadPool* const thread_pool_;
   const std::string name_;
-  const size_t stack_size_;
+  UniquePtr<MemMap> stack_;
   pthread_t pthread_;
 
  private:
@@ -77,7 +79,7 @@
   // after running it, it is the caller's responsibility.
   void AddTask(Thread* self, Task* task);
 
-  explicit ThreadPool(size_t num_threads);
+  explicit ThreadPool(const char* name, size_t num_threads);
   virtual ~ThreadPool();
 
   // Wait for all tasks currently on queue to get completed.
@@ -107,6 +109,7 @@
     return shutting_down_;
   }
 
+  const std::string name_;
   Mutex task_queue_lock_;
   ConditionVariable task_queue_condition_ GUARDED_BY(task_queue_lock_);
   ConditionVariable completion_condition_ GUARDED_BY(task_queue_lock_);
@@ -167,7 +170,7 @@
 
 class WorkStealingThreadPool : public ThreadPool {
  public:
-  explicit WorkStealingThreadPool(size_t num_threads);
+  explicit WorkStealingThreadPool(const char* name, size_t num_threads);
   virtual ~WorkStealingThreadPool();
 
  private:
diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc
index 9b789d2..1b22361 100644
--- a/runtime/thread_pool_test.cc
+++ b/runtime/thread_pool_test.cc
@@ -59,7 +59,7 @@
 // Check that the thread pool actually runs tasks that you assign it.
 TEST_F(ThreadPoolTest, CheckRun) {
   Thread* self = Thread::Current();
-  ThreadPool thread_pool(num_threads);
+  ThreadPool thread_pool("Thread pool test thread pool", num_threads);
   AtomicInteger count(0);
   static const int32_t num_tasks = num_threads * 4;
   for (int32_t i = 0; i < num_tasks; ++i) {
@@ -74,7 +74,7 @@
 
 TEST_F(ThreadPoolTest, StopStart) {
   Thread* self = Thread::Current();
-  ThreadPool thread_pool(num_threads);
+  ThreadPool thread_pool("Thread pool test thread pool", num_threads);
   AtomicInteger count(0);
   static const int32_t num_tasks = num_threads * 4;
   for (int32_t i = 0; i < num_tasks; ++i) {
@@ -129,7 +129,7 @@
 // Test that adding new tasks from within a task works.
 TEST_F(ThreadPoolTest, RecursiveTest) {
   Thread* self = Thread::Current();
-  ThreadPool thread_pool(num_threads);
+  ThreadPool thread_pool("Thread pool test thread pool", num_threads);
   AtomicInteger count(0);
   static const int depth = 8;
   thread_pool.AddTask(self, new TreeTask(&thread_pool, &count, depth));