Allocate interrupted exception before re-acquiring lock
Allocating the monitor exception after acquiring the lock can cause
a deadlock in the following scenario:
Reference queue daemon interrupted from wait() on
ReferenceQueue.class. Tries to allocate exception and starts a GC
that blocks on WaitForGcToComplete.
Some other thread is already doing a GC, and tries to enqueue the
cleared refereneces. This deadlocks trying to lock Reference.class
in ReferenceQueue.add.
Bug: 27508829
Change-Id: Icbc18b6b8d1e906c3f7413810d6cefdda06eb921
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 1ce5841..a262c7a 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -497,6 +497,24 @@
self->SetWaitMonitor(nullptr);
}
+ // Allocate the interrupted exception not holding the monitor lock since it may cause a GC.
+ // If the GC requires acquiring the monitor for enqueuing cleared references, this would
+ // cause a deadlock if the monitor is held.
+ if (was_interrupted && interruptShouldThrow) {
+ /*
+ * We were interrupted while waiting, or somebody interrupted an
+ * un-interruptible thread earlier and we're bailing out immediately.
+ *
+ * The doc sayeth: "The interrupted status of the current thread is
+ * cleared when this exception is thrown."
+ */
+ {
+ MutexLock mu(self, *self->GetWaitMutex());
+ self->SetInterruptedLocked(false);
+ }
+ self->ThrowNewException("Ljava/lang/InterruptedException;", nullptr);
+ }
+
// Re-acquire the monitor and lock.
Lock(self);
monitor_lock_.Lock(self);
@@ -516,21 +534,6 @@
RemoveFromWaitSet(self);
monitor_lock_.Unlock(self);
-
- if (was_interrupted && interruptShouldThrow) {
- /*
- * We were interrupted while waiting, or somebody interrupted an
- * un-interruptible thread earlier and we're bailing out immediately.
- *
- * The doc sayeth: "The interrupted status of the current thread is
- * cleared when this exception is thrown."
- */
- {
- MutexLock mu(self, *self->GetWaitMutex());
- self->SetInterruptedLocked(false);
- }
- self->ThrowNewException("Ljava/lang/InterruptedException;", nullptr);
- }
}
void Monitor::Notify(Thread* self) {