Add a stress test for notifyStartupCompleted
Call notifyStartupCompleted to stress test the freeing logic for
race conditions. This is done while dex files and app images are
being loaded from another thread.
Test: test/run-test --host 1002
Bug: 135486422
Change-Id: I138586d83db7bc2f8d75f223170f9bf4af5e71eb
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d91f5dc..12fda43 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2840,6 +2840,10 @@
}
}
+void Runtime::ResetStartupCompleted() {
+ startup_completed_.store(false, std::memory_order_seq_cst);
+}
+
void Runtime::NotifyStartupCompleted() {
bool expected = false;
if (!startup_completed_.compare_exchange_strong(expected, true, std::memory_order_seq_cst)) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 7050fd2..6e27a9f 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -868,6 +868,10 @@
load_app_image_startup_cache_ = enabled;
}
+ // Reset the startup completed status so that we can call NotifyStartupCompleted again. Should
+ // only be used for testing.
+ void ResetStartupCompleted();
+
// Notify the runtime that application startup is considered completed. Only has effect for the
// first call.
void NotifyStartupCompleted();
diff --git a/test/1002-notify-startup/src-art/Main.java b/test/1002-notify-startup/src-art/Main.java
index 9a1e442..8951af8 100644
--- a/test/1002-notify-startup/src-art/Main.java
+++ b/test/1002-notify-startup/src-art/Main.java
@@ -14,15 +14,62 @@
* limitations under the License.
*/
+import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
-public class Main {
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- System.out.println("Startup completed: " + hasStartupCompleted());
- VMRuntime.getRuntime().notifyStartupCompleted();
- System.out.println("Startup completed: " + hasStartupCompleted());
- }
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.util.concurrent.atomic.AtomicBoolean;
- private static native boolean hasStartupCompleted();
+public class Main {
+ static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/1002-notify-startup.jar";
+ static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+ static AtomicBoolean completed = new AtomicBoolean(false);
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ System.out.println("Startup completed: " + hasStartupCompleted());
+ Thread workerThread = new WorkerThread();
+ workerThread.start();
+ do {
+ resetStartupCompleted();
+ VMRuntime.getRuntime().notifyStartupCompleted();
+ Thread.yield();
+ } while (!completed.get());
+ try {
+ workerThread.join();
+ } catch (Throwable e) {
+ System.err.println(e);
+ }
+ System.out.println("Startup completed: " + hasStartupCompleted());
+ }
+
+ private static class WorkerThread extends Thread {
+ static final int NUM_ITERATIONS = 100;
+
+ private WeakReference<Class<?>> $noinline$loadClassInLoader() throws Exception {
+ ClassLoader loader = new PathClassLoader(
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
+ Class ret = loader.loadClass("Main");
+ return new WeakReference(ret);
+ }
+
+ public void run() {
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ try {
+ WeakReference<Class<?>> ref = $noinline$loadClassInLoader();
+ Runtime.getRuntime().gc();
+ Thread.yield();
+ // Don't validate the unloading since app images will keep classes live (for now).
+ } catch (Throwable e) {
+ System.err.println(e);
+ break;
+ }
+ }
+ completed.set(true);
+ }
+ }
+
+ private static native boolean hasStartupCompleted();
+ private static native void resetStartupCompleted();
}
diff --git a/test/1002-notify-startup/startup_interface.cc b/test/1002-notify-startup/startup_interface.cc
index 8705bb2..c9a238a 100644
--- a/test/1002-notify-startup/startup_interface.cc
+++ b/test/1002-notify-startup/startup_interface.cc
@@ -24,5 +24,9 @@
return Runtime::Current()->GetStartupCompleted();
}
+extern "C" void JNICALL Java_Main_resetStartupCompleted(JNIEnv*, jclass) {
+ Runtime::Current()->ResetStartupCompleted();
+}
+
} // namespace
} // namespace art