diff --git a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp index ff9e34d274a..9bfa6993a11 100644 --- a/src/hotspot/share/classfile/placeholders.cpp +++ b/src/hotspot/share/classfile/placeholders.cpp @@ -132,9 +132,6 @@ void PlaceholderEntry::add_seen_thread(JavaThread* thread, PlaceholderTable::cla SeenThread* threadEntry = new SeenThread(thread); SeenThread* seen = actionToQueue(action); - assert(action != PlaceholderTable::LOAD_INSTANCE || !EnableWaitForParallelLoad || seen == nullptr, - "Only one LOAD_INSTANCE allowed at a time"); - if (seen == nullptr) { set_threadQ(threadEntry, action); return; diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 7c06d5f78cb..6420dbf7db6 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -489,50 +489,6 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, return superk; } -// We only get here if this thread finds that another thread -// has already claimed the placeholder token for the current operation, -// but that other thread either never owned or gave up the -// object lock -// Waits on SystemDictionary_lock to indicate placeholder table updated -// On return, caller must recheck placeholder table state -// -// We only get here if -// 1) custom classLoader, i.e. not bootstrap classloader -// 2) custom classLoader has broken the class loader objectLock -// so another thread got here in parallel -// -// lockObject must be held. -// Complicated dance due to lock ordering: -// Must first release the classloader object lock to -// allow initial definer to complete the class definition -// and to avoid deadlock -// Reclaim classloader lock object with same original recursion count -// Must release SystemDictionary_lock after notify, since -// class loader lock must be claimed before SystemDictionary_lock -// to prevent deadlocks -// -// The notify allows applications that did an untimed wait() on -// the classloader object lock to not hang. -static void double_lock_wait(JavaThread* thread, Handle lockObject) { - assert_lock_strong(SystemDictionary_lock); - - assert(EnableWaitForParallelLoad, - "Only called when enabling legacy parallel class loading logic " - "for non-parallel capable class loaders"); - assert(lockObject() != nullptr, "lockObject must be non-null"); - bool calledholdinglock - = ObjectSynchronizer::current_thread_holds_lock(thread, lockObject); - assert(calledholdinglock, "must hold lock for notify"); - assert(!is_parallelCapable(lockObject), "lockObject must not be parallelCapable"); - // These don't throw exceptions. - ObjectSynchronizer::notifyall(lockObject, thread); - intx recursions = ObjectSynchronizer::complete_exit(lockObject, thread); - SystemDictionary_lock->wait(); - SystemDictionary_lock->unlock(); - ObjectSynchronizer::reenter(lockObject, recursions, thread); - SystemDictionary_lock->lock(); -} - // 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 @@ -554,20 +510,17 @@ static void handle_parallel_super_load(Symbol* name, } // Bootstrap and non-parallel capable class loaders use the LOAD_INSTANCE placeholder to -// wait for parallel class loading and to check for circularity error for Xcomp when loading -// signature classes. -// parallelCapable class loaders do NOT wait for parallel loads to complete +// wait for parallel class loading and/or to check for circularity error for Xcomp when loading. static bool needs_load_placeholder(Handle class_loader) { return class_loader.is_null() || !is_parallelCapable(class_loader); } -// 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, - Symbol* name, - ClassLoaderData* loader_data, - Handle lockObject, - bool* throw_circularity_error) { +// Check for other threads loading this class either to throw CCE or wait in the case of the boot loader. +static InstanceKlass* handle_parallel_loading(JavaThread* current, + Symbol* name, + ClassLoaderData* loader_data, + bool must_wait_for_class_loading, + bool* throw_circularity_error) { PlaceholderEntry* oldprobe = PlaceholderTable::get_entry(name, loader_data); if (oldprobe != nullptr) { // -Xcomp calls load_signature_classes which might result in loading @@ -577,32 +530,15 @@ InstanceKlass* SystemDictionary::handle_parallel_loading(JavaThread* current, log_circularity_error(name, oldprobe); *throw_circularity_error = true; return nullptr; - } else { + } else if (must_wait_for_class_loading) { // 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())) { - // 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 - // this thread is also trying to load this class. - // To minimize surprises, the first thread that started to - // load a class should be the one to complete the loading - // with the classfile it initially expected. - // This logic has the current thread wait once it has done - // all the superclass/superinterface loading it can, until - // 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. - - if (lockObject.is_null()) { - SystemDictionary_lock->wait(); - } else if (EnableWaitForParallelLoad) { - double_lock_wait(current, lockObject); - } else { - return nullptr; - } + // LOAD_INSTANCE placeholders are used to implement parallel capable class loading + // for the bootclass loader. + SystemDictionary_lock->wait(); // Check if classloading completed while we were waiting InstanceKlass* check = loader_data->dictionary()->find_class(current, name); @@ -709,7 +645,6 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, bool load_placeholder_added = false; // Add placeholder entry to record loading instance class - // 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. @@ -718,20 +653,16 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, // These class loaders lock a per-class object lock when ClassLoader.loadClass() // is called. A LOAD_INSTANCE placeholder isn't used for mutual exclusion. // case 3. traditional classloaders that rely on the classloader object lock - // There should be no need for need for LOAD_INSTANCE, except: - // case 4. traditional class loaders that break the classloader object lock - // as a legacy deadlock workaround. Detection of this case requires that - // this check is done while holding the classloader object lock, - // 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. + // There should be no need for need for LOAD_INSTANCE for mutual exclusion, + // except the LOAD_INSTANCE placeholder is used to detect CCE for -Xcomp. + // TODO: should also be used to detect CCE for parallel capable class loaders but it's not. { MutexLocker mu(THREAD, SystemDictionary_lock); if (needs_load_placeholder(class_loader)) { loaded_class = handle_parallel_loading(THREAD, name, loader_data, - lockObject, + class_loader.is_null(), &throw_circularity_error); } @@ -742,8 +673,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, if (check != nullptr) { loaded_class = check; } else if (needs_load_placeholder(class_loader)) { - // Add the LOAD_INSTANCE token. Threads will wait on loading to complete for this thread, - // and check for ClassCircularityError with -Xcomp. + // Add the LOAD_INSTANCE token. Threads will wait on loading to complete for this thread. PlaceholderEntry* newprobe = PlaceholderTable::find_and_add(name, loader_data, PlaceholderTable::LOAD_INSTANCE, nullptr, diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index fc90979772e..ffb5a48e6c9 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -302,12 +302,6 @@ private: static Klass* resolve_array_class_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS); - static InstanceKlass* handle_parallel_loading(JavaThread* current, - 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, diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 787f7f9fb03..73e73ef6f46 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -542,7 +542,6 @@ static SpecialFlag const special_jvm_flags[] = { { "DynamicDumpSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, - { "EnableWaitForParallelLoad", JDK_Version::jdk(20), JDK_Version::jdk(21), JDK_Version::jdk(22) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "DefaultMaxRAMFraction", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -551,6 +550,7 @@ static SpecialFlag const special_jvm_flags[] = { // -------------- Obsolete Flags - sorted by expired_in -------------- + { "EnableWaitForParallelLoad", JDK_Version::jdk(20), JDK_Version::jdk(21), JDK_Version::jdk(22) }, { "G1ConcRefinementGreenZone", JDK_Version::undefined(), JDK_Version::jdk(20), JDK_Version::undefined() }, { "G1ConcRefinementYellowZone", JDK_Version::undefined(), JDK_Version::jdk(20), JDK_Version::undefined() }, { "G1ConcRefinementRedZone", JDK_Version::undefined(), JDK_Version::jdk(20), JDK_Version::undefined() }, diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 64a8e69e8f8..3111425f120 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -688,10 +688,6 @@ const int ObjectAlignmentInBytes = 8; "Allow parallel defineClass requests for class loaders " \ "registering as parallel capable") \ \ - product(bool, EnableWaitForParallelLoad, false, \ - "(Deprecated) Enable legacy parallel classloading logic for " \ - "class loaders not registered as parallel capable") \ - \ product_pd(bool, DontYieldALot, \ "Throw away obvious excess yield calls") \ \ diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index ea539be2928..f4eb4e477a9 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -1340,12 +1340,7 @@ void ObjectMonitor::ExitEpilog(JavaThread* current, ObjectWaiter* Wakee) { OM_PERFDATA_OP(Parks, inc()); } - -// ----------------------------------------------------------------------------- -// Class Loader deadlock handling. -// // complete_exit exits a lock returning recursion count -// complete_exit/reenter operate as a wait without waiting // complete_exit requires an inflated monitor // The _owner field is not always the Thread addr even with an // inflated monitor, e.g. the monitor can be inflated by a non-owning @@ -1370,20 +1365,6 @@ intx ObjectMonitor::complete_exit(JavaThread* current) { return save; } -// reenter() enters a lock and sets recursion count -// complete_exit/reenter operate as a wait without waiting -bool ObjectMonitor::reenter(intx recursions, JavaThread* current) { - - guarantee(owner_raw() != current, "reenter already owner"); - if (!enter(current)) { - return false; - } - // Entered the monitor. - guarantee(_recursions == 0, "reenter recursion"); - _recursions = recursions; - return true; -} - // Checks that the current THREAD owns this monitor and causes an // immediate return if it doesn't. We don't use the CHECK macro // because we want the IMSE to be the only exception that is thrown diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index 285232f6c4d..eca244b9693 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -321,7 +321,6 @@ class ObjectMonitor : public CHeapObj { // Use the following at your own risk intx complete_exit(JavaThread* current); - bool reenter(intx recursions, JavaThread* current); private: void AddWaiter(ObjectWaiter* waiter); diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index d34a3ccab1a..3b3e128b346 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -575,42 +575,6 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) monitor->exit(current); } -// ----------------------------------------------------------------------------- -// Class Loader support to workaround deadlocks on the class loader lock objects -// Also used by GC -// complete_exit()/reenter() are used to wait on a nested lock -// i.e. to give up an outer lock completely and then re-enter -// Used when holding nested locks - lock acquisition order: lock1 then lock2 -// 1) complete_exit lock1 - saving recursion count -// 2) wait on lock2 -// 3) when notified on lock2, unlock lock2 -// 4) reenter lock1 with original recursion count -// 5) lock lock2 -// NOTE: must use heavy weight monitor to handle complete_exit/reenter() -intx ObjectSynchronizer::complete_exit(Handle obj, JavaThread* current) { - // The ObjectMonitor* can't be async deflated until ownership is - // dropped inside exit() and the ObjectMonitor* must be !is_busy(). - ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_vm_internal); - intx recur_count = monitor->complete_exit(current); - current->dec_held_monitor_count(recur_count + 1); - return recur_count; -} - -// NOTE: must use heavy weight monitor to handle complete_exit/reenter() -void ObjectSynchronizer::reenter(Handle obj, intx recursions, JavaThread* current) { - // An async deflation can race after the inflate() call and before - // reenter() -> enter() can make the ObjectMonitor busy. reenter() -> - // enter() returns false if we have lost the race to async deflation - // and we simply try again. - while (true) { - ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_vm_internal); - if (monitor->reenter(recursions, current)) { - current->inc_held_monitor_count(recursions + 1); - return; - } - } -} - // ----------------------------------------------------------------------------- // JNI locks on java objects // NOTE: must use heavy weight monitor to handle jni monitor enter diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index 06eb40dc504..66d08a9d998 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -154,12 +154,6 @@ class ObjectSynchronizer : AllStatic { static bool quick_notify(oopDesc* obj, JavaThread* current, bool All); static bool quick_enter(oop obj, JavaThread* current, BasicLock* Lock); - // used by classloading to free classloader object lock, - // wait on an internal lock, and reclaim original lock - // with original recursion count - static intx complete_exit(Handle obj, JavaThread* current); - static void reenter (Handle obj, intx recursions, JavaThread* current); - // Inflate light weight monitor to heavy weight monitor static ObjectMonitor* inflate(Thread* current, oop obj, const InflateCause cause); // This version is only for internal use diff --git a/test/hotspot/jtreg/runtime/ParallelLoad/ParallelSuper/ParallelSuperTest.java b/test/hotspot/jtreg/runtime/ParallelLoad/ParallelSuper/ParallelSuperTest.java index ca356f199e9..95a2af9618b 100644 --- a/test/hotspot/jtreg/runtime/ParallelLoad/ParallelSuper/ParallelSuperTest.java +++ b/test/hotspot/jtreg/runtime/ParallelLoad/ParallelSuper/ParallelSuperTest.java @@ -31,7 +31,6 @@ * @compile -XDignore.symbol.file AsmClasses.java * @compile test-classes/ClassInLoader.java test-classes/A.java test-classes/B.java ../share/ThreadPrint.java * @run main/othervm ParallelSuperTest - * @run main/othervm -XX:+EnableWaitForParallelLoad ParallelSuperTest -parallel * @run main/othervm ParallelSuperTest -parallel -parallelCapable */