summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/common_runtime_test.cc8
-rw-r--r--runtime/runtime.cc20
-rw-r--r--runtime/runtime_callbacks.cc14
-rw-r--r--runtime/runtime_callbacks.h23
-rw-r--r--runtime/runtime_callbacks_test.cc64
5 files changed, 126 insertions, 3 deletions
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 743fcc87eb..fc82264b6a 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -133,7 +133,9 @@ void ScratchFile::Unlink() {
static bool unstarted_initialized_ = false;
-CommonRuntimeTestImpl::CommonRuntimeTestImpl() {}
+CommonRuntimeTestImpl::CommonRuntimeTestImpl()
+ : class_linker_(nullptr), java_lang_dex_file_(nullptr) {
+}
CommonRuntimeTestImpl::~CommonRuntimeTestImpl() {
// Ensure the dex files are cleaned up before the runtime.
@@ -425,7 +427,9 @@ void CommonRuntimeTestImpl::TearDown() {
TearDownAndroidData(android_data_, true);
dalvik_cache_.clear();
- Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
+ if (runtime_ != nullptr) {
+ runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption after the test
+ }
}
static std::string GetDexFileName(const std::string& jar_prefix, bool host) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 02466499c8..8b355c8c19 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -304,6 +304,13 @@ Runtime::~Runtime() {
Trace::Shutdown();
+ // Report death. Clients me require a working thread, still, so do it before GC completes and
+ // all non-daemon threads are done.
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kDeath);
+ }
+
if (attach_shutdown_thread) {
DetachCurrentThread();
self = nullptr;
@@ -706,6 +713,13 @@ bool Runtime::Start() {
Thread::FinishStartup();
+ // Send the start phase event. We have to wait till here as this is when the main thread peer
+ // has just been generated, important root clinits have been run and JNI is completely functional.
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kStart);
+ }
+
system_class_loader_ = CreateSystemClassLoader(this);
if (!is_zygote_) {
@@ -742,6 +756,12 @@ bool Runtime::Start() {
0);
}
+ // Send the initialized phase event.
+ {
+ ScopedObjectAccess soa(self);
+ callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit);
+ }
+
return true;
}
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index cd38ead2c3..7b15a4f1b5 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -87,4 +87,18 @@ void RuntimeCallbacks::SigQuit() {
}
}
+void RuntimeCallbacks::AddRuntimePhaseCallback(RuntimePhaseCallback* cb) {
+ phase_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb) {
+ Remove(cb, &phase_callbacks_);
+}
+
+void RuntimeCallbacks::NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase) {
+ for (RuntimePhaseCallback* cb : phase_callbacks_) {
+ cb->NextRuntimePhase(phase);
+ }
+}
+
} // namespace art
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index d700cf2d53..6344c69b1f 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -55,6 +55,19 @@ class RuntimeSigQuitCallback {
virtual void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
+class RuntimePhaseCallback {
+ public:
+ enum RuntimePhase {
+ kStart, // The runtime is started.
+ kInit, // The runtime is initialized (and will run user code soon).
+ kDeath, // The runtime just died.
+ };
+
+ virtual ~RuntimePhaseCallback() {}
+
+ virtual void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
class RuntimeCallbacks {
public:
void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
@@ -77,6 +90,14 @@ class RuntimeCallbacks {
void SigQuit() REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddRuntimePhaseCallback(RuntimePhaseCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+ void RemoveRuntimePhaseCallback(RuntimePhaseCallback* cb)
+ REQUIRES(Locks::mutator_lock_);
+
+ void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
std::vector<ThreadLifecycleCallback*> thread_callbacks_
GUARDED_BY(Locks::mutator_lock_);
@@ -84,6 +105,8 @@ class RuntimeCallbacks {
GUARDED_BY(Locks::mutator_lock_);
std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_
GUARDED_BY(Locks::mutator_lock_);
+ std::vector<RuntimePhaseCallback*> phase_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
};
} // namespace art
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index c96bfd4c70..c379b5c267 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -62,7 +62,6 @@ class RuntimeCallbacksTest : public CommonRuntimeTest {
ScopedObjectAccess soa(self);
ScopedThreadSuspension sts(self, kWaitingForDebuggerToAttach);
ScopedSuspendAll ssa("RuntimeCallbacksTest TearDown");
- AddListener();
RemoveListener();
}
@@ -336,4 +335,67 @@ TEST_F(RuntimeSigQuitCallbackRuntimeCallbacksTest, SigQuit) {
EXPECT_EQ(1u, cb_.sigquit_count);
}
+class RuntimePhaseCallbackRuntimeCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+ void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&cb_);
+ }
+ void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&cb_);
+ }
+
+ void TearDown() OVERRIDE {
+ // Bypass RuntimeCallbacksTest::TearDown, as the runtime is already gone.
+ CommonRuntimeTest::TearDown();
+ }
+
+ struct Callback : public RuntimePhaseCallback {
+ void NextRuntimePhase(RuntimePhaseCallback::RuntimePhase p) OVERRIDE {
+ if (p == RuntimePhaseCallback::RuntimePhase::kStart) {
+ if (init_seen > 0) {
+ LOG(FATAL) << "Init seen before start.";
+ }
+ ++start_seen;
+ } else if (p == RuntimePhaseCallback::RuntimePhase::kInit) {
+ ++init_seen;
+ } else if (p == RuntimePhaseCallback::RuntimePhase::kDeath) {
+ ++death_seen;
+ } else {
+ LOG(FATAL) << "Unknown phase " << static_cast<uint32_t>(p);
+ }
+ }
+
+ size_t start_seen = 0;
+ size_t init_seen = 0;
+ size_t death_seen = 0;
+ };
+
+ Callback cb_;
+};
+
+TEST_F(RuntimePhaseCallbackRuntimeCallbacksTest, Phases) {
+ ASSERT_EQ(0u, cb_.start_seen);
+ ASSERT_EQ(0u, cb_.init_seen);
+ ASSERT_EQ(0u, cb_.death_seen);
+
+ // Start the runtime.
+ {
+ Thread* self = Thread::Current();
+ self->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ ASSERT_TRUE(started);
+ }
+
+ ASSERT_EQ(1u, cb_.start_seen);
+ ASSERT_EQ(1u, cb_.init_seen);
+ ASSERT_EQ(0u, cb_.death_seen);
+
+ // Delete the runtime.
+ runtime_.reset();
+
+ ASSERT_EQ(1u, cb_.start_seen);
+ ASSERT_EQ(1u, cb_.init_seen);
+ ASSERT_EQ(1u, cb_.death_seen);
+}
+
} // namespace art