Make the microtask queue thread-local

Previously, all the threads tried to share a common queue without a lock.

Change-Id: I85d444e950da565dc8fa6b1d1a28c8ad255212b1
diff --git a/dart_microtask_queue.cc b/dart_microtask_queue.cc
index 6a97d79..6e7ba04 100644
--- a/dart_microtask_queue.cc
+++ b/dart_microtask_queue.cc
@@ -4,36 +4,39 @@
 
 #include "lib/tonic/dart_microtask_queue.h"
 
-#include <vector>
-
 #include "lib/tonic/logging/dart_invoke.h"
-#include "lib/tonic/dart_persistent_value.h"
 #include "lib/tonic/dart_state.h"
 #include "lib/tonic/dart_sticky_error.h"
 
 namespace tonic {
 namespace {
 
-typedef std::vector<DartPersistentValue> MicrotaskQueue;
+thread_local DartMicrotaskQueue* g_queue = nullptr;
 
-DartErrorHandleType g_last_error = kNoError;
-
-static MicrotaskQueue& GetQueue() {
-  static MicrotaskQueue* queue = new MicrotaskQueue();
-  return *queue;
 }
 
-}  // namespace
+DartMicrotaskQueue::DartMicrotaskQueue() : last_error_(kNoError) {}
+
+DartMicrotaskQueue::~DartMicrotaskQueue() = default;
+
+void DartMicrotaskQueue::StartForCurrentThread() {
+  FTL_CHECK(!g_queue);
+  g_queue = new DartMicrotaskQueue();
+}
+
+DartMicrotaskQueue* DartMicrotaskQueue::GetForCurrentThread() {
+  FTL_DCHECK(g_queue);
+  return g_queue;
+}
 
 void DartMicrotaskQueue::ScheduleMicrotask(Dart_Handle callback) {
-  GetQueue().emplace_back(DartState::Current(), callback);
+  queue_.emplace_back(DartState::Current(), callback);
 }
 
 void DartMicrotaskQueue::RunMicrotasks() {
-  MicrotaskQueue& queue = GetQueue();
-  while (!queue.empty()) {
+  while (!queue_.empty()) {
     MicrotaskQueue local;
-    std::swap(queue, local);
+    std::swap(queue_, local);
     for (const auto& callback : local) {
       ftl::WeakPtr<DartState> dart_state = callback.dart_state();
       if (!dart_state.get())
@@ -42,13 +45,19 @@
       Dart_Handle result = DartInvokeVoid(callback.value());
       DartErrorHandleType error = GetErrorHandleType(result);
       if (error != kNoError)
-        g_last_error = error;
+        last_error_ = error;
     }
   }
 }
 
+void DartMicrotaskQueue::Destroy() {
+  FTL_DCHECK(g_queue);
+  delete g_queue;
+  g_queue = nullptr;
+}
+
 DartErrorHandleType DartMicrotaskQueue::GetLastError() {
-  return g_last_error;
+  return last_error_;
 }
 
 }
diff --git a/dart_microtask_queue.h b/dart_microtask_queue.h
index 34b4db4..a759daf 100644
--- a/dart_microtask_queue.h
+++ b/dart_microtask_queue.h
@@ -5,16 +5,33 @@
 #ifndef LIB_TONIC_DART_MICROTASK_QUEUE_H_
 #define LIB_TONIC_DART_MICROTASK_QUEUE_H_
 
+#include <vector>
+
 #include "dart/runtime/include/dart_api.h"
+#include "lib/tonic/dart_persistent_value.h"
 #include "lib/tonic/logging/dart_error.h"
 
 namespace tonic {
 
 class DartMicrotaskQueue {
  public:
-  static void ScheduleMicrotask(Dart_Handle callback);
-  static void RunMicrotasks();
-  static DartErrorHandleType GetLastError();
+  static void StartForCurrentThread();
+  static DartMicrotaskQueue* GetForCurrentThread();
+
+  void ScheduleMicrotask(Dart_Handle callback);
+  void RunMicrotasks();
+  void Destroy();
+
+  DartErrorHandleType GetLastError();
+
+ private:
+  DartMicrotaskQueue();
+  ~DartMicrotaskQueue();
+
+  typedef std::vector<DartPersistentValue> MicrotaskQueue;
+
+  DartErrorHandleType last_error_;
+  MicrotaskQueue queue_;
 };
 
 }  // namespace tonic