Fix a heap lock/thread list lock deadlock.
We had an uncaught OOME whose uncaught exception handler -- running
with the thread lock held -- was trying to cause a GC while some
other thread had the heap lock and was waiting for the thread list
lock.
Change-Id: I22177129562268837127d9edcc63ef5e93054bdf
diff --git a/src/heap.cc b/src/heap.cc
index 73b6640..d9db50b 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -584,6 +584,10 @@
SetIdealFootprint(target_size);
}
+uint32_t Heap::GetLockOwner() {
+ return lock_->GetOwner();
+}
+
void Heap::Lock() {
// Grab the lock, but put ourselves into Thread::kVmWait if it looks
// like we're going to have to wait on the mutex. This prevents
diff --git a/src/heap.h b/src/heap.h
index ffe2abf..ea4901d 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -100,8 +100,8 @@
// Blocks the caller until the garbage collector becomes idle.
static void WaitForConcurrentGcToComplete();
+ static uint32_t GetLockOwner(); // For SignalCatcher.
static void Lock();
-
static void Unlock();
static const std::vector<Space*>& GetSpaces() {
diff --git a/src/mutex.h b/src/mutex.h
index 37d5510..eb90565 100644
--- a/src/mutex.h
+++ b/src/mutex.h
@@ -52,9 +52,10 @@
DCHECK_NE(GetOwner(), GetTid());
}
- private:
pid_t GetOwner();
- pid_t GetTid();
+
+ private:
+ static pid_t GetTid();
std::string name_;
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index a129ba1..06491e4 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -62,7 +62,13 @@
}
void SignalCatcher::HandleSigQuit() {
- Runtime::Current()->GetThreadList()->SuspendAll();
+ Runtime* runtime = Runtime::Current();
+ ThreadList* thread_list = runtime->GetThreadList();
+
+ LOG(INFO) << "Heap lock owner: " << Heap::GetLockOwner() << "\n"
+ << "Thread lock owner: " << thread_list->GetLockOwner() << "\n";
+
+ thread_list->SuspendAll();
std::ostringstream os;
os << "\n"
@@ -75,7 +81,7 @@
os << "Cmd line: " << cmdline << "\n";
}
- Runtime::Current()->Dump(os);
+ runtime->Dump(os);
std::string maps;
if (ReadFileToString("/proc/self/maps", &maps)) {
@@ -84,7 +90,7 @@
os << "----- end " << getpid() << " -----";
- Runtime::Current()->GetThreadList()->ResumeAll();
+ thread_list->ResumeAll();
LOG(INFO) << os.str();
}
diff --git a/src/thread.cc b/src/thread.cc
index d68d1db..af6c29d 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -751,34 +751,9 @@
}
if (peer_ != NULL) {
- Object* group = gThread_group->GetObject(peer_);
-
- // Handle any pending exception.
- if (IsExceptionPending()) {
- // Get and clear the exception.
- Object* exception = GetException();
- ClearException();
-
- // If the thread has its own handler, use that.
- Object* handler = gThread_uncaughtHandler->GetObject(peer_);
- if (handler == NULL) {
- // Otherwise use the thread group's default handler.
- handler = group;
- }
-
- // Call the handler.
- Method* m = handler->GetClass()->FindVirtualMethodForVirtualOrInterface(gUncaughtExceptionHandler_uncaughtException);
- Object* args[2];
- args[0] = peer_;
- args[1] = exception;
- m->Invoke(this, handler, reinterpret_cast<byte*>(&args), NULL);
-
- // If the handler threw, clear that exception too.
- ClearException();
- }
-
// this.group.removeThread(this);
// group can be null if we're in the compiler or a test.
+ Object* group = gThread_group->GetObject(peer_);
if (group != NULL) {
Method* m = group->GetClass()->FindVirtualMethodForVirtualOrInterface(gThreadGroup_removeThread);
Object* args = peer_;
@@ -816,6 +791,35 @@
delete long_jump_context_;
}
+void Thread::HandleUncaughtExceptions() {
+ if (!IsExceptionPending()) {
+ return;
+ }
+
+ ScopedThreadStateChange tsc(this, Thread::kRunnable);
+
+ // Get and clear the exception.
+ Object* exception = GetException();
+ ClearException();
+
+ // If the thread has its own handler, use that.
+ Object* handler = gThread_uncaughtHandler->GetObject(peer_);
+ if (handler == NULL) {
+ // Otherwise use the thread group's default handler.
+ handler = gThread_group->GetObject(peer_);
+ }
+
+ // Call the handler.
+ Method* m = handler->GetClass()->FindVirtualMethodForVirtualOrInterface(gUncaughtExceptionHandler_uncaughtException);
+ Object* args[2];
+ args[0] = peer_;
+ args[1] = exception;
+ m->Invoke(this, handler, reinterpret_cast<byte*>(&args), NULL);
+
+ // If the handler threw, clear that exception too.
+ ClearException();
+}
+
size_t Thread::NumSirtReferences() {
size_t count = 0;
for (StackIndirectReferenceTable* cur = top_sirt_; cur; cur = cur->Link()) {
@@ -1186,13 +1190,22 @@
msg, (throwing_OutOfMemoryError_ ? " (recursive case)" : ""));
if (!throwing_OutOfMemoryError_) {
throwing_OutOfMemoryError_ = true;
- ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
+ ThrowNewException("Ljava/lang/OutOfMemoryError;", NULL);
} else {
SetException(pre_allocated_OutOfMemoryError_);
}
throwing_OutOfMemoryError_ = false;
}
+
+Thread* Thread::CurrentFromGdb() const {
+ return Thread::Current();
+}
+
+void Thread::DumpFromGdb() const {
+ Dump(std::cerr);
+}
+
class CatchBlockStackVisitor : public Thread::StackVisitor {
public:
CatchBlockStackVisitor(Class* to_find, Context* ljc)
diff --git a/src/thread.h b/src/thread.h
index a7097ce..112a1ef 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -457,9 +457,15 @@
void DumpState(std::ostream& os) const;
void DumpStack(std::ostream& os) const;
+ // Out-of-line conveniences for debugging in gdb.
+ Thread* CurrentFromGdb() const; // Like Thread::Current.
+ void DumpFromGdb() const; // Like Thread::Dump(std::cerr).
+
void Attach(const Runtime* runtime);
static void* CreateCallback(void* arg);
+ void HandleUncaughtExceptions();
+
void InitCpu();
void InitFunctionPointers();
void InitTid();
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 7b6d3ba..77ee31a 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -43,6 +43,10 @@
return find(list_.begin(), list_.end(), thread) != list_.end();
}
+uint32_t ThreadList::GetLockOwner() {
+ return thread_list_lock_.GetOwner();
+}
+
void ThreadList::Dump(std::ostream& os) {
MutexLock mu(thread_list_lock_);
os << "DALVIK THREADS (" << list_.size() << "):\n";
@@ -267,6 +271,10 @@
LOG(INFO) << "ThreadList::Unregister() " << *self;
}
+ // This may need to call user-supplied managed code. Make sure we do this before we start tearing
+ // down the Thread* and removing it from the thread list (or start taking any locks).
+ self->HandleUncaughtExceptions();
+
MutexLock mu(thread_list_lock_);
// Remove this thread from the list.
diff --git a/src/thread_list.h b/src/thread_list.h
index 35b23e7..f5e1a3a 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -32,6 +32,7 @@
~ThreadList();
void Dump(std::ostream& os);
+ uint32_t GetLockOwner(); // For SignalCatcher.
// Thread suspension support.
void FullSuspendCheck(Thread* thread);