summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastien Hertz <shertz@google.com>2015-04-08 16:42:38 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-04-08 16:42:39 +0000
commita2d40be3e64b339c6c39d59655507c597251e506 (patch)
tree011137a2bb46a6bc56e9b47c0e5c9d7978aa8ace
parent9d0ab6f0a2f08c3fa9a59e0b8742cf366d7d0feb (diff)
parent4e5b20863898006ec6c9d120cda167d38dda6e60 (diff)
downloadart-a2d40be3e64b339c6c39d59655507c597251e506.tar.gz
art-a2d40be3e64b339c6c39d59655507c597251e506.tar.bz2
art-a2d40be3e64b339c6c39d59655507c597251e506.zip
Merge "Fix JDWP race at runtime shutdown"
-rw-r--r--runtime/base/mutex.h1
-rw-r--r--runtime/debugger.cc12
-rw-r--r--runtime/debugger.h15
-rw-r--r--runtime/jdwp/jdwp.h8
-rw-r--r--runtime/jdwp/jdwp_handler.cc2
-rw-r--r--runtime/jdwp/jdwp_main.cc36
6 files changed, 55 insertions, 19 deletions
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 6e7b04fc93..af008347cd 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -97,6 +97,7 @@ enum LockLevel {
kAllocTrackerLock,
kDeoptimizationLock,
kProfilerLock,
+ kJdwpShutdownLock,
kJdwpEventListLock,
kJdwpAttachLock,
kJdwpStartLock,
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 6759c4d9c3..a909a1afbe 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -307,7 +307,6 @@ static JDWP::JdwpOptions gJdwpOptions;
// Runtime JDWP state.
static JDWP::JdwpState* gJdwpState = nullptr;
static bool gDebuggerConnected; // debugger or DDMS is connected.
-static bool gDisposed; // debugger called VirtualMachine.Dispose, so we should drop the connection.
static bool gDdmThreadNotification = false;
@@ -319,6 +318,7 @@ static Dbg::HpsgWhen gDdmNhsgWhen = Dbg::HPSG_WHEN_NEVER;
static Dbg::HpsgWhat gDdmNhsgWhat;
bool Dbg::gDebuggerActive = false;
+bool Dbg::gDisposed = false;
ObjectRegistry* Dbg::gRegistry = nullptr;
// Recent allocation tracking.
@@ -551,7 +551,7 @@ void Dbg::StopJdwp() {
gJdwpState->PostVMDeath();
}
// Prevent the JDWP thread from processing JDWP incoming packets after we close the connection.
- Disposed();
+ Dispose();
delete gJdwpState;
gJdwpState = nullptr;
delete gRegistry;
@@ -599,14 +599,6 @@ void Dbg::Connected() {
gDisposed = false;
}
-void Dbg::Disposed() {
- gDisposed = true;
-}
-
-bool Dbg::IsDisposed() {
- return gDisposed;
-}
-
bool Dbg::RequiresDeoptimization() {
// We don't need deoptimization if everything runs with interpreter after
// enabling -Xint mode.
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 5898784c43..dd7f9c56fa 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -239,7 +239,9 @@ class Dbg {
static void GoActive()
LOCKS_EXCLUDED(Locks::breakpoint_lock_, Locks::deoptimization_lock_, Locks::mutator_lock_);
static void Disconnected() LOCKS_EXCLUDED(Locks::deoptimization_lock_, Locks::mutator_lock_);
- static void Disposed();
+ static void Dispose() {
+ gDisposed = true;
+ }
// Returns true if we're actually debugging with a real debugger, false if it's
// just DDMS (or nothing at all).
@@ -255,9 +257,12 @@ class Dbg {
// Returns true if a method has any breakpoints.
static bool MethodHasAnyBreakpoints(mirror::ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::breakpoint_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ LOCKS_EXCLUDED(Locks::breakpoint_lock_);
- static bool IsDisposed();
+ static bool IsDisposed() {
+ return gDisposed;
+ }
/*
* Time, in milliseconds, since the last debugger activity. Does not
@@ -756,6 +761,10 @@ class Dbg {
// Indicates whether the debugger is making requests.
static bool gDebuggerActive;
+ // Indicates whether we should drop the JDWP connection because the runtime stops or the
+ // debugger called VirtualMachine.Dispose.
+ static bool gDisposed;
+
// The registry mapping objects to JDWP ids.
static ObjectRegistry* gRegistry;
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index e16221c69a..31c9a0bb4e 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -403,6 +403,14 @@ struct JdwpState {
// Used for VirtualMachine.Exit command handling.
bool should_exit_;
int exit_status_;
+
+ // Used to synchronize runtime shutdown with JDWP command handler thread.
+ // When the runtime shuts down, it needs to stop JDWP command handler thread by closing the
+ // JDWP connection. However, if the JDWP thread is processing a command, it needs to wait
+ // for the command to finish so we can send its reply before closing the connection.
+ Mutex shutdown_lock_ ACQUIRED_AFTER(event_list_lock_);
+ ConditionVariable shutdown_cond_ GUARDED_BY(shutdown_lock_);
+ bool processing_request_ GUARDED_BY(shutdown_lock_);
};
std::string DescribeField(const FieldId& field_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0d161bc100..d0ca214ee4 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -271,7 +271,7 @@ static JdwpError VM_IDSizes(JdwpState*, Request*, ExpandBuf* pReply)
static JdwpError VM_Dispose(JdwpState*, Request*, ExpandBuf*)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Dbg::Disposed();
+ Dbg::Dispose();
return ERR_NONE;
}
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index e2b88a5e79..5b30f0cd8c 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -126,6 +126,7 @@ void JdwpNetStateBase::Close() {
*/
ssize_t JdwpNetStateBase::WritePacket(ExpandBuf* pReply, size_t length) {
MutexLock mu(Thread::Current(), socket_lock_);
+ DCHECK(IsConnected()) << "Connection with debugger is closed";
DCHECK_LE(length, expandBufGetLength(pReply));
return TEMP_FAILURE_RETRY(write(clientSock, expandBufGetBuffer(pReply), length));
}
@@ -140,6 +141,7 @@ ssize_t JdwpNetStateBase::WriteBufferedPacket(const std::vector<iovec>& iov) {
ssize_t JdwpNetStateBase::WriteBufferedPacketLocked(const std::vector<iovec>& iov) {
socket_lock_.AssertHeld(Thread::Current());
+ DCHECK(IsConnected()) << "Connection with debugger is closed";
return TEMP_FAILURE_RETRY(writev(clientSock, &iov[0], iov.size()));
}
@@ -225,7 +227,10 @@ JdwpState::JdwpState(const JdwpOptions* options)
jdwp_token_owner_thread_id_(0),
ddm_is_active_(false),
should_exit_(false),
- exit_status_(0) {
+ exit_status_(0),
+ shutdown_lock_("JDWP shutdown lock", kJdwpShutdownLock),
+ shutdown_cond_("JDWP shutdown condition variable", shutdown_lock_),
+ processing_request_(false) {
}
/*
@@ -338,10 +343,20 @@ void JdwpState::ResetState() {
JdwpState::~JdwpState() {
if (netState != nullptr) {
/*
- * Close down the network to inspire the thread to halt.
+ * Close down the network to inspire the thread to halt. If a request is being processed,
+ * we need to wait for it to finish first.
*/
- VLOG(jdwp) << "JDWP shutting down net...";
- netState->Shutdown();
+ {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, shutdown_lock_);
+ while (processing_request_) {
+ VLOG(jdwp) << "JDWP command in progress: wait for it to finish ...";
+ shutdown_cond_.Wait(self);
+ }
+
+ VLOG(jdwp) << "JDWP shutting down net...";
+ netState->Shutdown();
+ }
if (debug_thread_started_) {
run = false;
@@ -369,7 +384,13 @@ bool JdwpState::IsActive() {
// Returns "false" if we encounter a connection-fatal error.
bool JdwpState::HandlePacket() {
- JdwpNetStateBase* netStateBase = reinterpret_cast<JdwpNetStateBase*>(netState);
+ Thread* const self = Thread::Current();
+ {
+ MutexLock mu(self, shutdown_lock_);
+ processing_request_ = true;
+ }
+ JdwpNetStateBase* netStateBase = netState;
+ CHECK(netStateBase != nullptr) << "Connection has been closed";
JDWP::Request request(netStateBase->input_buffer_, netStateBase->input_count_);
ExpandBuf* pReply = expandBufAlloc();
@@ -388,6 +409,11 @@ bool JdwpState::HandlePacket() {
}
expandBufFree(pReply);
netStateBase->ConsumeBytes(request.GetLength());
+ {
+ MutexLock mu(self, shutdown_lock_);
+ processing_request_ = false;
+ shutdown_cond_.Broadcast(self);
+ }
return true;
}