8231125: Improve testing of parallel loading of shared classes by the boot class loader
authoriklam
Sun, 22 Sep 2019 17:16:35 -0700
changeset 58260 704e4ff399a2
parent 58252 14c1ff687621
child 58261 01c5971b0a2c
8231125: Improve testing of parallel loading of shared classes by the boot class loader Reviewed-by: ccheung, coleenp, dholmes
src/hotspot/share/classfile/systemDictionary.cpp
src/hotspot/share/oops/klass.hpp
test/hotspot/jtreg/runtime/cds/appcds/ParallelLoadTest.java
test/hotspot/jtreg/runtime/cds/appcds/test-classes/ParallelLoad.java
--- a/src/hotspot/share/classfile/systemDictionary.cpp	Sat Sep 21 10:13:25 2019 -0400
+++ b/src/hotspot/share/classfile/systemDictionary.cpp	Sun Sep 22 17:16:35 2019 -0700
@@ -1270,6 +1270,7 @@
                                                    TRAPS) {
 
   if (ik != NULL) {
+    assert(!ik->is_unshareable_info_restored(), "shared class can be loaded only once");
     Symbol* class_name = ik->name();
 
     bool visible = is_shared_class_visible(
--- a/src/hotspot/share/oops/klass.hpp	Sat Sep 21 10:13:25 2019 -0400
+++ b/src/hotspot/share/oops/klass.hpp	Sun Sep 22 17:16:35 2019 -0700
@@ -524,6 +524,18 @@
   virtual void remove_java_mirror();
   virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS);
 
+  bool is_unshareable_info_restored() const {
+    assert(is_shared(), "use this for shared classes only");
+    if (has_raw_archived_mirror()) {
+      // _java_mirror is not a valid OopHandle but rather an encoded reference in the shared heap
+      return false;
+    } else if (_java_mirror.ptr_raw() == NULL) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
  public:
   // subclass accessor (here for convenience; undefined for non-klass objects)
   virtual bool is_leaf_class() const { fatal("not a class"); return false; }
--- a/test/hotspot/jtreg/runtime/cds/appcds/ParallelLoadTest.java	Sat Sep 21 10:13:25 2019 -0400
+++ b/test/hotspot/jtreg/runtime/cds/appcds/ParallelLoadTest.java	Sun Sep 22 17:16:35 2019 -0700
@@ -36,10 +36,34 @@
 public class ParallelLoadTest {
     public static final int MAX_CLASSES = 40;
 
+    /* For easy stress testing, do this:
+
+       i=0; while jtreg -DParallelLoadTest.app.loops=100 -DParallelLoadTest.boot.loops=100 \
+           ParallelLoadTest.java; do i=$(expr $i + 1); echo =====$i; done
+
+     */
+
+    private static final int APP_LOOPS  = Integer.parseInt(System.getProperty("ParallelLoadTest.app.loops", "1"));
+    private static final int BOOT_LOOPS = Integer.parseInt(System.getProperty("ParallelLoadTest.boot.loops", "1"));
+
     public static void main(String[] args) throws Exception {
         JarBuilder.build("parallel_load", getClassList(true));
-        String appJar = TestCommon.getTestJar("parallel_load.jar");
-        TestCommon.test(appJar, getClassList(false), "ParallelLoad");
+        String testJar = TestCommon.getTestJar("parallel_load.jar");
+
+        // (1) Load the classes from app class loader
+        TestCommon.testDump(testJar, getClassList(false));
+        for (int i = 0; i < APP_LOOPS; i++) {
+            TestCommon.run("-cp", testJar,  "ParallelLoad").assertNormalExit();
+        }
+
+        // (2) Load the classes from boot class loader
+        String bootcp = "-Xbootclasspath/a:" + testJar;
+        TestCommon.testDump(null, getClassList(false), bootcp);
+        for (int i = 0; i < BOOT_LOOPS; i++) {
+            TestCommon.run(bootcp,
+                           // "-Xlog:class+load=debug",
+                           "ParallelLoad").assertNormalExit();
+        }
     }
 
     private static String[] getClassList(boolean includeWatchdog) {
@@ -47,7 +71,7 @@
         String[] classList = new String[MAX_CLASSES + extra];
 
         int i;
-        for (i=0; i<MAX_CLASSES; i++) {
+        for (i = 0; i < MAX_CLASSES; i++) {
             classList[i] = "ParallelClass" + i;
         }
 
--- a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/ParallelLoad.java	Sat Sep 21 10:13:25 2019 -0400
+++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/ParallelLoad.java	Sun Sep 22 17:16:35 2019 -0700
@@ -25,6 +25,7 @@
 import java.io.*;
 import java.net.*;
 import java.lang.reflect.Field;
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 // This test helper is parameterized by:
@@ -78,7 +79,7 @@
             if ("FINGERPRINT_MODE".equals(args[1])) {
                 mode = FINGERPRINT_MODE;
                 classLoaders = new ClassLoader[NUM_THREADS];
-                for (int i=0; i<NUM_THREADS; i++) {
+                for (int i = 0; i < NUM_THREADS; i++) {
                     URL url = new File(customJar).toURI().toURL();
                     URL[] urls = new URL[] {url};
                     classLoaders[i] = new URLClassLoader(urls);
@@ -93,7 +94,7 @@
         System.out.println("Start Parallel Load ...");
 
         Thread thread[] = new Thread[NUM_THREADS];
-        for (int i=0; i<NUM_THREADS; i++) {
+        for (int i = 0; i < NUM_THREADS; i++) {
             Thread t = new ParallelLoadThread(i);
             t.start();
             thread[i] = t;
@@ -103,7 +104,7 @@
         watchdog.setDaemon(true);
         watchdog.start();
 
-        for (int i=0; i<NUM_THREADS; i++) {
+        for (int i = 0; i < NUM_THREADS; i++) {
             thread[i].join();
         }
         System.out.println("Parallel Load ... done");
@@ -128,8 +129,13 @@
 
 
 class ParallelLoadThread extends Thread {
-    static int num_ready[] = new int[ParallelLoad.MAX_CLASSES];
-    static Object lock = new Object();
+    static AtomicInteger num_ready[];
+    static {
+        num_ready = new AtomicInteger[ParallelLoad.MAX_CLASSES];
+        for (int i = 0; i < ParallelLoad.MAX_CLASSES; i++) {
+            num_ready[i] = new AtomicInteger();
+        }
+    }
     static String transformMode =
         System.getProperty("appcds.parallel.transform.mode", "none");
 
@@ -153,35 +159,36 @@
     }
 
     private void run0() throws Throwable {
-        for (int i=0; i<ParallelLoad.MAX_CLASSES; i++) {
-            synchronized(lock) {
-                num_ready[i] ++;
-                while (num_ready[i] < ParallelLoad.NUM_THREADS) {
-                    lock.wait();
-                }
-                lock.notifyAll();
+        for (int i = 0;  i < ParallelLoad.MAX_CLASSES; i++) {
+            String className = "ParallelClass" + i;
+            if (transformMode.equals("cflh")) {
+                className = "ParallelClassTr" + i;
             }
-            log("this = %s %d", this, i);
-            String className = "ParallelClass" + i;
-            if (transformMode.equals("cflh"))
-                className = "ParallelClassTr" + i;
-
             Class clazz = null;
 
-            switch (ParallelLoad.loaderType) {
-            case ParallelLoad.SYSTEM_LOADER:
-                clazz = Class.forName(className);
-                break;
-            case ParallelLoad.SINGLE_CUSTOM_LOADER:
-                clazz = ParallelLoad.classLoaders[0].loadClass(className);
-                break;
-            case ParallelLoad.MULTI_CUSTOM_LOADER:
-                clazz = ParallelLoad.classLoaders[thread_id].loadClass(className);
-                break;
+            // Spin until every thread is ready to proceed
+            num_ready[i].incrementAndGet();
+            while (num_ready[i].intValue() < ParallelLoad.NUM_THREADS) {
+                ;
             }
 
-            log("clazz = %s", clazz);
-            testTransformation(clazz);
+            {   // Avoid logging in this block so the threads can proceed without
+                // waiting for the stdout lock, etc.
+                switch (ParallelLoad.loaderType) {
+                case ParallelLoad.SYSTEM_LOADER:
+                    clazz = Class.forName(className);
+                    break;
+                case ParallelLoad.SINGLE_CUSTOM_LOADER:
+                    clazz = ParallelLoad.classLoaders[0].loadClass(className);
+                    break;
+                case ParallelLoad.MULTI_CUSTOM_LOADER:
+                    clazz = ParallelLoad.classLoaders[thread_id].loadClass(className);
+                    break;
+                }
+                testTransformation(clazz);
+            }
+
+            log("thread[%d] t = %s, c = %s, l = %s", thread_id, this, clazz, clazz.getClassLoader());
         }
     }