8336919: Cleanup and rename tags in placeholders code

Co-authored-by: Frederic Parain <fparain@openjdk.org>
Reviewed-by: iklam, fparain, dholmes
This commit is contained in:
Coleen Phillimore 2024-07-31 17:55:01 +00:00
parent e4c7850c17
commit 8f039b5629
6 changed files with 110 additions and 101 deletions

View File

@ -835,14 +835,12 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY,
"Bad interface name in class file %s", CHECK);
// Call resolve_super so class circularity is checked
interf = SystemDictionary::resolve_super_or_fail(
_class_name,
unresolved_klass,
Handle(THREAD, _loader_data->class_loader()),
_protection_domain,
false,
CHECK);
// Call resolve on the interface class name with class circularity checking
interf = SystemDictionary::resolve_super_or_fail(_class_name,
unresolved_klass,
Handle(THREAD, _loader_data->class_loader()),
_protection_domain,
false, CHECK);
}
if (!interf->is_interface()) {

View File

@ -95,8 +95,8 @@ SeenThread* PlaceholderEntry::actionToQueue(PlaceholderTable::classloadAction ac
case PlaceholderTable::LOAD_INSTANCE:
queuehead = _loadInstanceThreadQ;
break;
case PlaceholderTable::LOAD_SUPER:
queuehead = _superThreadQ;
case PlaceholderTable::DETECT_CIRCULARITY:
queuehead = _circularityThreadQ;
break;
case PlaceholderTable::DEFINE_CLASS:
queuehead = _defineThreadQ;
@ -111,8 +111,8 @@ void PlaceholderEntry::set_threadQ(SeenThread* seenthread, PlaceholderTable::cla
case PlaceholderTable::LOAD_INSTANCE:
_loadInstanceThreadQ = seenthread;
break;
case PlaceholderTable::LOAD_SUPER:
_superThreadQ = seenthread;
case PlaceholderTable::DETECT_CIRCULARITY:
_circularityThreadQ = seenthread;
break;
case PlaceholderTable::DEFINE_CLASS:
_defineThreadQ = seenthread;
@ -188,10 +188,10 @@ bool PlaceholderEntry::remove_seen_thread(JavaThread* thread, PlaceholderTable::
}
void PlaceholderEntry::set_supername(Symbol* supername) {
void PlaceholderEntry::set_next_klass_name(Symbol* next_klass_name) {
assert_locked_or_safepoint(SystemDictionary_lock);
assert(_supername == nullptr || _supername->refcount() > 1, "must be referenced also by the loader");
_supername = supername;
assert(_next_klass_name == nullptr || _next_klass_name->refcount() > 1, "must be referenced also by the loader");
_next_klass_name = next_klass_name;
}
// Placeholder objects represent classes currently being loaded.
@ -199,12 +199,12 @@ void PlaceholderEntry::set_supername(Symbol* supername) {
// SystemDictionary_lock, so we don't need special precautions
// on store ordering here.
static PlaceholderEntry* add_entry(Symbol* class_name, ClassLoaderData* loader_data,
Symbol* supername){
Symbol* next_klass_name){
assert_locked_or_safepoint(SystemDictionary_lock);
assert(class_name != nullptr, "adding nullptr obj");
PlaceholderEntry entry;
entry.set_supername(supername);
entry.set_next_klass_name(next_klass_name);
PlaceholderKey key(class_name, loader_data);
bool created;
PlaceholderEntry* table_copy = _placeholders->put_if_absent(key, entry, &created);
@ -230,7 +230,7 @@ PlaceholderEntry* PlaceholderTable::get_entry(Symbol* class_name, ClassLoaderDat
static const char* action_to_string(PlaceholderTable::classloadAction action) {
switch (action) {
case PlaceholderTable::LOAD_INSTANCE: return "LOAD_INSTANCE";
case PlaceholderTable::LOAD_SUPER: return "LOAD_SUPER";
case PlaceholderTable::DETECT_CIRCULARITY: return "DETECT_CIRCULARITY";
case PlaceholderTable::DEFINE_CLASS: return "DEFINE_CLASS";
}
return "";
@ -250,20 +250,21 @@ inline void log(Symbol* name, PlaceholderEntry* entry, const char* function, Pla
// If no entry exists, add a placeholder entry
// If entry exists, reuse entry
// For both, push SeenThread for classloadAction
// If LOAD_SUPER, this is used for circularity detection for instanceklass loading.
// If DETECT_CIRCULARITY, this is used for circularity detection for instanceklass loading.
PlaceholderEntry* PlaceholderTable::find_and_add(Symbol* name,
ClassLoaderData* loader_data,
classloadAction action,
Symbol* supername,
Symbol* next_klass_name,
JavaThread* thread) {
assert(action != LOAD_SUPER || supername != nullptr, "must have a super class name");
assert(action != DETECT_CIRCULARITY || next_klass_name != nullptr,
"must have a class name for the next step in the class resolution recursion");
PlaceholderEntry* probe = get_entry(name, loader_data);
if (probe == nullptr) {
// Nothing found, add place holder
probe = add_entry(name, loader_data, supername);
probe = add_entry(name, loader_data, next_klass_name);
} else {
if (action == LOAD_SUPER) {
probe->set_supername(supername);
if (action == DETECT_CIRCULARITY) {
probe->set_next_klass_name(next_klass_name);
}
}
probe->add_seen_thread(thread, action);
@ -295,11 +296,11 @@ void PlaceholderTable::find_and_remove(Symbol* name, ClassLoaderData* loader_dat
assert(probe != nullptr, "must find an entry");
log(name, probe, "find_and_remove", action);
probe->remove_seen_thread(thread, action);
if (probe->superThreadQ() == nullptr) {
probe->set_supername(nullptr);
if (probe->circularityThreadQ() == nullptr) {
probe->set_next_klass_name(nullptr);
}
// If no other threads using this entry, and this thread is not using this entry for other states
if ((probe->superThreadQ() == nullptr) && (probe->loadInstanceThreadQ() == nullptr)
if ((probe->circularityThreadQ() == nullptr) && (probe->loadInstanceThreadQ() == nullptr)
&& (probe->defineThreadQ() == nullptr) && (probe->definer() == nullptr)) {
remove_entry(name, loader_data);
}
@ -312,9 +313,9 @@ void PlaceholderKey::print_on(outputStream* st) const {
}
void PlaceholderEntry::print_on(outputStream* st) const {
if (supername() != nullptr) {
st->print(", supername ");
supername()->print_value_on(st);
if (next_klass_name() != nullptr) {
st->print(", next_klass_name ");
next_klass_name()->print_value_on(st);
}
if (definer() != nullptr) {
st->print(", definer ");
@ -328,8 +329,8 @@ void PlaceholderEntry::print_on(outputStream* st) const {
st->print("loadInstanceThreadQ threads:");
SeenThread::print_action_queue(loadInstanceThreadQ(), st);
st->cr();
st->print("superThreadQ threads:");
SeenThread::print_action_queue(superThreadQ(), st);
st->print("circularityThreadQ threads:");
SeenThread::print_action_queue(circularityThreadQ(), st);
st->cr();
st->print("defineThreadQ threads:");
SeenThread::print_action_queue(defineThreadQ(), st);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2024, 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
@ -41,15 +41,15 @@ class PlaceholderTable : public AllStatic {
// caller claims ownership of that action
// For parallel classloading:
// multiple LOAD_INSTANCE threads can proceed in parallel
// multiple LOAD_SUPER threads can proceed in parallel
// LOAD_SUPER needed to check for class circularity
// multiple DETECT_CIRCULARITY threads can proceed in parallel
// DETECT_CIRCULARITY needed to check for class circularity
// DEFINE_CLASS: ultimately define class must be single threaded
// on a class/classloader basis
// so the head of that queue owns the token
// and the rest of the threads return the result the first thread gets
enum classloadAction {
LOAD_INSTANCE = 1, // calling load_instance_class
LOAD_SUPER = 2, // loading superclass for this class
DETECT_CIRCULARITY = 2, // loading while detecting class circularity
DEFINE_CLASS = 3 // find_or_define class
};
static void initialize();
@ -81,13 +81,13 @@ class SeenThread;
class PlaceholderEntry {
friend class PlaceholderTable;
private:
SymbolHandle _supername;
JavaThread* _definer; // owner of define token
InstanceKlass* _instanceKlass; // InstanceKlass from successful define
SeenThread* _superThreadQ; // doubly-linked queue of Threads loading a superclass for this class
SeenThread* _loadInstanceThreadQ; // loadInstance thread
// This can't be multiple threads since class loading waits for
// this token to be removed.
SymbolHandle _next_klass_name; // next step in the recursive process of class loading
JavaThread* _definer; // owner of define token
InstanceKlass* _instanceKlass; // InstanceKlass from successful define
SeenThread* _circularityThreadQ; // doubly-linked queue of Threads loading with circularity detection
SeenThread* _loadInstanceThreadQ; // loadInstance thread
// This can't be multiple threads since class loading
// waits for this token to be removed.
SeenThread* _defineThreadQ; // queue of Threads trying to define this class
// including _definer
@ -99,8 +99,8 @@ class PlaceholderEntry {
void add_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action);
bool remove_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action);
SeenThread* superThreadQ() const { return _superThreadQ; }
void set_superThreadQ(SeenThread* SeenThread) { _superThreadQ = SeenThread; }
SeenThread* circularityThreadQ() const { return _circularityThreadQ; }
void set_circularityThreadQ(SeenThread* SeenThread) { _circularityThreadQ = SeenThread; }
SeenThread* loadInstanceThreadQ() const { return _loadInstanceThreadQ; }
void set_loadInstanceThreadQ(SeenThread* SeenThread) { _loadInstanceThreadQ = SeenThread; }
@ -110,10 +110,10 @@ class PlaceholderEntry {
public:
PlaceholderEntry() :
_definer(nullptr), _instanceKlass(nullptr),
_superThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { }
_circularityThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { }
Symbol* supername() const { return _supername; }
void set_supername(Symbol* supername);
Symbol* next_klass_name() const { return _next_klass_name; }
void set_next_klass_name(Symbol* next_klass_name);
JavaThread* definer() const {return _definer; }
void set_definer(JavaThread* definer) { _definer = definer; }
@ -121,8 +121,8 @@ class PlaceholderEntry {
InstanceKlass* instance_klass() const {return _instanceKlass; }
void set_instance_klass(InstanceKlass* ik) { _instanceKlass = ik; }
bool super_load_in_progress() {
return (_superThreadQ != nullptr);
bool circularity_detection_in_progress() {
return (_circularityThreadQ != nullptr);
}
bool instance_load_in_progress() {

View File

@ -399,32 +399,32 @@ static inline void log_circularity_error(Symbol* name, PlaceholderEntry* probe)
// 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
// resolve_with_circularity_detection adds a DETECT_CIRCULARITY placeholder to the placeholder table before calling
// resolve_instance_class_or_null. ClassCircularityError is detected when a DETECT_CIRCULARITY 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) {
InstanceKlass* SystemDictionary::resolve_with_circularity_detection(Symbol* class_name,
Symbol* next_name,
Handle class_loader,
Handle protection_domain,
bool is_superclass,
TRAPS) {
assert(super_name != nullptr, "null superclass for resolving");
assert(!Signature::is_array(super_name), "invalid superclass name");
assert(next_name != nullptr, "null superclass for resolving");
assert(!Signature::is_array(next_name), "invalid superclass name");
#if INCLUDE_CDS
if (CDSConfig::is_dumping_static_archive()) {
// Special processing for handling UNREGISTERED shared classes.
InstanceKlass* k = SystemDictionaryShared::lookup_super_for_unregistered_class(class_name,
super_name, is_superclass);
next_name, is_superclass);
if (k) {
return k;
}
}
#endif // INCLUDE_CDS
// If klass is already loaded, just return the superclass or superinterface.
// If class_name 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.
@ -439,28 +439,27 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
InstanceKlass* klassk = dictionary->find_class(THREAD, class_name);
InstanceKlass* quicksuperk;
// To support parallel loading: if class is done loading, just return the superclass
// if the super_name matches class->super()->name() and if the class loaders match.
// Otherwise, a LinkageError will be thrown later.
// if the next_name matches class->super()->name() and if the class loaders match.
if (klassk != nullptr && is_superclass &&
((quicksuperk = klassk->java_super()) != nullptr) &&
((quicksuperk->name() == super_name) &&
(quicksuperk->class_loader() == class_loader()))) {
return quicksuperk;
((quicksuperk = klassk->java_super()) != nullptr) &&
((quicksuperk->name() == next_name) &&
(quicksuperk->class_loader() == class_loader()))) {
return quicksuperk;
} else {
// Must check ClassCircularity before checking if superclass is already loaded.
PlaceholderEntry* probe = PlaceholderTable::get_entry(class_name, loader_data);
if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER)) {
if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::DETECT_CIRCULARITY)) {
log_circularity_error(class_name, probe);
throw_circularity_error = true;
}
}
if (!throw_circularity_error) {
// Be careful not to exit resolve_super without removing this placeholder.
// Be careful not to exit resolve_with_circularity_detection without removing this placeholder.
PlaceholderEntry* newprobe = PlaceholderTable::find_and_add(class_name,
loader_data,
PlaceholderTable::LOAD_SUPER,
super_name, THREAD);
PlaceholderTable::DETECT_CIRCULARITY,
next_name, THREAD);
}
}
@ -471,7 +470,7 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
// Resolve the superclass or superinterface, check results on return
InstanceKlass* superk =
SystemDictionary::resolve_instance_class_or_null(super_name,
SystemDictionary::resolve_instance_class_or_null(next_name,
class_loader,
protection_domain,
THREAD);
@ -479,13 +478,13 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
// Clean up placeholder entry.
{
MutexLocker mu(THREAD, SystemDictionary_lock);
PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::LOAD_SUPER, THREAD);
PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::DETECT_CIRCULARITY, THREAD);
SystemDictionary_lock->notify_all();
}
// Check for pending exception or null superk, and throw exception
if (HAS_PENDING_EXCEPTION || superk == nullptr) {
handle_resolution_exception(super_name, true, CHECK_NULL);
handle_resolution_exception(next_name, true, CHECK_NULL);
}
return superk;
@ -502,13 +501,15 @@ static void handle_parallel_super_load(Symbol* name,
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,
superclassname,
class_loader,
protection_domain,
true,
CHECK);
// The result superk is not used; resolve_with_circularity_detection is called for circularity check only.
// This passes true to is_superclass even though it might not be the super class in order to perform the
// optimization anyway.
Klass* superk = SystemDictionary::resolve_with_circularity_detection(name,
superclassname,
class_loader,
protection_domain,
true,
CHECK);
}
// Bootstrap and non-parallel capable class loaders use the LOAD_INSTANCE placeholder to
@ -536,7 +537,7 @@ static InstanceKlass* handle_parallel_loading(JavaThread* current,
// 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 != nullptr &&
(oldprobe->instance_load_in_progress() || oldprobe->super_load_in_progress())) {
(oldprobe->instance_load_in_progress() || oldprobe->circularity_detection_in_progress())) {
// LOAD_INSTANCE placeholders are used to implement parallel capable class loading
// for the bootclass loader.
@ -575,8 +576,9 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
Handle protection_domain,
TRAPS) {
// name must be in the form of "java/lang/Object" -- cannot be "Ljava/lang/Object;"
DEBUG_ONLY(ResourceMark rm(THREAD));
assert(name != nullptr && !Signature::is_array(name) &&
!Signature::has_envelope(name), "invalid class name");
!Signature::has_envelope(name), "invalid class name: %s", name == nullptr ? "nullptr" : name->as_C_string());
EventClassLoad class_load_start_event;
@ -607,7 +609,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
Handle lockObject = get_loader_lock_or_null(class_loader);
ObjectLocker ol(lockObject, THREAD);
bool super_load_in_progress = false;
bool circularity_detection_in_progress = false;
InstanceKlass* loaded_class = nullptr;
SymbolHandle superclassname; // Keep alive while loading in parallel thread.
@ -625,9 +627,9 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
loaded_class = check;
} else {
PlaceholderEntry* placeholder = PlaceholderTable::get_entry(name, loader_data);
if (placeholder != nullptr && placeholder->super_load_in_progress()) {
super_load_in_progress = true;
superclassname = placeholder->supername();
if (placeholder != nullptr && placeholder->circularity_detection_in_progress()) {
circularity_detection_in_progress = true;
superclassname = placeholder->next_klass_name();
assert(superclassname != nullptr, "superclass has to have a name");
}
}
@ -635,7 +637,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
// If the class is in the placeholder table with super_class set,
// handle superclass loading in progress.
if (super_load_in_progress) {
if (circularity_detection_in_progress) {
handle_parallel_super_load(name, superclassname,
class_loader,
protection_domain,
@ -1052,8 +1054,8 @@ bool SystemDictionary::check_shared_class_super_type(InstanceKlass* klass, Insta
}
}
Klass *found = resolve_super_or_fail(klass->name(), super_type->name(),
class_loader, protection_domain, is_superclass, CHECK_0);
Klass *found = resolve_with_circularity_detection(klass->name(), super_type->name(),
class_loader, protection_domain, is_superclass, CHECK_0);
if (found == super_type) {
return true;
} else {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, 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
@ -102,15 +102,23 @@ class SystemDictionary : AllStatic {
return resolve_or_null(class_name, Handle(), Handle(), THREAD);
}
static InstanceKlass* resolve_with_circularity_detection(Symbol* class_name,
Symbol* next_name,
Handle class_loader,
Handle protection_domain,
bool is_superclass,
TRAPS);
// Resolve a superclass or superinterface. Called from ClassFileParser,
// parse_interfaces, resolve_instance_class_or_null, load_shared_class
// "class_name" is the class whose super class or interface is being resolved.
static InstanceKlass* resolve_super_or_fail(Symbol* class_name,
Symbol* super_name,
static InstanceKlass* resolve_super_or_fail(Symbol* class_name, Symbol* super_name,
Handle class_loader,
Handle protection_domain,
bool is_superclass,
TRAPS);
Handle protection_domain, bool is_superclass, TRAPS) {
return resolve_with_circularity_detection(class_name, super_name, class_loader, protection_domain,
is_superclass, THREAD);
}
private:
// Parse the stream to create a hidden class.
// Used by jvm_lookup_define_class.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2024, 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
@ -49,7 +49,7 @@ TEST_VM(PlaceholderTable, supername) {
{
MutexLocker ml(THREAD, SystemDictionary_lock);
PlaceholderTable::classloadAction super_action = PlaceholderTable::LOAD_SUPER;
PlaceholderTable::classloadAction super_action = PlaceholderTable::DETECT_CIRCULARITY;
PlaceholderTable::classloadAction define_action = PlaceholderTable::DEFINE_CLASS;
// DefineClass A and D
@ -71,7 +71,7 @@ TEST_VM(PlaceholderTable, supername) {
// Another thread comes in and finds A loading Super
PlaceholderEntry* placeholder = PlaceholderTable::get_entry(A, loader_data);
SymbolHandle supername = placeholder->supername();
SymbolHandle supername = placeholder->next_klass_name();
// Other thread is done before handle_parallel_super_load
PlaceholderTable::find_and_remove(A, loader_data, super_action, THREAD);
@ -86,7 +86,7 @@ TEST_VM(PlaceholderTable, supername) {
// Refcount should be 3: one in table for class A, one in table for class D
// and one locally with SymbolHandle keeping it alive
placeholder = PlaceholderTable::get_entry(A, loader_data);
supername = placeholder->supername();
supername = placeholder->next_klass_name();
EXPECT_EQ(super->refcount(), 3) << "super class name refcount should be 3";
// Second thread's done too