Implement asynchronous exception support

This CL implements async exception support in the switch interpreter.
It also adds support for the MTerp to detect and switch back to the
switch interpreter in cases where an async exception is detected.

Tests follow in next CL.

Test: ./test.py --host -j50

Bug: 62821960
Bug: 34415266

Change-Id: Idb53711a40c20f962de8aa6b74662676b8bd25c6
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 281dfd9..7c912d0 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -139,7 +139,8 @@
                         sizeof(void*) * kLockLevelCount);
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, flip_function, method_verifier, sizeof(void*));
     EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, method_verifier, thread_local_mark_stack, sizeof(void*));
-    EXPECT_OFFSET_DIFF(Thread, tlsPtr_.thread_local_mark_stack, Thread, wait_mutex_, sizeof(void*),
+    EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_mark_stack, async_exception, sizeof(void*));
+    EXPECT_OFFSET_DIFF(Thread, tlsPtr_.async_exception, Thread, wait_mutex_, sizeof(void*),
                        thread_tlsptr_end);
   }
 
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index c345013..5938113 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -594,6 +594,10 @@
                          uint16_t inst_data,
                          JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Make sure to check for async exceptions
+  if (UNLIKELY(self->ObserveAsyncException())) {
+    return false;
+  }
   // Invoke-polymorphic instructions always take a receiver. i.e, they are never static.
   const uint32_t vRegC = (is_range) ? inst->VRegC_4rcc() : inst->VRegC_45cc();
   const int invoke_method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc();
@@ -899,6 +903,10 @@
                     uint16_t inst_data,
                     JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_) {
+  // Make sure to check for async exceptions
+  if (UNLIKELY(self->ObserveAsyncException())) {
+    return false;
+  }
   // invoke-custom is not supported in transactions. In transactions
   // there is a limited set of types supported. invoke-custom allows
   // running arbitrary code and instantiating arbitrary types.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 50bd7e7..f32e2f7 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -169,6 +169,10 @@
                             const Instruction* inst,
                             uint16_t inst_data,
                             JValue* result) {
+  // Make sure to check for async exceptions before anything else.
+  if (UNLIKELY(self->ObserveAsyncException())) {
+    return false;
+  }
   const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
   const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
   ObjPtr<mirror::Object> receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 850419b..a524e9d 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -102,6 +102,13 @@
     }                                                                                          \
   } while (false)
 
+#define HANDLE_ASYNC_EXCEPTION()                                                               \
+  if (UNLIKELY(self->ObserveAsyncException())) {                                               \
+    HANDLE_PENDING_EXCEPTION();                                                                \
+    break;                                                                                     \
+  }                                                                                            \
+  do {} while (false)
+
 #define HANDLE_BACKWARD_BRANCH(offset)                                                         \
   do {                                                                                         \
     if (IsBackwardBranch(offset)) {                                                            \
@@ -527,6 +534,7 @@
       }
       case Instruction::MONITOR_ENTER: {
         PREAMBLE();
+        HANDLE_ASYNC_EXCEPTION();
         ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         if (UNLIKELY(obj == nullptr)) {
           ThrowNullPointerExceptionFromInterpreter();
@@ -539,6 +547,7 @@
       }
       case Instruction::MONITOR_EXIT: {
         PREAMBLE();
+        HANDLE_ASYNC_EXCEPTION();
         ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         if (UNLIKELY(obj == nullptr)) {
           ThrowNullPointerExceptionFromInterpreter();
@@ -686,6 +695,7 @@
       }
       case Instruction::THROW: {
         PREAMBLE();
+        HANDLE_ASYNC_EXCEPTION();
         ObjPtr<mirror::Object> exception =
             shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         if (UNLIKELY(exception == nullptr)) {
@@ -704,6 +714,7 @@
       }
       case Instruction::GOTO: {
         PREAMBLE();
+        HANDLE_ASYNC_EXCEPTION();
         int8_t offset = inst->VRegA_10t(inst_data);
         BRANCH_INSTRUMENTATION(offset);
         inst = inst->RelativeAt(offset);
@@ -712,6 +723,7 @@
       }
       case Instruction::GOTO_16: {
         PREAMBLE();
+        HANDLE_ASYNC_EXCEPTION();
         int16_t offset = inst->VRegA_20t();
         BRANCH_INSTRUMENTATION(offset);
         inst = inst->RelativeAt(offset);
@@ -720,6 +732,7 @@
       }
       case Instruction::GOTO_32: {
         PREAMBLE();
+        HANDLE_ASYNC_EXCEPTION();
         int32_t offset = inst->VRegA_30t();
         BRANCH_INSTRUMENTATION(offset);
         inst = inst->RelativeAt(offset);
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 6c24753..2318125 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -145,9 +145,14 @@
 
 extern "C" size_t MterpShouldSwitchInterpreters()
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  const instrumentation::Instrumentation* const instrumentation =
-      Runtime::Current()->GetInstrumentation();
-  return instrumentation->NonJitProfilingActive() || Dbg::IsDebuggerActive();
+  const Runtime* const runtime = Runtime::Current();
+  const instrumentation::Instrumentation* const instrumentation = runtime->GetInstrumentation();
+  return instrumentation->NonJitProfilingActive() ||
+      Dbg::IsDebuggerActive() ||
+      // An async exception has been thrown. We need to go to the switch interpreter. MTerp doesn't
+      // know how to deal with these so we could end up never dealing with it if we are in an
+      // infinite loop.
+      UNLIKELY(Thread::Current()->IsAsyncExceptionPending());
 }
 
 
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index d85479a..7823413 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1143,7 +1143,7 @@
   Handle<mirror::Object> h_obj(hs.NewHandle(obj));
 
   Runtime::Current()->GetRuntimeCallbacks()->ObjectWaitStart(h_obj, ms);
-  if (UNLIKELY(self->IsExceptionPending())) {
+  if (UNLIKELY(self->ObserveAsyncException() || self->IsExceptionPending())) {
     // See b/65558434 for information on handling of exceptions here.
     return;
   }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 968a23b..4a128a5 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3464,6 +3464,10 @@
     visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception),
                        RootInfo(kRootNativeStack, thread_id));
   }
+  if (tlsPtr_.async_exception != nullptr) {
+    visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&tlsPtr_.async_exception),
+                       RootInfo(kRootNativeStack, thread_id));
+  }
   visitor->VisitRootIfNonNull(&tlsPtr_.monitor_enter_object, RootInfo(kRootNativeStack, thread_id));
   tlsPtr_.jni_env->locals.VisitRoots(visitor, RootInfo(kRootJNILocal, thread_id));
   tlsPtr_.jni_env->monitors.VisitRoots(visitor, RootInfo(kRootJNIMonitor, thread_id));
@@ -3683,6 +3687,34 @@
                                               method_type);
 }
 
+void Thread::SetAsyncException(ObjPtr<mirror::Throwable> new_exception) {
+  CHECK(new_exception != nullptr);
+  if (kIsDebugBuild) {
+    // Make sure we are in a checkpoint.
+    MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_);
+    CHECK(this == Thread::Current() || GetSuspendCount() >= 1)
+        << "It doesn't look like this was called in a checkpoint! this: "
+        << this << " count: " << GetSuspendCount();
+  }
+  tlsPtr_.async_exception = new_exception.Ptr();
+}
+
+bool Thread::ObserveAsyncException() {
+  DCHECK(this == Thread::Current());
+  if (tlsPtr_.async_exception != nullptr) {
+    if (tlsPtr_.exception != nullptr) {
+      LOG(WARNING) << "Overwriting pending exception with async exception. Pending exception is: "
+                   << tlsPtr_.exception->Dump();
+      LOG(WARNING) << "Async exception is " << tlsPtr_.async_exception->Dump();
+    }
+    tlsPtr_.exception = tlsPtr_.async_exception;
+    tlsPtr_.async_exception = nullptr;
+    return true;
+  } else {
+    return IsExceptionPending();
+  }
+}
+
 void Thread::SetException(ObjPtr<mirror::Throwable> new_exception) {
   CHECK(new_exception != nullptr);
   // TODO: DCHECK(!IsExceptionPending());
diff --git a/runtime/thread.h b/runtime/thread.h
index 2e4a3da..ab89778 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -401,6 +401,10 @@
     return tlsPtr_.exception != nullptr;
   }
 
+  bool IsAsyncExceptionPending() const {
+    return tlsPtr_.async_exception != nullptr;
+  }
+
   mirror::Throwable* GetException() const REQUIRES_SHARED(Locks::mutator_lock_) {
     return tlsPtr_.exception;
   }
@@ -412,10 +416,24 @@
 
   void SetException(ObjPtr<mirror::Throwable> new_exception) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Set an exception that is asynchronously thrown from a different thread. This will be checked
+  // periodically and might overwrite the current 'Exception'. This can only be called from a
+  // checkpoint.
+  //
+  // The caller should also make sure that the thread has been deoptimized so that the exception
+  // could be detected on back-edges.
+  void SetAsyncException(ObjPtr<mirror::Throwable> new_exception)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   void ClearException() REQUIRES_SHARED(Locks::mutator_lock_) {
     tlsPtr_.exception = nullptr;
   }
 
+  // Move the current async-exception to the main exception. This should be called when the current
+  // thread is ready to deal with any async exceptions. Returns true if there is an async exception
+  // that needs to be dealt with, false otherwise.
+  bool ObserveAsyncException() REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Find catch block and perform long jump to appropriate exception handle
   NO_RETURN void QuickDeliverException() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -1518,7 +1536,8 @@
       thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr),
       mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr),
       thread_local_alloc_stack_end(nullptr),
-      flip_function(nullptr), method_verifier(nullptr), thread_local_mark_stack(nullptr) {
+      flip_function(nullptr), method_verifier(nullptr), thread_local_mark_stack(nullptr),
+      async_exception(nullptr) {
       std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr);
     }
 
@@ -1675,6 +1694,9 @@
 
     // Thread-local mark stack for the concurrent copying collector.
     gc::accounting::AtomicStack<mirror::Object>* thread_local_mark_stack;
+
+    // The pending async-exception or null.
+    mirror::Throwable* async_exception;
   } tlsPtr_;
 
   // Guards the 'wait_monitor_' members.