diff options
author | Sebastien Hertz <shertz@google.com> | 2015-04-08 16:42:38 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-04-08 16:42:39 +0000 |
commit | a2d40be3e64b339c6c39d59655507c597251e506 (patch) | |
tree | 011137a2bb46a6bc56e9b47c0e5c9d7978aa8ace | |
parent | 9d0ab6f0a2f08c3fa9a59e0b8742cf366d7d0feb (diff) | |
parent | 4e5b20863898006ec6c9d120cda167d38dda6e60 (diff) | |
download | art-a2d40be3e64b339c6c39d59655507c597251e506.tar.gz art-a2d40be3e64b339c6c39d59655507c597251e506.tar.bz2 art-a2d40be3e64b339c6c39d59655507c597251e506.zip |
Merge "Fix JDWP race at runtime shutdown"
-rw-r--r-- | runtime/base/mutex.h | 1 | ||||
-rw-r--r-- | runtime/debugger.cc | 12 | ||||
-rw-r--r-- | runtime/debugger.h | 15 | ||||
-rw-r--r-- | runtime/jdwp/jdwp.h | 8 | ||||
-rw-r--r-- | runtime/jdwp/jdwp_handler.cc | 2 | ||||
-rw-r--r-- | runtime/jdwp/jdwp_main.cc | 36 |
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; } |