8262046: Clean up parallel class loading code and comments

Reviewed-by: lfoltan, iklam
This commit is contained in:
Coleen Phillimore 2021-04-01 21:16:58 +00:00
parent 04f24fe9ad
commit 5e59d28def
11 changed files with 720 additions and 227 deletions

@ -322,50 +322,46 @@ Klass* SystemDictionary::resolve_array_class_or_null(Symbol* class_name,
return k;
}
static inline void log_circularity_error(Thread* thread, PlaceholderEntry* probe) {
LogTarget(Debug, class, load, placeholders) lt;
if (lt.is_enabled()) {
ResourceMark rm(thread);
LogStream ls(lt);
ls.print("ClassCircularityError detected for placeholder ");
probe->print_entry(&ls);
ls.cr();
}
}
// Must be called for any super-class or super-interface resolution
// Must be called for any superclass or superinterface resolution
// during class definition to allow class circularity checking
// super-interface callers:
// parse_interfaces - for defineClass
// super-class callers:
// ClassFileParser - for defineClass
// superinterface callers:
// parse_interfaces - from defineClass
// superclass callers:
// ClassFileParser - from defineClass
// load_shared_class - while loading a class from shared archive
// resolve_instance_class_or_null:
// via: handle_parallel_super_load
// when resolving a class that has an existing placeholder with
// a saved superclass [i.e. a defineClass is currently in progress]
// if another thread is trying to resolve the class, it must do
// super-class checks on its own thread to catch class circularity
// This last call is critical in class circularity checking for cases
// where classloading is delegated to different threads and the
// classloader lock is released.
// Take the case: Base->Super->Base
// 1. If thread T1 tries to do a defineClass of class Base
// resolve_super_or_fail creates placeholder: T1, Base (super Super)
// 2. resolve_instance_class_or_null does not find SD or placeholder for Super
// so it tries to load Super
// 3. If we load the class internally, or user classloader uses same thread
// loadClassFromxxx or defineClass via parseClassFile Super ...
// 3.1 resolve_super_or_fail creates placeholder: T1, Super (super Base)
// 3.3 resolve_instance_class_or_null Base, finds placeholder for Base
// 3.4 calls resolve_super_or_fail Base
// 3.5 finds T1,Base -> throws class circularity
//OR 4. If T2 tries to resolve Super via defineClass Super ...
// 4.1 resolve_super_or_fail creates placeholder: T2, Super (super Base)
// 4.2 resolve_instance_class_or_null Base, finds placeholder for Base (super Super)
// 4.3 calls resolve_super_or_fail Super in parallel on own thread T2
// 4.4 finds T2, Super -> throws class circularity
// Be careful when modifying this code: once you have run
// placeholders()->find_and_add(PlaceholderTable::LOAD_SUPER),
// you need to find_and_remove it before returning.
// So be careful to not exit with a CHECK_ macro between these calls.
// If another thread is trying to resolve the class, it must do
// superclass checks on its own thread to catch class circularity and
// to avoid deadlock.
//
// resolve_super_or_fail adds a LOAD_SUPER placeholder to the placeholder table before calling
// resolve_instance_class_or_null. ClassCircularityError is detected when a LOAD_SUPER or LOAD_INSTANCE
// placeholder for the same thread, class, classloader is found.
// This can be seen with logging option: -Xlog:class+load+placeholders=debug.
//
InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
Symbol* super_name,
Handle class_loader,
Handle protection_domain,
bool is_superclass,
TRAPS) {
assert(!Signature::is_array(super_name), "invalid super class name");
assert(super_name != NULL, "null superclass for resolving");
assert(!Signature::is_array(super_name), "invalid superclass name");
#if INCLUDE_CDS
if (DumpSharedSpaces) {
// Special processing for handling UNREGISTERED shared classes.
@ -377,25 +373,16 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
}
#endif // INCLUDE_CDS
// Double-check, if klass is already loaded, just return super-class,interface
// Don't add a placedholder if already loaded, i.e. already in appropriate class loader
// dictionary.
// Make sure there's a placeholder for the *klass* before resolving.
// Used as a claim that this thread is currently loading superclass/classloader
// Used here for ClassCircularity checks and also for heap verification
// (every InstanceKlass needs to be in its class loader dictionary or have a placeholder).
// Must check ClassCircularity before checking if super class is already loaded.
//
// We might not already have a placeholder if this class_name was
// first seen via resolve_from_stream (jni_DefineClass or JVM_DefineClass);
// the name of the class might not be known until the stream is actually
// parsed.
// Bugs 4643874, 4715493
// If klass is already loaded, just return the superclass or superinterface.
// Make sure there's a placeholder for the class_name before resolving.
// This is used as a claim that this thread is currently loading superclass/classloader
// and for ClassCircularity checks.
ClassLoaderData* loader_data = class_loader_data(class_loader);
Dictionary* dictionary = loader_data->dictionary();
unsigned int name_hash = dictionary->compute_hash(class_name);
assert(placeholders()->compute_hash(class_name) == name_hash, "they're the same hashcode");
// can't throw error holding a lock
bool throw_circularity_error = false;
{
@ -411,36 +398,37 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
(quicksuperk->class_loader() == class_loader()))) {
return quicksuperk;
} else {
// Must check ClassCircularity before checking if superclass is already loaded.
PlaceholderEntry* probe = placeholders()->get_entry(name_hash, class_name, loader_data);
if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER)) {
log_circularity_error(THREAD, probe);
throw_circularity_error = true;
}
}
if (!throw_circularity_error) {
// Be careful not to exit resolve_super without removing this placeholder.
PlaceholderEntry* newprobe = placeholders()->find_and_add(name_hash, class_name, loader_data, PlaceholderTable::LOAD_SUPER, super_name, THREAD);
PlaceholderEntry* newprobe = placeholders()->find_and_add(name_hash,
class_name,
loader_data,
PlaceholderTable::LOAD_SUPER,
super_name, THREAD);
}
}
if (throw_circularity_error) {
ResourceMark rm(THREAD);
THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), class_name->as_C_string());
}
// java.lang.Object should have been found above
assert(super_name != NULL, "null super class for resolving");
// Resolve the super class or interface, check results on return
// Resolve the superclass or superinterface, check results on return
InstanceKlass* superk =
SystemDictionary::resolve_instance_class_or_null_helper(super_name,
class_loader,
protection_domain,
THREAD);
// Clean up of placeholders moved so that each classloadAction registrar self-cleans up
// It is no longer necessary to keep the placeholder table alive until update_dictionary
// or error. GC used to walk the placeholder table as strong roots.
// The instanceKlass is kept alive because the class loader is on the stack,
// which keeps the loader_data alive, as well as all instanceKlasses in
// the loader_data. parseClassFile adds the instanceKlass to loader_data.
// Clean up placeholder entry.
{
MutexLocker mu(THREAD, SystemDictionary_lock);
placeholders()->find_and_remove(name_hash, class_name, loader_data, PlaceholderTable::LOAD_SUPER, THREAD);
@ -479,7 +467,7 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
//
// The notify allows applications that did an untimed wait() on
// the classloader object lock to not hang.
void SystemDictionary::double_lock_wait(JavaThread* thread, Handle lockObject) {
static void double_lock_wait(JavaThread* thread, Handle lockObject) {
assert_lock_strong(SystemDictionary_lock);
assert(lockObject() != NULL, "lockObject must be non-NULL");
@ -496,23 +484,16 @@ void SystemDictionary::double_lock_wait(JavaThread* thread, Handle lockObject) {
SystemDictionary_lock->lock();
}
// If the class in is in the placeholder table, class loading is in progress
// If the class in is in the placeholder table, class loading is in progress.
// For cases where the application changes threads to load classes, it
// is critical to ClassCircularity detection that we try loading
// the superclass on the same thread internally, so we do parallel
// super class loading here.
// This also is critical in cases where the original thread gets stalled
// even in non-circularity situations.
// Returns non-null Klass* if other thread has completed load
// and we are done. If this returns a null Klass* and no pending exception,
// the caller must load the class.
InstanceKlass* SystemDictionary::handle_parallel_super_load(
Symbol* name, Symbol* superclassname, Handle class_loader,
Handle protection_domain, Handle lockObject, TRAPS) {
ClassLoaderData* loader_data = class_loader_data(class_loader);
Dictionary* dictionary = loader_data->dictionary();
unsigned int name_hash = dictionary->compute_hash(name);
// the superclass on the new thread internally, so we do parallel
// superclass loading here. This avoids deadlock for ClassCircularity
// detection for parallelCapable class loaders that lock on a per-class lock.
static void handle_parallel_super_load(Symbol* name,
Symbol* superclassname,
Handle class_loader,
Handle protection_domain, TRAPS) {
// superk is not used; resolve_super_or_fail is called for circularity check only.
Klass* superk = SystemDictionary::resolve_super_or_fail(name,
@ -520,29 +501,37 @@ InstanceKlass* SystemDictionary::handle_parallel_super_load(
class_loader,
protection_domain,
true,
CHECK_NULL);
CHECK);
}
// parallelCapable class loaders do NOT wait for parallel superclass loads to complete
// Serial class loaders and bootstrap classloader do wait for superclass loads
if (!class_loader.is_null() && is_parallelCapable(class_loader)) {
MutexLocker mu(THREAD, SystemDictionary_lock);
return dictionary->find_class(name_hash, name);
}
// parallelCapable class loaders do NOT wait for parallel superclass loads to complete
// Serial class loaders and bootstrap classloader do wait for superclass loads
static bool should_wait_for_loading(Handle class_loader) {
return class_loader.is_null() || !is_parallelCapable(class_loader);
}
// must loop to both handle other placeholder updates
// and spurious notifications
bool super_load_in_progress = true;
PlaceholderEntry* placeholder;
while (super_load_in_progress) {
MutexLocker mu(THREAD, SystemDictionary_lock);
// Check if classloading completed while we were loading superclass or waiting
InstanceKlass* check = dictionary->find_class(name_hash, name);
if (check != NULL) {
// Klass is already loaded, so just return it
return check;
// For bootstrap and non-parallelCapable class loaders, check and wait for
// another thread to complete loading this class.
InstanceKlass* SystemDictionary::handle_parallel_loading(JavaThread* current,
unsigned int name_hash,
Symbol* name,
ClassLoaderData* loader_data,
Handle lockObject,
bool* throw_circularity_error) {
PlaceholderEntry* oldprobe = placeholders()->get_entry(name_hash, name, loader_data);
if (oldprobe != NULL) {
// only need check_seen_thread once, not on each loop
// 6341374 java/lang/Instrument with -Xcomp
if (oldprobe->check_seen_thread(current, PlaceholderTable::LOAD_INSTANCE)) {
log_circularity_error(current, oldprobe);
*throw_circularity_error = true;
return NULL;
} else {
placeholder = placeholders()->get_entry(name_hash, name, loader_data);
if (placeholder != NULL && placeholder->super_load_in_progress()) {
// Wait until the first thread has finished loading this class. Also wait until all the
// threads trying to load its superclass have removed their placeholders.
while (oldprobe != NULL &&
(oldprobe->instance_load_in_progress() || oldprobe->super_load_in_progress())) {
// We only get here if the application has released the
// classloader lock when another thread was in the middle of loading a
// superclass/superinterface for this class, and now
@ -555,16 +544,22 @@ InstanceKlass* SystemDictionary::handle_parallel_super_load(
// the original thread completes the class loading or fails
// If it completes we will use the resulting InstanceKlass
// which we will find below in the systemDictionary.
// We also get here for parallel bootstrap classloader
if (class_loader.is_null()) {
oldprobe = NULL; // Other thread could delete this placeholder entry
if (lockObject.is_null()) {
SystemDictionary_lock->wait();
} else {
double_lock_wait(THREAD->as_Java_thread(), lockObject);
double_lock_wait(current, lockObject);
}
} else {
// If not in SD and not in PH, the other thread is done loading the super class
// but not done loading this class. We'll give up the lock and wait for that below.
super_load_in_progress = false;
// Check if classloading completed while we were waiting
InstanceKlass* check = loader_data->dictionary()->find_class(name_hash, name);
if (check != NULL) {
// Klass is already loaded, so just return it
return check;
}
// check if other thread failed to load and cleaned up
oldprobe = placeholders()->get_entry(name_hash, name, loader_data);
}
}
}
@ -581,17 +576,16 @@ void SystemDictionary::post_class_load_event(EventClassLoad* event, const Instan
event->commit();
}
// Be careful when modifying this code: once you have run
// placeholders()->find_and_add(PlaceholderTable::LOAD_INSTANCE),
// you need to find_and_remove it before returning.
// So be careful to not exit with a CHECK_ macro betweeen these calls.
//
// name must be in the form of "java/lang/Object" -- cannot be "Ljava/lang/Object;"
// SystemDictionary::resolve_instance_class_or_null is the main function for class name resolution.
// After checking if the InstanceKlass already exists, it checks for ClassCircularityError and
// whether the thread must wait for loading in parallel. It eventually calls load_instance_class,
// which will load the class via the bootstrap loader or call ClassLoader.loadClass().
// This can return NULL, an exception or an InstanceKlass.
InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
Handle class_loader,
Handle protection_domain,
TRAPS) {
// name must be in the form of "java/lang/Object" -- cannot be "Ljava/lang/Object;"
assert(name != NULL && !Signature::is_array(name) &&
!Signature::has_envelope(name), "invalid class name");
@ -605,16 +599,13 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
Dictionary* dictionary = loader_data->dictionary();
unsigned int name_hash = dictionary->compute_hash(name);
// Do lookup to see if class already exist and the protection domain
// has the right access
// Do lookup to see if class already exists and the protection domain
// has the right access.
// This call uses find which checks protection domain already matches
// All subsequent calls use find_class, and set has_loaded_class so that
// before we return a result we call out to java to check for valid protection domain
// to allow returning the Klass* and add it to the pd_set if it is valid
{
InstanceKlass* probe = dictionary->find(name_hash, name, protection_domain);
if (probe != NULL) return probe;
}
// All subsequent calls use find_class, and set loaded_class so that
// before we return a result, we call out to java to check for valid protection domain.
InstanceKlass* probe = dictionary->find(name_hash, name, protection_domain);
if (probe != NULL) return probe;
// Non-bootstrap class loaders will call out to class loader and
// define via jvm/jni_DefineClass which will acquire the
@ -623,13 +614,11 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
// This lock must be acquired here so the waiter will find
// any successful result in the SystemDictionary and not attempt
// the define.
// ParallelCapable Classloaders and the bootstrap classloader
// ParallelCapable class loaders and the bootstrap classloader
// do not acquire lock here.
Handle lockObject = get_loader_lock_or_null(class_loader);
ObjectLocker ol(lockObject, THREAD->as_Java_thread());
// Check again (after locking) if the class already exists in SystemDictionary
bool class_has_been_loaded = false;
bool super_load_in_progress = false;
InstanceKlass* loaded_class = NULL;
Symbol* superclassname = NULL;
@ -641,12 +630,12 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
assert(placeholders()->compute_hash(name) == name_hash, "they're the same hashcode");
// Check again (after locking) if the class already exists in SystemDictionary
{
MutexLocker mu(THREAD, SystemDictionary_lock);
InstanceKlass* check = dictionary->find_class(name_hash, name);
if (check != NULL) {
// InstanceKlass is already loaded, so just return it
class_has_been_loaded = true;
// InstanceKlass is already loaded, but we still need to check protection domain below.
loaded_class = check;
} else {
PlaceholderEntry* placeholder = placeholders()->get_entry(name_hash, name, loader_data);
@ -658,24 +647,21 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
}
}
// If the class is in the placeholder table, class loading is in progress
// If the class is in the placeholder table with super_class set,
// handle superclass loading in progress.
if (super_load_in_progress) {
loaded_class = handle_parallel_super_load(name,
superclassname,
class_loader,
protection_domain,
lockObject, CHECK_NULL);
if (loaded_class != NULL) {
class_has_been_loaded = true;
}
handle_parallel_super_load(name, superclassname,
class_loader,
protection_domain,
CHECK_NULL);
}
bool throw_circularity_error = false;
if (!class_has_been_loaded) {
bool load_instance_added = false;
if (loaded_class == NULL) {
bool load_placeholder_added = false;
// Add placeholder entry to record loading instance class
// Three cases:
// Four cases:
// case 1. Bootstrap classloader
// This classloader supports parallelism at the classloader level
// but only allows a single thread to load a class/classloader pair.
@ -691,104 +677,52 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
// and that lock is still held when calling classloader's loadClass.
// For these classloaders, we ensure that the first requestor
// completes the load and other requestors wait for completion.
if (class_loader.is_null() || !is_parallelCapable(class_loader)) {
{
MutexLocker mu(THREAD, SystemDictionary_lock);
PlaceholderEntry* oldprobe = placeholders()->get_entry(name_hash, name, loader_data);
if (oldprobe != NULL) {
// only need check_seen_thread once, not on each loop
// 6341374 java/lang/Instrument with -Xcomp
if (oldprobe->check_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE)) {
throw_circularity_error = true;
} else {
// case 3: traditional: should never see load_in_progress.
while (!class_has_been_loaded && oldprobe != NULL && oldprobe->instance_load_in_progress()) {
// case 1: bootstrap classloader: prevent futile classloading,
// wait on first requestor
if (class_loader.is_null()) {
SystemDictionary_lock->wait();
} else {
// case 4: traditional with broken classloader lock. wait on first
// requestor.
double_lock_wait(THREAD->as_Java_thread(), lockObject);
}
// Check if classloading completed while we were waiting
InstanceKlass* check = dictionary->find_class(name_hash, name);
if (check != NULL) {
// Klass is already loaded, so just return it
loaded_class = check;
class_has_been_loaded = true;
}
// check if other thread failed to load and cleaned up
oldprobe = placeholders()->get_entry(name_hash, name, loader_data);
}
}
if (should_wait_for_loading(class_loader)) {
loaded_class = handle_parallel_loading(THREAD->as_Java_thread(),
name_hash,
name,
loader_data,
lockObject,
&throw_circularity_error);
}
// Add LOAD_INSTANCE while holding the SystemDictionary_lock
if (!throw_circularity_error && !class_has_been_loaded) {
// For the bootclass loader, if the thread did not catch another thread holding
// the LOAD_INSTANCE token, we need to check whether it completed loading
// while holding the SD_lock.
// Recheck if the class has been loaded for all class loader cases and
// add a LOAD_INSTANCE placeholder while holding the SystemDictionary_lock.
if (!throw_circularity_error && loaded_class == NULL) {
InstanceKlass* check = dictionary->find_class(name_hash, name);
if (check != NULL) {
// Klass is already loaded, so return it after checking/adding protection domain
loaded_class = check;
class_has_been_loaded = true;
} else {
// Now we've got the LOAD_INSTANCE token. Threads will wait on loading to complete for this thread.
} else if (should_wait_for_loading(class_loader)) {
// Add the LOAD_INSTANCE token. Threads will wait on loading to complete for this thread.
PlaceholderEntry* newprobe = placeholders()->find_and_add(name_hash, name, loader_data,
PlaceholderTable::LOAD_INSTANCE,
NULL,
THREAD);
load_instance_added = true;
load_placeholder_added = true;
}
}
}
// must throw error outside of owning lock
// Must throw error outside of owning lock
if (throw_circularity_error) {
assert(!HAS_PENDING_EXCEPTION && !load_instance_added, "circularity error cleanup");
assert(!HAS_PENDING_EXCEPTION && !load_placeholder_added, "circularity error cleanup");
ResourceMark rm(THREAD);
THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string());
}
if (!class_has_been_loaded) {
// Be careful when modifying this code: once you have run
// placeholders()->find_and_add(PlaceholderTable::LOAD_INSTANCE),
// you need to find_and_remove it before returning.
// So be careful to not exit with a CHECK_ macro between these calls.
if (loaded_class == NULL) {
// Do actual loading
loaded_class = load_instance_class(name, class_loader, THREAD);
loaded_class = load_instance_class(name_hash, name, class_loader, THREAD);
}
// If everything was OK (no exceptions, no null return value), and
// class_loader is NOT the defining loader, do a little more bookkeeping.
if (!HAS_PENDING_EXCEPTION && loaded_class != NULL &&
loaded_class->class_loader() != class_loader()) {
check_constraints(name_hash, loaded_class, class_loader, false, THREAD);
// Need to check for a PENDING_EXCEPTION again; check_constraints
// can throw but we may have to remove entry from the placeholder table below.
if (!HAS_PENDING_EXCEPTION) {
// Record dependency for non-parent delegation.
// This recording keeps the defining class loader of the klass (loaded_class) found
// from being unloaded while the initiating class loader is loaded
// even if the reference to the defining class loader is dropped
// before references to the initiating class loader.
loader_data->record_dependency(loaded_class);
{ // Grabbing the Compile_lock prevents systemDictionary updates
// during compilations.
MutexLocker mu(THREAD, Compile_lock);
update_dictionary(name_hash, loaded_class, class_loader);
}
if (JvmtiExport::should_post_class_load()) {
JvmtiExport::post_class_load(THREAD->as_Java_thread(), loaded_class);
}
}
}
} // load_instance_class
if (load_instance_added) {
if (load_placeholder_added) {
// clean up placeholder entries for LOAD_INSTANCE success or error
// This brackets the SystemDictionary updates for both defining
// and initiating loaders
@ -801,6 +735,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
if (HAS_PENDING_EXCEPTION || loaded_class == NULL) {
return NULL;
}
if (class_load_start_event.should_commit()) {
post_class_load_event(&class_load_start_event, loaded_class, loader_data);
}
@ -1039,7 +974,7 @@ InstanceKlass* SystemDictionary::resolve_from_stream(ClassFileStream* st,
#if INCLUDE_CDS
// Load a class for boot loader from the shared spaces. This also
// forces the super class and all interfaces to be loaded.
// forces the superclass and all interfaces to be loaded.
InstanceKlass* SystemDictionary::load_shared_boot_class(Symbol* class_name,
PackageEntry* pkg_entry,
TRAPS) {
@ -1149,7 +1084,7 @@ bool SystemDictionary::check_shared_class_super_type(InstanceKlass* klass, Insta
// super_type->class_loader_data() could be stale.
// + Don't check if loader data is NULL, ie. the super_type isn't fully loaded.
if (!super_type->is_shared_unregistered_class() && super_type->class_loader_data() != NULL) {
// Check if the super class is loaded by the current class_loader
// Check if the superclass is loaded by the current class_loader
Symbol* name = super_type->name();
InstanceKlass* check = find_instance_klass(name, class_loader, protection_domain);
if (check == super_type) {
@ -1314,7 +1249,7 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData
#endif // INCLUDE_CDS
InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS) {
if (class_loader.is_null()) {
ResourceMark rm(THREAD);
@ -1453,6 +1388,41 @@ InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle
}
}
InstanceKlass* SystemDictionary::load_instance_class(unsigned int name_hash,
Symbol* name,
Handle class_loader,
TRAPS) {
InstanceKlass* loaded_class = load_instance_class_impl(name, class_loader, CHECK_NULL);
// If everything was OK (no exceptions, no null return value), and
// class_loader is NOT the defining loader, do a little more bookkeeping.
if (loaded_class != NULL &&
loaded_class->class_loader() != class_loader()) {
check_constraints(name_hash, loaded_class, class_loader, false, CHECK_NULL);
// Record dependency for non-parent delegation.
// This recording keeps the defining class loader of the klass (loaded_class) found
// from being unloaded while the initiating class loader is loaded
// even if the reference to the defining class loader is dropped
// before references to the initiating class loader.
ClassLoaderData* loader_data = class_loader_data(class_loader);
loader_data->record_dependency(loaded_class);
{ // Grabbing the Compile_lock prevents systemDictionary updates
// during compilations.
MutexLocker mu(THREAD, Compile_lock);
update_dictionary(name_hash, loaded_class, class_loader);
}
if (JvmtiExport::should_post_class_load()) {
JvmtiExport::post_class_load(THREAD->as_Java_thread(), loaded_class);
}
}
return loaded_class;
}
static void post_class_define_event(InstanceKlass* k, const ClassLoaderData* def_cld) {
EventClassDefine event;
if (event.should_commit()) {

@ -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)
//
@ -340,21 +337,21 @@ private:
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);
}
}

@ -0,0 +1,75 @@
/*
* 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.
*/
/*
* @test MyLoaderTest
* @bug 8262046
* @summary Call handle_parallel_super_load, loading parallel threads that throw CCE
* @modules java.base/jdk.internal.org.objectweb.asm
* java.base/jdk.internal.misc
* @library /test/lib
* @compile -XDignore.symbol.file AsmClasses.java
* @compile test-classes/ClassInLoader.java test-classes/A.java test-classes/B.java
* @run main/othervm ParallelSuperTest
* @run main/othervm ParallelSuperTest -parallel
* @run main/othervm ParallelSuperTest -parallel -parallelCapable
*/
public class ParallelSuperTest {
public static void main(java.lang.String[] args) throws Exception {
boolean parallel = false;
boolean parallelCapable = false;
boolean success = true;
for (int i = 0; i < args.length; i++) {
try {
// Don't print debug info
if (args[i].equals("-parallel")) {
parallel = true;
} else if (args[i].equals("-parallelCapable")) {
parallelCapable = true;
} else {
System.out.println("Unrecognized " + args[i]);
}
} catch (NumberFormatException e) {
System.err.println("Invalid parameter: " + args[i - 1] + " " + args[i]);
}
}
// The -parallel -parallelCapable case will deadlock on locks for A and B if
// the jvm doesn't eagerly try to load A's superclass from the second thread.
// ie. needs to call SystemDictionary::handle_parallel_super_load
if (parallelCapable) {
MyLoader ldr = new MyLoader(parallel);
ldr.startLoading();
success = ldr.report_success();
} else {
MyNonParallelLoader ldr = new MyNonParallelLoader(parallel);
ldr.startLoading();
success = ldr.report_success();
}
if (success) {
System.out.println("PASSED");
} else {
throw new RuntimeException("FAILED");
}
}
}

@ -0,0 +1,31 @@
/*
* 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 to print thread-annotated output
class ThreadPrint {
public static void println(String s) {
System.out.println(Thread.currentThread().getName() + ": " + s);
System.out.flush();
}
}

@ -0,0 +1,29 @@
/*
* 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.
*/
public class A extends B {
public A() {
System.out.println("A extends B");
throw new RuntimeException("Should throw CCE here");
}
}

@ -0,0 +1,24 @@
/*
* 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.
*/
public class B { public B() { System.out.println("B"); } }

@ -0,0 +1,74 @@
/*
* 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.
*/
// Create a class to load inside the loader instance, that will load
// A through a constant pool reference.
// Class A extends B extends A
class CP1 {
void foo() throws Exception {
ThreadPrint.println("CP1.foo()");
try {
Class<?> a = A.class;
Object obj = a.getConstructor().newInstance();
throw new RuntimeException("Should throw CCE here");
} catch (Throwable e) {
ThreadPrint.println("Exception is caught: " + e);
if (!(e instanceof java.lang.ClassCircularityError)) {
throw new RuntimeException("Unexpected exception");
}
}
}
}
// This class has a constant pool reference to B which will also get CCE, but
// starting at B.
class CP2 {
void foo() throws Exception {
ThreadPrint.println("CP2.foo()");
try {
Class<?> a = B.class;
Object obj = a.getConstructor().newInstance();
throw new RuntimeException("Should throw CCE here");
} catch (Throwable e) {
ThreadPrint.println("Exception is caught: " + e);
if (!(e instanceof java.lang.ClassCircularityError)) {
throw new RuntimeException("Unexpected exception");
}
}
}
}
public class ClassInLoader {
private static boolean first = true;
public ClassInLoader() throws Exception {
ThreadPrint.println("ClassInLoader");
if (first) {
first = false;
CP1 c1 = new CP1();
c1.foo();
} else {
CP2 c2 = new CP2();
c2.foo();
}
}
}