Skip to content

Commit 5d273b9

Browse files
committed
8295278: Add parallel class loading tests
Reviewed-by: vlivanov, ccheung
1 parent 172006c commit 5d273b9

File tree

23 files changed

+733
-2
lines changed

23 files changed

+733
-2
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8295278
27+
* @summary Call unlocked version loadClass directly, with another thread calling forName
28+
* One class goes through the ClassLoader path and the other goes through JVM path
29+
* @library /test/lib
30+
* @compile test-classes/A.java ../share/ThreadPrint.java
31+
* @run main/othervm CallLoadClassTest
32+
*/
33+
34+
import jdk.test.lib.classloader.ClassUnloadCommon;
35+
import java.util.concurrent.Semaphore;
36+
37+
public class CallLoadClassTest {
38+
39+
private static Semaphore mainSync = null;
40+
41+
private static class MyLoader extends ClassLoader {
42+
43+
ClassLoader parent;
44+
int count;
45+
46+
MyLoader(ClassLoader parent) {
47+
this.parent = parent;
48+
this.count = 0;
49+
}
50+
51+
public synchronized Class<?> loadClass(String name) throws ClassNotFoundException {
52+
Class<?> loadedClass = findLoadedClass(name);
53+
if (name.equals("A") && loadedClass == null) {
54+
ThreadPrint.println("Loading A");
55+
if (count == 0) {
56+
count++;
57+
ThreadPrint.println("Waiting for A");
58+
try {
59+
mainSync.release(); // Let t2 start
60+
wait(); // let the other thread load A instead.
61+
} catch (InterruptedException ie) {
62+
}
63+
} else {
64+
notify(); // notify any waiting threads.
65+
}
66+
byte[] classfile = ClassUnloadCommon.getClassData("A");
67+
return defineClass(name, classfile, 0, classfile.length);
68+
} else {
69+
return parent.loadClass(name);
70+
}
71+
}
72+
}
73+
74+
private static ClassLoadingThread[] threads = new ClassLoadingThread[2];
75+
private static boolean success = true;
76+
77+
private static boolean report_success() {
78+
for (int i = 0; i < 2; i++) {
79+
try {
80+
threads[i].join();
81+
if (!threads[i].report_success()) success = false;
82+
} catch (InterruptedException e) {}
83+
}
84+
return success;
85+
}
86+
87+
public static void main(java.lang.String[] unused) {
88+
mainSync = new Semaphore(0);
89+
90+
// t1 does loadClass directly, t2 does class.ForName()
91+
ClassLoader appLoader = CallLoadClassTest.class.getClassLoader();
92+
MyLoader ldr = new MyLoader(appLoader);
93+
for (int i = 0; i < 2; i++) {
94+
threads[i] = new ClassLoadingThread(ldr, i, mainSync);
95+
threads[i].setName("Loading Thread #" + (i + 1));
96+
threads[i].start();
97+
System.out.println("Thread " + (i + 1) + " was started...");
98+
}
99+
if (report_success()) {
100+
System.out.println("PASSED");
101+
} else {
102+
throw new RuntimeException("FAILED");
103+
}
104+
}
105+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.util.concurrent.Semaphore;
25+
26+
class ClassLoadingThread extends Thread {
27+
28+
private ClassLoader ldr = null;
29+
private int which;
30+
private Semaphore syncOrder;
31+
32+
public ClassLoadingThread(ClassLoader loader, int i, Semaphore sem) {
33+
ldr = loader;
34+
which = i;
35+
syncOrder = sem;
36+
}
37+
38+
private boolean success = true;
39+
public boolean report_success() { return success; }
40+
41+
public void callForName() {
42+
try {
43+
ThreadPrint.println("Starting forName thread ...");
44+
// Initiate class loading using specified type
45+
Class<?> a = Class.forName("A", true, ldr);
46+
Object obj = a.getConstructor().newInstance();
47+
} catch (Throwable e) {
48+
ThreadPrint.println("Exception is caught: " + e);
49+
e.printStackTrace();
50+
success = false;
51+
}
52+
}
53+
54+
public void callLoadClass() {
55+
try {
56+
ThreadPrint.println("Starting loadClass thread ...");
57+
Class<?> a = ldr.loadClass("A");
58+
Object obj = a.getConstructor().newInstance();
59+
success = false; // Should have thrown LinkageError
60+
} catch (Throwable e) {
61+
// If you call loadClass directly, this will result in LinkageError
62+
ThreadPrint.println("Exception is caught: " + e);
63+
e.printStackTrace();
64+
success = (e instanceof LinkageError);
65+
}
66+
}
67+
68+
public void run() {
69+
if (which == 0) {
70+
callLoadClass();
71+
} else {
72+
try {
73+
syncOrder.acquire(); // wait until loadClass is waiting.
74+
} catch (InterruptedException idc) {}
75+
callForName();
76+
}
77+
}
78+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
// This doesn't have to do anything
25+
public class A {
26+
static { System.out.println("A called"); }
27+
public A() { System.out.println("A.<init> called"); }
28+
}
29+
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 4699981 8295278
27+
* @summary This is a testcase for JDK-4699981. When 2 threads are loading the same class with
28+
* the same classloader, and somehow one of the 2 threads releases the
29+
* synchronization lock on the classloader, the JVM code
30+
* throws ClassCircularityError, mistakenly.
31+
* @library /test/lib
32+
* @compile test-classes/Base.java test-classes/Derived.java test-classes/Support.java
33+
* @run main/othervm ParallelCircularityTest
34+
*/
35+
36+
import java.net.URL;
37+
import java.net.URLClassLoader;
38+
39+
public class ParallelCircularityTest {
40+
41+
private Object lock = new Object();
42+
43+
private void test() throws Exception {
44+
URL location = getClass().getProtectionDomain().getCodeSource().getLocation();
45+
URLLoader loader = new URLLoader(new URL[] {location}, getClass().getClassLoader().getParent());
46+
47+
Class cls = loader.loadClass("Support");
48+
49+
Thread t1 = new Thread(new Run1(cls));
50+
t1.start();
51+
52+
Thread.sleep(1000);
53+
54+
// Load Derived, will trigger a loadClassInternal for Base
55+
loader.loadClass("Derived");
56+
}
57+
58+
public static void main(String[] args) throws Exception {
59+
ParallelCircularityTest pct = new ParallelCircularityTest();
60+
pct.test();
61+
}
62+
63+
public class URLLoader extends URLClassLoader {
64+
private boolean m_firstTime = true;
65+
66+
public URLLoader(URL[] urls, ClassLoader parent) {
67+
super(urls, parent);
68+
}
69+
70+
public Class loadClass(String name) throws ClassNotFoundException {
71+
if (name.equals("Base")) {
72+
if (m_firstTime) {
73+
m_firstTime = false;
74+
75+
// Notify the other thread
76+
synchronized (lock) {
77+
lock.notifyAll();
78+
}
79+
80+
// Wait on the classloader to have the JVM throw ClassCircularityError
81+
try {
82+
synchronized (this) {
83+
wait(5000);
84+
}
85+
}
86+
catch (InterruptedException ignored) { }
87+
}
88+
}
89+
return super.loadClass(name);
90+
}
91+
}
92+
93+
public class Run1 implements Runnable {
94+
private Class cls;
95+
96+
public Run1(Class cls) {
97+
this.cls = cls;
98+
}
99+
100+
public void run() {
101+
synchronized (lock) {
102+
try {
103+
lock.wait();
104+
}
105+
catch (InterruptedException ignored) {}
106+
}
107+
108+
// Trigger loadClassInternal for Base
109+
try {
110+
cls.newInstance();
111+
} catch (Throwable x) {
112+
x.printStackTrace();
113+
}
114+
}
115+
}
116+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
public class Base {}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
public class Derived extends Base {}

0 commit comments

Comments
 (0)