Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8262046: Clean up parallel class loading code and comments #3200

Closed
wants to merge 6 commits into from

Large diffs are not rendered by default.

@@ -46,9 +46,7 @@
// be done concurrently, but only by different loaders.
//
// During loading a placeholder (name, loader) is temporarily placed in
// a side data structure, and is used to detect ClassCircularityErrors
// and to perform verification during GC. A GC can occur in the midst
// of class loading, as we call out to Java, have to take locks, etc.
// a side data structure, and is used to detect ClassCircularityErrors.
//
// When class loading is finished, a new entry is added to the dictionary
// of the class loader and the placeholder is removed. Note that the protection
@@ -58,9 +56,8 @@
// Clients of this class who are interested in finding if a class has
// been completely loaded -- not classes in the process of being loaded --
// can read the dictionary unlocked. This is safe because
// - entries are only deleted at safepoints
// - readers cannot come to a safepoint while actively examining
// an entry (an entry cannot be deleted from under a reader)
// - entries are only deleted when the class loader is not alive, when the
// entire dictionary is deleted.
// - entries must be fully formed before they are available to concurrent
// readers (we must ensure write ordering)
//
@@ -332,21 +329,21 @@ class SystemDictionary : AllStatic {
static Klass* resolve_array_class_or_null(Symbol* class_name,
Handle class_loader,
Handle protection_domain, TRAPS);
static InstanceKlass* handle_parallel_super_load(Symbol* class_name,
Symbol* supername,
Handle class_loader,
Handle protection_domain,
Handle lockObject, TRAPS);
// Wait on SystemDictionary_lock; unlocks lockObject before
// waiting; relocks lockObject with correct recursion count
// after waiting, but before reentering SystemDictionary_lock
// to preserve lock order semantics.
static void double_lock_wait(JavaThread* thread, Handle lockObject);
static InstanceKlass* handle_parallel_loading(JavaThread* current,
unsigned int name_hash,
Symbol* name,
ClassLoaderData* loader_data,
Handle lockObject,
bool* throw_circularity_error);

static void define_instance_class(InstanceKlass* k, Handle class_loader, TRAPS);
static InstanceKlass* find_or_define_helper(Symbol* class_name,
Handle class_loader,
InstanceKlass* k, TRAPS);
static InstanceKlass* load_instance_class(Symbol* class_name, Handle class_loader, TRAPS);
static InstanceKlass* load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS);
static InstanceKlass* load_instance_class(unsigned int name_hash,
Symbol* class_name,
Handle class_loader, TRAPS);

static bool is_shared_class_visible(Symbol* class_name, InstanceKlass* ik,
PackageEntry* pkg_entry,
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import jdk.internal.org.objectweb.asm.*;

public class AsmClasses implements Opcodes {

// class B extends A {} to generate ClassCircularityError
// Can't use jcod, jasm or InMemoryCompiler because the compiler will
// see that A extends B and fail.
public static byte[] dumpB() throws Exception {

ClassWriter classWriter = new ClassWriter(0);
FieldVisitor fieldVisitor;
RecordComponentVisitor recordComponentVisitor;
MethodVisitor methodVisitor;
AnnotationVisitor annotationVisitor0;

classWriter.visit(61, ACC_PUBLIC | ACC_SUPER, "B", null, "A", null);

classWriter.visitSource("B.java", null);
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(24, label0);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "A", "<init>", "()V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
classWriter.visitEnd();

return classWriter.toByteArray();
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

class ClassLoadingThread extends Thread {

private ClassLoader ldr = null;
private Object thread_sync = null;

public ClassLoadingThread(ClassLoader loader, Object sync) {
ldr = loader;
thread_sync = sync;
}

private boolean success = true;
public boolean report_success() { return success; }

public void run() {
try {
ThreadPrint.println("Starting...");
// Initiate class loading using specified type
Class<?> a = Class.forName("ClassInLoader", true, ldr);
Object obj = a.getConstructor().newInstance();

} catch (Throwable e) {
ThreadPrint.println("Exception is caught: " + e);
e.printStackTrace();
success = false;
} finally {
ThreadPrint.println("Finished");
// Wake up the second thread
synchronized (thread_sync) {
thread_sync.notify();
}
}
}
}
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import java.io.*;
import jdk.test.lib.classloader.ClassUnloadCommon;

// This class loader will deadlock where one thread has a lock for A, trying to get a lock for B
// and the other thread has a lock for B, trying to get a lock for A in the case of
// A extends B extends A. It should throw ClassCircularityError from both threads.

class MyLoader extends ClassLoader {
static {
registerAsParallelCapable();
}

private static boolean first = true;

public Class loadClass(String name) throws ClassNotFoundException {
// Wait before getting B lock.
if (name.equals("B") && first) {
first = false;
makeThreadWait();
}
synchronized(getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c != null) return c;

if (name.equals("B") && !first) {
wakeUpThread();
}

byte[] b = loadClassData(name);
if (b != null) {
return defineClass(name, b, 0, b.length);
} else {
return super.loadClass(name);
}
}
}

private static boolean parallel = false;
private Object sync = new Object();
private Object thread_sync = new Object();

private void makeThreadWait() {
if (!parallel) { return; }

// Wake up the second thread here.
synchronized (thread_sync) {
thread_sync.notify();
}
if (isRegisteredAsParallelCapable()) {
synchronized(sync) {
try {
ThreadPrint.println("t1 waits parallelCapable loader");
sync.wait(); // Give up lock before request to load B
} catch (InterruptedException e) {}
}
} else {
try {
ThreadPrint.println("t1 waits non-parallelCapable loader");
wait(); // Give up lock before request to load B
} catch (InterruptedException e) {}
}
}

// Parallel capable loader should wake up the first thread.
// Non-parallelCapable class loader thread will be woken up by the jvm.
private void wakeUpThread() {
if (isRegisteredAsParallelCapable()) {
synchronized(sync) {
sync.notify();
}
}
}

private byte[] loadClassData(String name) {
if (name.equals("A")) {
ThreadPrint.println("loading A extends B");
return ClassUnloadCommon.getClassData("A");
} else if (name.equals("B")) {
ThreadPrint.println("loading B extends A");
try {
return AsmClasses.dumpB();
} catch (Exception e) {
e.printStackTrace();
return null;
}
} else if (!name.startsWith("java")) {
return ClassUnloadCommon.getClassData(name);
}
return null;
}


ClassLoadingThread[] threads = new ClassLoadingThread[2];
private boolean success = true;

public boolean report_success() {
for (int i = 0; i < 2; i++) {
try {
threads[i].join();
if (!threads[i].report_success()) success = false;
} catch (InterruptedException e) {}
}
return success;
}

void startLoading() {

for (int i = 0; i < 2; i++) {
threads[i] = new ClassLoadingThread(this, thread_sync);
threads[i].setName("Loading Thread #" + (i + 1));
threads[i].start();
System.out.println("Thread " + (i + 1) + " was started...");
// wait to start the second thread if not concurrent
if (i == 0) {
synchronized(thread_sync) {
try {
ThreadPrint.println("t2 waits");
thread_sync.wait();
} catch (InterruptedException e) {}
}
}
}
}

MyLoader(boolean load_in_parallel) {
parallel = load_in_parallel;
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

class MyNonParallelLoader extends MyLoader {
// This loader isn't parallel capable because it's not registered in the static
// initializer as such. parallelCapable is not an inheritable attribute.
MyNonParallelLoader(boolean load_in_parallel) {
super(load_in_parallel);
}
}
ProTip! Use n and p to navigate between commits in a pull request.