JDWP: update thread synchronization
This CL ensures only one thread can do JDWP stuff at a time: either
processing a command coming from the debugger (JDWP thread) or
sending an event (breakpoint, class prepare, etc) to the debugger
before suspending.
The JDWP thread now uses AcquireJdwpTokenForCommand and
ReleaseJdwpTokenForCommand, respectively acquiring and releasing the
token used for synchronization. On the other hand, the event threads
now use AcquireJdwpTokenForEvent and ReleaseJdwpTokenForEvent.
During an invoke, the target thread needs to take the JDWP token to
execute the method while it's already held by the JDWP handler thread
waiting for the invocation to complete. To avoid both threads from
waiting for each other (deadlock), the JDWP thread releases the token
and acquires it again after the invocation is complete, so the target
thread can run safely and prevents other threads from sending events.
Bug: 19120467
Change-Id: Ie3208fb940a60573769d494128cf22f0fa30fa61
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 678b29a..8de0478 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -604,7 +604,7 @@
}
void Dbg::ClearWaitForEventThread() {
- gJdwpState->ClearWaitForEventThread();
+ gJdwpState->ReleaseJdwpTokenForEvent();
}
void Dbg::Connected() {
@@ -3637,6 +3637,11 @@
thread_list->Resume(targetThread, true);
}
+ // The target thread is resumed but needs the JDWP token we're holding.
+ // We release it now and will acquire it again when the invocation is
+ // complete and the target thread suspends itself.
+ gJdwpState->ReleaseJdwpTokenForCommand();
+
// Wait for the request to finish executing.
while (req->invoke_needed) {
req->cond.Wait(self);
@@ -3646,6 +3651,10 @@
/* wait for thread to re-suspend itself */
SuspendThread(thread_id, false /* request_suspension */);
+
+ // Now the thread is suspended again, we can re-acquire the JDWP token.
+ gJdwpState->AcquireJdwpTokenForCommand();
+
self->TransitionFromSuspendedToRunnable();
}
@@ -3653,7 +3662,7 @@
* Suspend the threads. We waited for the target thread to suspend
* itself, so all we need to do is suspend the others.
*
- * The suspendAllThreads() call will double-suspend the event thread,
+ * The SuspendAllForDebugger() call will double-suspend the event thread,
* so we want to resume the target thread once to keep the books straight.
*/
if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {