ART: Add SigQuit Callback
Add callback being triggered when the runtime handles SigQuit.
Bug: 31684920
Test: m test-art-host-gtest-runtime_callbacks_test
Change-Id: I23e3b256c654b6078c79b3897439d893ea79d96e
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 5a5ed75b..0246649 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1552,6 +1552,12 @@
thread_list_->DumpForSigQuit(os);
BaseMutex::DumpAll(os);
+
+ // Inform anyone else who is interested in SigQuit.
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ callbacks_->SigQuit();
+ }
}
void Runtime::DumpLockHolders(std::ostream& os) {
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index ee9edda..cd38ead 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -18,6 +18,7 @@
#include <algorithm>
+#include "base/macros.h"
#include "class_linker.h"
#include "thread.h"
@@ -27,13 +28,19 @@
thread_callbacks_.push_back(cb);
}
-void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
- auto it = std::find(thread_callbacks_.begin(), thread_callbacks_.end(), cb);
- if (it != thread_callbacks_.end()) {
- thread_callbacks_.erase(it);
+template <typename T>
+ALWAYS_INLINE
+static inline void Remove(T* cb, std::vector<T*>* data) {
+ auto it = std::find(data->begin(), data->end(), cb);
+ if (it != data->end()) {
+ data->erase(it);
}
}
+void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+ Remove(cb, &thread_callbacks_);
+}
+
void RuntimeCallbacks::ThreadStart(Thread* self) {
for (ThreadLifecycleCallback* cb : thread_callbacks_) {
cb->ThreadStart(self);
@@ -51,10 +58,7 @@
}
void RuntimeCallbacks::RemoveClassLoadCallback(ClassLoadCallback* cb) {
- auto it = std::find(class_callbacks_.begin(), class_callbacks_.end(), cb);
- if (it != class_callbacks_.end()) {
- class_callbacks_.erase(it);
- }
+ Remove(cb, &class_callbacks_);
}
void RuntimeCallbacks::ClassLoad(Handle<mirror::Class> klass) {
@@ -69,4 +73,18 @@
}
}
+void RuntimeCallbacks::AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) {
+ sigquit_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb) {
+ Remove(cb, &sigquit_callbacks_);
+}
+
+void RuntimeCallbacks::SigQuit() {
+ for (RuntimeSigQuitCallback* cb : sigquit_callbacks_) {
+ cb->SigQuit();
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 5bdb44a..d700cf2d 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -48,6 +48,13 @@
// any state checking (is the listener enabled) in the listener itself. For an example, see
// Dbg.
+class RuntimeSigQuitCallback {
+ public:
+ virtual ~RuntimeSigQuitCallback() {}
+
+ virtual void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
class RuntimeCallbacks {
public:
void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
@@ -63,11 +70,20 @@
void ClassPrepare(Handle<mirror::Class> temp_klass, Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+ void RemoveRuntimeSigQuitCallback(RuntimeSigQuitCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+
+ void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
std::vector<ThreadLifecycleCallback*> thread_callbacks_
GUARDED_BY(Locks::mutator_lock_);
std::vector<ClassLoadCallback*> class_callbacks_
GUARDED_BY(Locks::mutator_lock_);
+ std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
};
} // namespace art
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index f05794d..c96bfd4 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -17,6 +17,9 @@
#include "runtime_callbacks.h"
#include "jni.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <initializer_list>
#include <memory>
@@ -290,4 +293,47 @@
EXPECT_TRUE(expect2);
}
+class RuntimeSigQuitCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+ void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->AddRuntimeSigQuitCallback(&cb_);
+ }
+ void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimeSigQuitCallback(&cb_);
+ }
+
+ struct Callback : public RuntimeSigQuitCallback {
+ void SigQuit() OVERRIDE {
+ ++sigquit_count;
+ }
+
+ size_t sigquit_count = 0;
+ };
+
+ Callback cb_;
+};
+
+TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) {
+ // The runtime needs to be started for the signal handler.
+ Thread* self = Thread::Current();
+
+ self->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ ASSERT_TRUE(started);
+
+ EXPECT_EQ(0u, cb_.sigquit_count);
+
+ kill(getpid(), SIGQUIT);
+
+ // Try a few times.
+ for (size_t i = 0; i != 30; ++i) {
+ if (cb_.sigquit_count == 0) {
+ sleep(1);
+ } else {
+ break;
+ }
+ }
+ EXPECT_EQ(1u, cb_.sigquit_count);
+}
+
} // namespace art