ART: Change shutdown order

Add explicit Shutdown method to ThreadList, doing the work that
was previously in the destructor. Call sid method before unloading
plugins and stopping the debugger.

Modulo daemon threads, this ensures that thread events will have
quiesced when plugins stop.

Bug: 37763974
Test: m test-art-host
Change-Id: I11d22f2c89a68e9f6720d61967b8e5aea41188dc
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e61ec23..f1e2105 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -336,6 +336,16 @@
     jit_->DeleteThreadPool();
   }
 
+  // Make sure our internal threads are dead before we start tearing down things they're using.
+  Dbg::StopJdwp();
+  delete signal_catcher_;
+
+  // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
+  {
+    ScopedTrace trace2("Delete thread list");
+    thread_list_->ShutDown();
+  }
+
   // TODO Maybe do some locking.
   for (auto& agent : agents_) {
     agent.Unload();
@@ -346,15 +356,9 @@
     plugin.Unload();
   }
 
-  // Make sure our internal threads are dead before we start tearing down things they're using.
-  Dbg::StopJdwp();
-  delete signal_catcher_;
+  // Finally delete the thread list.
+  delete thread_list_;
 
-  // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
-  {
-    ScopedTrace trace2("Delete thread list");
-    delete thread_list_;
-  }
   // Delete the JIT after thread list to ensure that there is no remaining threads which could be
   // accessing the instrumentation when we delete it.
   if (jit_ != nullptr) {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 2e0d866..d7f9ce3 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -73,12 +73,17 @@
       unregistering_count_(0),
       suspend_all_historam_("suspend all histogram", 16, 64),
       long_suspend_(false),
+      shut_down_(false),
       thread_suspend_timeout_ns_(thread_suspend_timeout_ns),
       empty_checkpoint_barrier_(new Barrier(0)) {
   CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1, 0U)));
 }
 
 ThreadList::~ThreadList() {
+  CHECK(shut_down_);
+}
+
+void ThreadList::ShutDown() {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   // Detach the current thread if necessary. If we failed to start, there might not be any threads.
   // We need to detach the current thread here in case there's another thread waiting to join with
@@ -102,6 +107,8 @@
   // TODO: there's an unaddressed race here where a thread may attach during shutdown, see
   //       Thread::Init.
   SuspendAllDaemonThreadsForShutdown();
+
+  shut_down_ = true;
 }
 
 bool ThreadList::Contains(Thread* thread) {
@@ -1362,6 +1369,7 @@
 
 void ThreadList::Register(Thread* self) {
   DCHECK_EQ(self, Thread::Current());
+  CHECK(!shut_down_);
 
   if (VLOG_IS_ON(threads)) {
     std::ostringstream oss;
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 70917eb..14bef5e 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -50,6 +50,8 @@
   explicit ThreadList(uint64_t thread_suspend_timeout_ns);
   ~ThreadList();
 
+  void ShutDown();
+
   void DumpForSigQuit(std::ostream& os)
       REQUIRES(!Locks::thread_list_lock_, !Locks::mutator_lock_);
   // For thread suspend timeout dumps.
@@ -219,6 +221,10 @@
   // Whether or not the current thread suspension is long.
   bool long_suspend_;
 
+  // Whether the shutdown function has been called. This is checked in the destructor. It is an
+  // error to destroy a ThreadList instance without first calling ShutDown().
+  bool shut_down_;
+
   // Thread suspension timeout in nanoseconds.
   const uint64_t thread_suspend_timeout_ns_;