8241390: 'Deadlock' with VM_RedefineClasses::lock_classes()

Reviewed-by: coleenp, sspitsyn
This commit is contained in:
Daniil Titov 2020-09-21 19:32:24 +00:00
parent 2e30ff61b0
commit f800af978c
4 changed files with 179 additions and 9 deletions

View file

@ -96,31 +96,69 @@ static inline InstanceKlass* get_ik(jclass def) {
// If any of the classes are being redefined, wait
// Parallel constant pool merging leads to indeterminate constant pools.
void VM_RedefineClasses::lock_classes() {
JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current());
GrowableArray<Klass*>* redef_classes = state->get_classes_being_redefined();
MonitorLocker ml(RedefineClasses_lock);
if (redef_classes == NULL) {
redef_classes = new(ResourceObj::C_HEAP, mtClass) GrowableArray<Klass*>(1, mtClass);
state->set_classes_being_redefined(redef_classes);
}
bool has_redefined;
do {
has_redefined = false;
// Go through classes each time until none are being redefined.
// Go through classes each time until none are being redefined. Skip
// the ones that are being redefined by this thread currently. Class file
// load hook event may trigger new class redefine when we are redefining
// a class (after lock_classes()).
for (int i = 0; i < _class_count; i++) {
if (get_ik(_class_defs[i].klass)->is_being_redefined()) {
ml.wait();
has_redefined = true;
break; // for loop
InstanceKlass* ik = get_ik(_class_defs[i].klass);
// Check if we are currently redefining the class in this thread already.
if (redef_classes->contains(ik)) {
assert(ik->is_being_redefined(), "sanity");
} else {
if (ik->is_being_redefined()) {
ml.wait();
has_redefined = true;
break; // for loop
}
}
}
} while (has_redefined);
for (int i = 0; i < _class_count; i++) {
get_ik(_class_defs[i].klass)->set_is_being_redefined(true);
InstanceKlass* ik = get_ik(_class_defs[i].klass);
redef_classes->push(ik); // Add to the _classes_being_redefined list
ik->set_is_being_redefined(true);
}
ml.notify_all();
}
void VM_RedefineClasses::unlock_classes() {
JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current());
GrowableArray<Klass*>* redef_classes = state->get_classes_being_redefined();
assert(redef_classes != NULL, "_classes_being_redefined is not allocated");
MonitorLocker ml(RedefineClasses_lock);
for (int i = 0; i < _class_count; i++) {
assert(get_ik(_class_defs[i].klass)->is_being_redefined(),
for (int i = _class_count - 1; i >= 0; i--) {
InstanceKlass* def_ik = get_ik(_class_defs[i].klass);
if (redef_classes->length() > 0) {
// Remove the class from _classes_being_redefined list
Klass* k = redef_classes->pop();
assert(def_ik == k, "unlocking wrong class");
}
assert(def_ik->is_being_redefined(),
"should be being redefined to get here");
get_ik(_class_defs[i].klass)->set_is_being_redefined(false);
// Unlock after we finish all redefines for this class within
// the thread. Same class can be pushed to the list multiple
// times (not more than once by each recursive redefinition).
if (!redef_classes->contains(def_ik)) {
def_ik->set_is_being_redefined(false);
}
}
ml.notify_all();
}