Unload native libraries during shutdown.
Bug: 31550016
Test: two_runtimes_test.cc
Change-Id: Ic44fa57d01d5e00e08b06e3b05c86d44cb45c509
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 56c84a6..ae97907 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -638,6 +638,7 @@
"subtype_check_test.cc",
"thread_pool_test.cc",
"transaction_test.cc",
+ "two_runtimes_test.cc",
"vdex_file_test.cc",
"verifier/method_verifier_test.cc",
"verifier/reg_type_test.cc",
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index df96d28..a0c013c 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -224,6 +224,20 @@
STLDeleteValues(&libraries_);
}
+ void UnloadBootNativeLibraries(JavaVM* vm) {
+ std::vector<SharedLibrary*> unload_libraries;
+ {
+ MutexLock mu(Thread::Current(), *Locks::jni_libraries_lock_);
+ for (auto it = libraries_.begin(); it != libraries_.end(); ++it) {
+ SharedLibrary* const library = it->second;
+ if (library->GetClassLoader() == nullptr) {
+ unload_libraries.push_back(library);
+ }
+ }
+ }
+ UnloadLibraries(vm, unload_libraries);
+ }
+
// NO_THREAD_SAFETY_ANALYSIS since this may be called from Dumpable. Dumpable can't be annotated
// properly due to the template. The caller should be holding the jni_libraries_lock_.
void Dump(std::ostream& os) const NO_THREAD_SAFETY_ANALYSIS {
@@ -337,17 +351,23 @@
}
ScopedThreadSuspension sts(self, kNative);
// Do this without holding the jni libraries lock to prevent possible deadlocks.
- using JNI_OnUnloadFn = void(*)(JavaVM*, void*);
+ UnloadLibraries(self->GetJniEnv()->GetVm(), unload_libraries);
for (auto library : unload_libraries) {
+ delete library;
+ }
+ }
+
+ static void UnloadLibraries(JavaVM* vm, const std::vector<SharedLibrary*>& libraries) {
+ using JNI_OnUnloadFn = void(*)(JavaVM*, void*);
+ for (SharedLibrary* library : libraries) {
void* const sym = library->FindSymbol("JNI_OnUnload", nullptr);
if (sym == nullptr) {
VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]";
} else {
VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling...";
JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym);
- jni_on_unload(self->GetJniEnv()->GetVm(), nullptr);
+ jni_on_unload(vm, nullptr);
}
- delete library;
}
}
@@ -856,6 +876,10 @@
libraries_.get()->UnloadNativeLibraries();
}
+void JavaVMExt::UnloadBootNativeLibraries() {
+ libraries_.get()->UnloadBootNativeLibraries(this);
+}
+
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
diff --git a/runtime/jni/java_vm_ext.h b/runtime/jni/java_vm_ext.h
index 424dd7c..6f7e546 100644
--- a/runtime/jni/java_vm_ext.h
+++ b/runtime/jni/java_vm_ext.h
@@ -109,6 +109,11 @@
REQUIRES(!Locks::jni_libraries_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Unload all boot classpath native libraries.
+ void UnloadBootNativeLibraries()
+ REQUIRES(!Locks::jni_libraries_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
/**
* Returns a pointer to the code for the native method 'm', found
* using dlsym(3) on every native library that's been loaded so far.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 8223fb8..9ab4047 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -394,6 +394,7 @@
WellKnownClasses::java_lang_Daemons_stop);
}
+ // Shutdown any trace running.
Trace::Shutdown();
// Report death. Clients me require a working thread, still, so do it before GC completes and
@@ -431,6 +432,10 @@
thread_list_->ShutDown();
}
+ // We can only unload boot classpath native libraries once all threads are terminated
+ // or suspended.
+ java_vm_->UnloadBootNativeLibraries();
+
// TODO Maybe do some locking.
for (auto& agent : agents_) {
agent->Unload();
diff --git a/runtime/two_runtimes_test.cc b/runtime/two_runtimes_test.cc
new file mode 100644
index 0000000..b52f66a
--- /dev/null
+++ b/runtime/two_runtimes_test.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dexopt_test.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+namespace art {
+
+// This test creates two runtimes consecutively to check that state is
+// setup and cleaned-up correctly each time.
+
+// Make this a DexoptTest, which makes sure runtime images get mapped
+// at random addresses.
+class TwoRuntimesTest : public DexoptTest {};
+
+TEST_F(TwoRuntimesTest, FirstInvocation) {
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ runtime_->Start();
+}
+
+TEST_F(TwoRuntimesTest, SecondInvocation) {
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ runtime_->Start();
+}
+
+} // namespace art
diff --git a/test/150-loadlibrary/expected.txt b/test/150-loadlibrary/expected.txt
index 41feacf..ecf40b6 100644
--- a/test/150-loadlibrary/expected.txt
+++ b/test/150-loadlibrary/expected.txt
@@ -1,2 +1,3 @@
JNI_OnLoad called
Success.
+JNI_OnUnload called